1 /*
2
3 Copyright (c) 2013, Arvid Norberg
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
30
31 */
32
33 #include "libtorrent/peer_list.hpp"
34 #include "libtorrent/torrent_handle.hpp"
35 #include "libtorrent/torrent_peer_allocator.hpp"
36 #include "libtorrent/peer_connection_interface.hpp"
37 #include "libtorrent/stat.hpp"
38 #include "libtorrent/ip_voter.hpp"
39 #include "libtorrent/ip_filter.hpp"
40 #include "libtorrent/peer_info.hpp"
41 #include "libtorrent/random.hpp"
42 #include "libtorrent/socket_io.hpp"
43
44 #include "test.hpp"
45 #include "setup_transfer.hpp"
46 #include <vector>
47 #include <memory> // for shared_ptr
48 #include <cstdarg>
49
50 using namespace lt;
51
52 namespace {
53
54 struct mock_torrent;
55
56 struct mock_peer_connection
57 : peer_connection_interface
58 , std::enable_shared_from_this<mock_peer_connection>
59 {
mock_peer_connection__anon8b1829440111::mock_peer_connection60 mock_peer_connection(mock_torrent* tor, bool out, tcp::endpoint const& remote)
61 : m_choked(false)
62 , m_outgoing(out)
63 , m_tp(nullptr)
64 , m_remote(remote)
65 , m_local(ep("127.0.0.1", 8080))
66 , m_our_id(nullptr)
67 , m_disconnect_called(false)
68 , m_torrent(*tor)
69 {
70 aux::random_bytes(m_id);
71 }
72
73 virtual ~mock_peer_connection() = default;
74
75 #if !defined TORRENT_DISABLE_LOGGING
should_log__anon8b1829440111::mock_peer_connection76 bool should_log(peer_log_alert::direction_t) const noexcept override
77 { return true; }
78
peer_log__anon8b1829440111::mock_peer_connection79 void peer_log(peer_log_alert::direction_t, char const* /*event*/
80 , char const* fmt, ...) const noexcept override
81 {
82 va_list v;
83 va_start(v, fmt);
84 #ifdef __clang__
85 #pragma clang diagnostic push
86 #pragma clang diagnostic ignored "-Wformat-nonliteral"
87 #endif
88 std::vprintf(fmt, v);
89 #ifdef __clang__
90 #pragma clang diagnostic pop
91 #endif
92 va_end(v);
93 }
94 #endif
95
was_disconnected__anon8b1829440111::mock_peer_connection96 bool was_disconnected() const { return m_disconnect_called; }
set_local_ep__anon8b1829440111::mock_peer_connection97 void set_local_ep(tcp::endpoint const& ep) { m_local = ep; }
98
99 lt::stat m_stat;
100 bool m_choked;
101 bool m_outgoing;
102 torrent_peer* m_tp;
103 tcp::endpoint m_remote;
104 tcp::endpoint m_local;
105 peer_id m_id;
106 peer_id m_our_id;
107 bool m_disconnect_called;
108 mock_torrent& m_torrent;
109
get_peer_info__anon8b1829440111::mock_peer_connection110 void get_peer_info(peer_info&) const override {}
remote__anon8b1829440111::mock_peer_connection111 tcp::endpoint const& remote() const override { return m_remote; }
local_endpoint__anon8b1829440111::mock_peer_connection112 tcp::endpoint local_endpoint() const override { return m_local; }
113 void disconnect(error_code const& ec
114 , operation_t op, disconnect_severity_t error = peer_connection_interface::normal) override;
pid__anon8b1829440111::mock_peer_connection115 peer_id const& pid() const override { return m_id; }
our_pid__anon8b1829440111::mock_peer_connection116 peer_id our_pid() const override { return m_our_id; }
set_holepunch_mode__anon8b1829440111::mock_peer_connection117 void set_holepunch_mode() override {}
peer_info_struct__anon8b1829440111::mock_peer_connection118 torrent_peer* peer_info_struct() const override { return m_tp; }
set_peer_info__anon8b1829440111::mock_peer_connection119 void set_peer_info(torrent_peer* pi) override { m_tp = pi; }
is_outgoing__anon8b1829440111::mock_peer_connection120 bool is_outgoing() const override { return m_outgoing; }
add_stat__anon8b1829440111::mock_peer_connection121 void add_stat(std::int64_t downloaded, std::int64_t uploaded) override
122 { m_stat.add_stat(downloaded, uploaded); }
fast_reconnect__anon8b1829440111::mock_peer_connection123 bool fast_reconnect() const override { return true; }
is_choked__anon8b1829440111::mock_peer_connection124 bool is_choked() const override { return m_choked; }
failed__anon8b1829440111::mock_peer_connection125 bool failed() const override { return false; }
statistics__anon8b1829440111::mock_peer_connection126 lt::stat const& statistics() const override { return m_stat; }
127 };
128
129 struct mock_torrent
130 {
mock_torrent__anon8b1829440111::mock_torrent131 explicit mock_torrent(torrent_state* st) : m_p(nullptr), m_state(st) {}
132 virtual ~mock_torrent() = default;
133
connect_to_peer__anon8b1829440111::mock_torrent134 bool connect_to_peer(torrent_peer* peerinfo)
135 {
136 TORRENT_ASSERT(peerinfo->connection == nullptr);
137 if (peerinfo->connection) return false;
138 auto c = std::make_shared<mock_peer_connection>(this, true, peerinfo->ip());
139 c->set_peer_info(peerinfo);
140
141 m_connections.push_back(c);
142 m_p->set_connection(peerinfo, c.get());
143 return true;
144 }
145
146 peer_list* m_p;
147 torrent_state* m_state;
148 std::vector<std::shared_ptr<mock_peer_connection>> m_connections;
149 };
150
disconnect(error_code const &,operation_t,disconnect_severity_t)151 void mock_peer_connection::disconnect(error_code const&
152 , operation_t, disconnect_severity_t)
153 {
154 m_torrent.m_p->connection_closed(*this, 0, m_torrent.m_state);
155 auto const i = std::find(m_torrent.m_connections.begin(), m_torrent.m_connections.end()
156 , std::static_pointer_cast<mock_peer_connection>(shared_from_this()));
157 if (i != m_torrent.m_connections.end()) m_torrent.m_connections.erase(i);
158
159 m_tp = nullptr;
160 m_disconnect_called = true;
161 }
162
has_peer(peer_list const & p,tcp::endpoint const & ep)163 bool has_peer(peer_list const& p, tcp::endpoint const& ep)
164 {
165 auto const its = p.find_peers(ep.address());
166 return its.first != its.second;
167 }
168
init_state()169 torrent_state init_state()
170 {
171 torrent_state st;
172 st.is_finished = false;
173 st.max_peerlist_size = 1000;
174 st.allow_multiple_connections_per_ip = false;
175 st.port = 9999;
176 return st;
177 }
178
add_peer(peer_list & p,torrent_state & st,tcp::endpoint const & ep)179 torrent_peer* add_peer(peer_list& p, torrent_state& st, tcp::endpoint const& ep)
180 {
181 int cc = p.num_connect_candidates();
182 torrent_peer* peer = p.add_peer(ep, {}, {}, &st);
183 if (peer)
184 {
185 TEST_EQUAL(p.num_connect_candidates(), cc + 1);
186 TEST_EQUAL(peer->port, ep.port());
187 }
188 st.erased.clear();
189 return peer;
190 }
191
connect_peer(peer_list & p,mock_torrent & t,torrent_state & st)192 void connect_peer(peer_list& p, mock_torrent& t, torrent_state& st)
193 {
194 torrent_peer* tp = p.connect_one_peer(0, &st);
195 TEST_CHECK(tp);
196 if (!tp) return;
197 t.connect_to_peer(tp);
198 st.erased.clear();
199 TEST_CHECK(tp->connection);
200 }
201
202 static torrent_peer_allocator allocator;
203
204 } // anonymous namespace
205
206 // test multiple peers with the same IP
207 // when disallowing it
TORRENT_TEST(multiple_ips_disallowed)208 TORRENT_TEST(multiple_ips_disallowed)
209 {
210 torrent_state st = init_state();
211 mock_torrent t(&st);
212 peer_list p(allocator);
213 t.m_p = &p;
214 TEST_EQUAL(p.num_connect_candidates(), 0);
215 torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), {}, {}, &st);
216
217 TEST_EQUAL(p.num_peers(), 1);
218 TEST_EQUAL(p.num_connect_candidates(), 1);
219 st.erased.clear();
220
221 torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), {}, {}, &st);
222 TEST_EQUAL(p.num_peers(), 1);
223 TEST_EQUAL(peer1, peer2);
224 TEST_EQUAL(p.num_connect_candidates(), 1);
225 st.erased.clear();
226 }
227
228 // test multiple peers with the same IP
229 // when allowing it
TORRENT_TEST(multiple_ips_allowed)230 TORRENT_TEST(multiple_ips_allowed)
231 {
232 torrent_state st = init_state();
233 mock_torrent t(&st);
234 st.allow_multiple_connections_per_ip = true;
235 peer_list p(allocator);
236 t.m_p = &p;
237 torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), {}, {}, &st);
238 TEST_EQUAL(p.num_connect_candidates(), 1);
239 TEST_EQUAL(p.num_peers(), 1);
240 st.erased.clear();
241
242 torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), {}, {}, &st);
243 TEST_EQUAL(p.num_peers(), 2);
244 TEST_CHECK(peer1 != peer2);
245 TEST_EQUAL(p.num_connect_candidates(), 2);
246 st.erased.clear();
247 }
248
249 // test adding two peers with the same IP, but different ports, to
250 // make sure they can be connected at the same time
251 // with allow_multiple_connections_per_ip enabled
TORRENT_TEST(multiple_ips_allowed2)252 TORRENT_TEST(multiple_ips_allowed2)
253 {
254 torrent_state st = init_state();
255 mock_torrent t(&st);
256 st.allow_multiple_connections_per_ip = true;
257 peer_list p(allocator);
258 t.m_p = &p;
259 torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), {}, {}, &st);
260 TEST_EQUAL(p.num_connect_candidates(), 1);
261 st.erased.clear();
262
263 TEST_EQUAL(p.num_peers(), 1);
264 torrent_peer* tp = p.connect_one_peer(0, &st);
265 TEST_CHECK(tp);
266 t.connect_to_peer(tp);
267 st.erased.clear();
268
269 // we only have one peer, we can't
270 // connect another one
271 tp = p.connect_one_peer(0, &st);
272 TEST_CHECK(tp == nullptr);
273 st.erased.clear();
274
275 torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), {}, {}, &st);
276 TEST_EQUAL(p.num_peers(), 2);
277 TEST_CHECK(peer1 != peer2);
278 TEST_EQUAL(p.num_connect_candidates(), 1);
279 st.erased.clear();
280
281 tp = p.connect_one_peer(0, &st);
282 TEST_CHECK(tp);
283 t.connect_to_peer(tp);
284 TEST_EQUAL(p.num_connect_candidates(), 0);
285 st.erased.clear();
286 }
287
288 // test adding two peers with the same IP, but different ports, to
289 // make sure they can not be connected at the same time
290 // with allow_multiple_connections_per_ip disabled
TORRENT_TEST(multiple_ips_disallowed2)291 TORRENT_TEST(multiple_ips_disallowed2)
292 {
293 torrent_state st = init_state();
294 mock_torrent t(&st);
295 st.allow_multiple_connections_per_ip = false;
296 peer_list p(allocator);
297 t.m_p = &p;
298 torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), {}, {}, &st);
299 TEST_EQUAL(p.num_connect_candidates(), 1);
300 TEST_EQUAL(peer1->port, 3000);
301 st.erased.clear();
302
303 TEST_EQUAL(p.num_peers(), 1);
304 torrent_peer* tp = p.connect_one_peer(0, &st);
305 TEST_CHECK(tp);
306 t.connect_to_peer(tp);
307 st.erased.clear();
308
309 // we only have one peer, we can't
310 // connect another one
311 tp = p.connect_one_peer(0, &st);
312 TEST_CHECK(tp == nullptr);
313 st.erased.clear();
314
315 torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), {}, {}, &st);
316 TEST_EQUAL(p.num_peers(), 1);
317 TEST_EQUAL(peer2->port, 9020);
318 TEST_CHECK(peer1 == peer2);
319 TEST_EQUAL(p.num_connect_candidates(), 0);
320 st.erased.clear();
321 }
322
323 // test incoming connection
324 // and update_peer_port
TORRENT_TEST(update_peer_port)325 TORRENT_TEST(update_peer_port)
326 {
327 torrent_state st = init_state();
328 mock_torrent t(&st);
329 st.allow_multiple_connections_per_ip = false;
330 peer_list p(allocator);
331 t.m_p = &p;
332 TEST_EQUAL(p.num_connect_candidates(), 0);
333 auto c = std::make_shared<mock_peer_connection>(&t, true, ep("10.0.0.1", 8080));
334 p.new_connection(*c, 0, &st);
335 TEST_EQUAL(p.num_connect_candidates(), 0);
336 TEST_EQUAL(p.num_peers(), 1);
337 st.erased.clear();
338
339 p.update_peer_port(4000, c->peer_info_struct(), peer_info::incoming, &st);
340 TEST_EQUAL(p.num_connect_candidates(), 0);
341 TEST_EQUAL(p.num_peers(), 1);
342 TEST_EQUAL(c->peer_info_struct()->port, 4000);
343 st.erased.clear();
344 }
345
346 // test incoming connection
347 // and update_peer_port, causing collission
TORRENT_TEST(update_peer_port_collide)348 TORRENT_TEST(update_peer_port_collide)
349 {
350 torrent_state st = init_state();
351 mock_torrent t(&st);
352 st.allow_multiple_connections_per_ip = true;
353 peer_list p(allocator);
354 t.m_p = &p;
355
356 torrent_peer* peer2 = p.add_peer(ep("10.0.0.1", 4000), {}, {}, &st);
357 TEST_CHECK(peer2);
358
359 TEST_EQUAL(p.num_connect_candidates(), 1);
360 auto c = std::make_shared<mock_peer_connection>(&t, true, ep("10.0.0.1", 8080));
361 p.new_connection(*c, 0, &st);
362 TEST_EQUAL(p.num_connect_candidates(), 1);
363 // at this point we have two peers, because we think they have different
364 // ports
365 TEST_EQUAL(p.num_peers(), 2);
366 st.erased.clear();
367
368 // this peer will end up having the same port as the existing peer in the list
369 p.update_peer_port(4000, c->peer_info_struct(), peer_info::incoming, &st);
370 TEST_EQUAL(p.num_connect_candidates(), 0);
371 // the expected behavior is to replace that one
372 TEST_EQUAL(p.num_peers(), 1);
373 TEST_EQUAL(c->peer_info_struct()->port, 4000);
374 st.erased.clear();
375 }
376
377 namespace {
shared_from_this(lt::peer_connection_interface * p)378 std::shared_ptr<mock_peer_connection> shared_from_this(lt::peer_connection_interface* p)
379 {
380 return std::static_pointer_cast<mock_peer_connection>(
381 static_cast<mock_peer_connection*>(p)->shared_from_this());
382 }
383 } // anonymous namespace
384
385 // test ip filter
TORRENT_TEST(ip_filter)386 TORRENT_TEST(ip_filter)
387 {
388 torrent_state st = init_state();
389 mock_torrent t(&st);
390 st.allow_multiple_connections_per_ip = false;
391 peer_list p(allocator);
392 t.m_p = &p;
393
394 // add peer 1
395 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.2", 3000));
396 torrent_peer* peer2 = add_peer(p, st, ep("11.0.0.2", 9020));
397
398 TEST_CHECK(peer1 != peer2);
399
400 connect_peer(p, t, st);
401 connect_peer(p, t, st);
402
403 auto con1 = shared_from_this(peer1->connection);
404 TEST_EQUAL(con1->was_disconnected(), false);
405 auto con2 = shared_from_this(peer2->connection);
406 TEST_EQUAL(con2->was_disconnected(), false);
407
408 // now, filter one of the IPs and make sure the peer is removed
409 ip_filter filter;
410 filter.add_rule(addr4("11.0.0.0"), addr4("255.255.255.255"), 1);
411 std::vector<address> banned;
412 p.apply_ip_filter(filter, &st, banned);
413 // we just erased a peer, because it was filtered by the ip filter
414 TEST_EQUAL(st.erased.size(), 1);
415 TEST_EQUAL(p.num_connect_candidates(), 0);
416 TEST_EQUAL(p.num_peers(), 1);
417 TEST_EQUAL(banned.size(), 1);
418 TEST_EQUAL(banned[0], addr4("11.0.0.2"));
419 TEST_EQUAL(con2->was_disconnected(), true);
420 TEST_EQUAL(con1->was_disconnected(), false);
421 }
422
423 // test port filter
TORRENT_TEST(port_filter)424 TORRENT_TEST(port_filter)
425 {
426 torrent_state st = init_state();
427 mock_torrent t(&st);
428 st.allow_multiple_connections_per_ip = false;
429 peer_list p(allocator);
430 t.m_p = &p;
431
432 // add peer 1
433 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.2", 3000));
434 torrent_peer* peer2 = add_peer(p, st, ep("11.0.0.2", 9020));
435
436 TEST_CHECK(peer1 != peer2);
437
438 connect_peer(p, t, st);
439 connect_peer(p, t, st);
440
441 auto con1 = shared_from_this(peer1->connection);
442 TEST_EQUAL(con1->was_disconnected(), false);
443 auto con2 = shared_from_this(peer2->connection);
444 TEST_EQUAL(con2->was_disconnected(), false);
445
446 // now, filter one of the IPs and make sure the peer is removed
447 port_filter filter;
448 filter.add_rule(9000, 10000, 1);
449 std::vector<address> banned;
450 p.apply_port_filter(filter, &st, banned);
451 // we just erased a peer, because it was filtered by the ip filter
452 TEST_EQUAL(st.erased.size(), 1);
453 TEST_EQUAL(p.num_connect_candidates(), 0);
454 TEST_EQUAL(p.num_peers(), 1);
455 TEST_EQUAL(banned.size(), 1);
456 TEST_EQUAL(banned[0], addr4("11.0.0.2"));
457 TEST_EQUAL(con2->was_disconnected(), true);
458 TEST_EQUAL(con1->was_disconnected(), false);
459 }
460
461 // test banning peers
TORRENT_TEST(ban_peers)462 TORRENT_TEST(ban_peers)
463 {
464 torrent_state st = init_state();
465 mock_torrent t(&st);
466 st.allow_multiple_connections_per_ip = false;
467 peer_list p(allocator);
468 t.m_p = &p;
469
470 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.1", 4000));
471
472 TEST_EQUAL(p.num_connect_candidates(), 1);
473 auto c = std::make_shared<mock_peer_connection>(&t, true, ep("10.0.0.1", 8080));
474 p.new_connection(*c, 0, &st);
475 TEST_EQUAL(p.num_connect_candidates(), 0);
476 TEST_EQUAL(p.num_peers(), 1);
477 st.erased.clear();
478
479 // now, ban the peer
480 bool ok = p.ban_peer(c->peer_info_struct());
481 TEST_EQUAL(ok, true);
482 TEST_EQUAL(peer1->banned, true);
483 // we still have it in the list
484 TEST_EQUAL(p.num_peers(), 1);
485 // it's just not a connect candidate, nor allowed to receive incoming connections
486 TEST_EQUAL(p.num_connect_candidates(), 0);
487
488 p.connection_closed(*c, 0, &st);
489 TEST_EQUAL(p.num_peers(), 1);
490 TEST_EQUAL(p.num_connect_candidates(), 0);
491 st.erased.clear();
492
493 c = std::make_shared<mock_peer_connection>(&t, true, ep("10.0.0.1", 8080));
494 ok = p.new_connection(*c, 0, &st);
495 // since it's banned, we should not allow this incoming connection
496 TEST_EQUAL(ok, false);
497 TEST_EQUAL(p.num_connect_candidates(), 0);
498 st.erased.clear();
499 }
500
501 // test erase_peers when we fill up the peer list
TORRENT_TEST(erase_peers)502 TORRENT_TEST(erase_peers)
503 {
504 torrent_state st = init_state();
505 mock_torrent t(&st);
506 st.max_peerlist_size = 100;
507 st.allow_multiple_connections_per_ip = true;
508 peer_list p(allocator);
509 t.m_p = &p;
510
511 for (int i = 0; i < 100; ++i)
512 {
513 TEST_EQUAL(st.erased.size(), 0);
514 tcp::endpoint ep = rand_tcp_ep();
515 torrent_peer* peer = add_peer(p, st, ep);
516 TEST_CHECK(peer);
517 if (peer == nullptr || st.erased.size() > 0)
518 {
519 std::printf("unexpected rejection of peer: %s | %d in list. "
520 "added peer %p, erased %d peers\n"
521 , print_endpoint(ep).c_str(), p.num_peers(), static_cast<void*>(peer)
522 , int(st.erased.size()));
523 }
524 }
525 TEST_EQUAL(p.num_peers(), 100);
526
527 // trigger the eviction of one peer
528 torrent_peer* peer = p.add_peer(rand_tcp_ep(), {}, {}, &st);
529 // we either removed an existing peer, or rejected this one
530 // either is valid behavior when the list is full
531 TEST_CHECK(st.erased.size() == 1 || peer == nullptr);
532 }
533
534 // test set_ip_filter
TORRENT_TEST(set_ip_filter)535 TORRENT_TEST(set_ip_filter)
536 {
537 torrent_state st = init_state();
538 std::vector<address> banned;
539
540 mock_torrent t(&st);
541 peer_list p(allocator);
542 t.m_p = &p;
543
544 for (int i = 0; i < 100; ++i)
545 {
546 p.add_peer(tcp::endpoint(
547 address_v4(std::uint32_t((10 << 24) + ((i + 10) << 16))), 353), {}, {}, &st);
548 TEST_EQUAL(st.erased.size(), 0);
549 st.erased.clear();
550 }
551 TEST_EQUAL(p.num_peers(), 100);
552 TEST_EQUAL(p.num_connect_candidates(), 100);
553
554 // trigger the removal of one peer
555 ip_filter filter;
556 filter.add_rule(addr4("10.13.0.0"), addr4("10.13.255.255"), ip_filter::blocked);
557 p.apply_ip_filter(filter, &st, banned);
558 TEST_EQUAL(st.erased.size(), 1);
559 TEST_EQUAL(st.erased[0]->address(), addr4("10.13.0.0"));
560 TEST_EQUAL(p.num_peers(), 99);
561 TEST_EQUAL(p.num_connect_candidates(), 99);
562 }
563
564 // test set_port_filter
TORRENT_TEST(set_port_filter)565 TORRENT_TEST(set_port_filter)
566 {
567 torrent_state st = init_state();
568 std::vector<address> banned;
569
570 mock_torrent t(&st);
571 peer_list p(allocator);
572 t.m_p = &p;
573
574 for (int i = 0; i < 100; ++i)
575 {
576 p.add_peer(tcp::endpoint(
577 address_v4(std::uint32_t((10 << 24) + ((i + 10) << 16))), std::uint16_t(i + 10)), {}, {}, &st);
578 TEST_EQUAL(st.erased.size(), 0);
579 st.erased.clear();
580 }
581 TEST_EQUAL(p.num_peers(), 100);
582 TEST_EQUAL(p.num_connect_candidates(), 100);
583
584 // trigger the removal of one peer
585 port_filter filter;
586 filter.add_rule(13, 13, port_filter::blocked);
587 p.apply_port_filter(filter, &st, banned);
588 TEST_EQUAL(st.erased.size(), 1);
589 TEST_EQUAL(st.erased[0]->address(), addr4("10.13.0.0"));
590 TEST_EQUAL(st.erased[0]->port, 13);
591 TEST_EQUAL(p.num_peers(), 99);
592 TEST_EQUAL(p.num_connect_candidates(), 99);
593 }
594
595 // test set_max_failcount
TORRENT_TEST(set_max_failcount)596 TORRENT_TEST(set_max_failcount)
597 {
598 torrent_state st = init_state();
599
600 mock_torrent t(&st);
601 peer_list p(allocator);
602 t.m_p = &p;
603
604 for (int i = 0; i < 100; ++i)
605 {
606 torrent_peer* peer = p.add_peer(tcp::endpoint(
607 address_v4(std::uint32_t((10 << 24) + ((i + 10) << 16))), std::uint16_t(i + 10)), {}, {}, &st);
608 TEST_EQUAL(st.erased.size(), 0);
609 st.erased.clear();
610 // every other peer has a failcount of 1
611 if (i % 2) p.inc_failcount(peer);
612 }
613 TEST_EQUAL(p.num_peers(), 100);
614 TEST_EQUAL(p.num_connect_candidates(), 100);
615
616 // set the max failcount to 1 and observe how half the peers no longer
617 // are connect candidates
618 st.max_failcount = 1;
619 p.set_max_failcount(&st);
620
621 TEST_EQUAL(p.num_connect_candidates(), 50);
622 TEST_EQUAL(p.num_peers(), 100);
623 }
624
625 // test set_seed
TORRENT_TEST(set_seed)626 TORRENT_TEST(set_seed)
627 {
628 torrent_state st = init_state();
629
630 mock_torrent t(&st);
631 peer_list p(allocator);
632 t.m_p = &p;
633
634 for (int i = 0; i < 100; ++i)
635 {
636 torrent_peer* peer = p.add_peer(tcp::endpoint(
637 address_v4(std::uint32_t((10 << 24) + ((i + 10) << 16))), std::uint16_t(i + 10)), {}, {}, &st);
638 TEST_EQUAL(st.erased.size(), 0);
639 st.erased.clear();
640 // make every other peer a seed
641 if (i % 2) p.set_seed(peer, true);
642 }
643 TEST_EQUAL(p.num_peers(), 100);
644 TEST_EQUAL(p.num_connect_candidates(), 100);
645
646 // now, the torrent completes and we're no longer interested in
647 // connecting to seeds. Make sure half the peers are no longer
648 // considered connect candidates
649 st.is_finished = true;
650
651 // this will make the peer_list recalculate the connect candidates
652 std::vector<torrent_peer*> peers;
653 p.connect_one_peer(1, &st);
654
655 TEST_EQUAL(p.num_connect_candidates(), 50);
656 TEST_EQUAL(p.num_peers(), 100);
657 }
658
659 // test has_peer
TORRENT_TEST(has_peer)660 TORRENT_TEST(has_peer)
661 {
662 torrent_state st = init_state();
663 std::vector<address> banned;
664
665 mock_torrent t(&st);
666 peer_list p(allocator);
667 t.m_p = &p;
668
669 torrent_peer* peer1 = add_peer(p, st, ep("10.10.0.1", 10));
670 torrent_peer* peer2 = add_peer(p, st, ep("10.10.0.2", 11));
671
672 TEST_EQUAL(p.num_peers(), 2);
673 TEST_EQUAL(p.num_connect_candidates(), 2);
674
675 TEST_EQUAL(p.has_peer(peer1), true);
676 TEST_EQUAL(p.has_peer(peer2), true);
677
678 ip_filter filter;
679 filter.add_rule(addr4("10.10.0.1"), addr4("10.10.0.1"), ip_filter::blocked);
680 p.apply_ip_filter(filter, &st, banned);
681 TEST_EQUAL(st.erased.size(), 1);
682 st.erased.clear();
683
684 TEST_EQUAL(p.num_peers(), 1);
685 TEST_EQUAL(p.num_connect_candidates(), 1);
686
687 TEST_EQUAL(p.has_peer(peer1), false);
688 TEST_EQUAL(p.has_peer(peer2), true);
689 }
690
691 // test connect_candidates torrent_finish
TORRENT_TEST(connect_candidates_finish)692 TORRENT_TEST(connect_candidates_finish)
693 {
694 torrent_state st = init_state();
695 std::vector<address> banned;
696
697 mock_torrent t(&st);
698 peer_list p(allocator);
699 t.m_p = &p;
700
701 torrent_peer* peer1 = add_peer(p, st, ep("10.10.0.1", 10));
702 TEST_CHECK(peer1);
703 p.set_seed(peer1, true);
704 torrent_peer* peer2 = add_peer(p, st, ep("10.10.0.2", 11));
705 TEST_CHECK(peer2);
706 p.set_seed(peer2, true);
707 torrent_peer* peer3 = add_peer(p, st, ep("10.10.0.3", 11));
708 TEST_CHECK(peer3);
709 p.set_seed(peer3, true);
710 torrent_peer* peer4 = add_peer(p, st, ep("10.10.0.4", 11));
711 TEST_CHECK(peer4);
712 torrent_peer* peer5 = add_peer(p, st, ep("10.10.0.5", 11));
713 TEST_CHECK(peer5);
714
715 TEST_EQUAL(p.num_peers(), 5);
716 TEST_EQUAL(p.num_connect_candidates(), 5);
717
718 st.is_finished = true;
719 // we're finished downloading now, only the non-seeds are
720 // connect candidates
721
722 // connect to one of them
723 connect_peer(p, t, st);
724
725 TEST_EQUAL(p.num_peers(), 5);
726 // and there should be one left
727 TEST_EQUAL(p.num_connect_candidates(), 1);
728 }
729
730 // test self-connection
TORRENT_TEST(self_connection)731 TORRENT_TEST(self_connection)
732 {
733 torrent_state st = init_state();
734 mock_torrent t(&st);
735 st.allow_multiple_connections_per_ip = false;
736 peer_list p(allocator);
737 t.m_p = &p;
738
739 // add and connect peer
740 torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 3000));
741 connect_peer(p, t, st);
742
743 auto con_out = shared_from_this(peer->connection);
744 con_out->set_local_ep(ep("10.0.0.2", 8080));
745
746 auto con_in = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 8080));
747 con_in->set_local_ep(ep("10.0.0.2", 3000));
748
749 p.new_connection(*con_in, 0, &st);
750
751 // from the peer_list's point of view, this looks like we made one
752 // outgoing connection and received an incoming one. Since they share
753 // the exact same endpoints (IP ports) but just swapped source and
754 // destination, the peer list is supposed to figure out that we connected
755 // to ourself and disconnect it
756 TEST_EQUAL(con_out->was_disconnected(), true);
757 TEST_EQUAL(con_in->was_disconnected(), true);
758 }
759
760 // test double connection (both incoming)
TORRENT_TEST(double_connection)761 TORRENT_TEST(double_connection)
762 {
763 torrent_state st = init_state();
764 mock_torrent t(&st);
765 st.allow_multiple_connections_per_ip = false;
766 peer_list p(allocator);
767 t.m_p = &p;
768
769 // we are 10.0.0.1 and the other peer is 10.0.0.2
770
771 // first incoming connection
772 auto con1 = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 7528));
773 con1->set_local_ep(ep("10.0.0.1", 8080));
774
775 p.new_connection(*con1, 0, &st);
776
777 // and the incoming connection
778 auto con2 = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 3561));
779 con2->set_local_ep(ep("10.0.0.1", 8080));
780
781 p.new_connection(*con2, 0, &st);
782
783 // the second incoming connection should be closed
784 TEST_EQUAL(con1->was_disconnected(), false);
785 TEST_EQUAL(con2->was_disconnected(), true);
786 }
787
788 // test double connection (we loose)
TORRENT_TEST(double_connection_loose)789 TORRENT_TEST(double_connection_loose)
790 {
791 torrent_state st = init_state();
792 mock_torrent t(&st);
793 st.allow_multiple_connections_per_ip = false;
794 peer_list p(allocator);
795 t.m_p = &p;
796
797 // we are 10.0.0.1 and the other peer is 10.0.0.2
798
799 // our outgoing connection
800 torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 3000));
801 connect_peer(p, t, st);
802
803 auto con_out = shared_from_this(peer->connection);
804 con_out->set_local_ep(ep("10.0.0.1", 3163));
805
806 // and the incoming connection
807 auto con_in = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 3561));
808 con_in->set_local_ep(ep("10.0.0.1", 8080));
809
810 p.new_connection(*con_in, 0, &st);
811
812 // the rules are documented in peer_list.cpp
813 TEST_EQUAL(con_out->was_disconnected(), true);
814 TEST_EQUAL(con_in->was_disconnected(), false);
815 }
816
817 // test double connection with identical ports (random)
TORRENT_TEST(double_connection_random)818 TORRENT_TEST(double_connection_random)
819 {
820 int in = 0;
821 int out = 0;
822 for (int i = 0; i < 30; ++i)
823 {
824 torrent_state st = init_state();
825 mock_torrent t(&st);
826 st.allow_multiple_connections_per_ip = false;
827 peer_list p(allocator);
828 t.m_p = &p;
829
830 // we are 10.0.0.1 and the other peer is 10.0.0.2
831
832 // our outgoing connection
833 torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 3000));
834 connect_peer(p, t, st);
835
836 auto con_out = static_cast<mock_peer_connection*>(peer->connection)->shared_from_this();
837 con_out->set_local_ep(ep("10.0.0.1", 3000));
838
839 // and the incoming connection
840 auto con_in = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 3000));
841 con_in->set_local_ep(ep("10.0.0.1", 3000));
842
843 p.new_connection(*con_in, 0, &st);
844
845 // the rules are documented in peer_list.cpp
846 out += con_out->was_disconnected();
847 in += con_in->was_disconnected();
848 }
849 // we should have gone different ways randomly
850 TEST_CHECK(out > 0);
851 TEST_CHECK(in > 0);
852 }
853
854 // test double connection (we win)
TORRENT_TEST(double_connection_win)855 TORRENT_TEST(double_connection_win)
856 {
857 torrent_state st = init_state();
858 mock_torrent t(&st);
859 st.allow_multiple_connections_per_ip = false;
860 peer_list p(allocator);
861 t.m_p = &p;
862
863 // we are 10.0.0.1 and the other peer is 10.0.0.2
864
865 // our outgoing connection
866 torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 8080));
867 connect_peer(p, t, st);
868
869 auto con_out = shared_from_this(peer->connection);
870 con_out->set_local_ep(ep("10.0.0.1", 3163));
871
872 //and the incoming connection
873 auto con_in = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 3561));
874 con_in->set_local_ep(ep("10.0.0.1", 3000));
875
876 p.new_connection(*con_in, 0, &st);
877
878 // the rules are documented in peer_list.cpp
879 TEST_EQUAL(con_out->was_disconnected(), false);
880 TEST_EQUAL(con_in->was_disconnected(), true);
881 }
882
883 // test incoming connection when we are at the list size limit
TORRENT_TEST(incoming_size_limit)884 TORRENT_TEST(incoming_size_limit)
885 {
886 torrent_state st = init_state();
887 st.max_peerlist_size = 5;
888 mock_torrent t(&st);
889 st.allow_multiple_connections_per_ip = false;
890 peer_list p(allocator);
891 t.m_p = &p;
892
893 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.1", 8080));
894 TEST_CHECK(peer1);
895 TEST_EQUAL(p.num_peers(), 1);
896 torrent_peer* peer2 = add_peer(p, st, ep("10.0.0.2", 8080));
897 TEST_CHECK(peer2);
898 TEST_EQUAL(p.num_peers(), 2);
899 torrent_peer* peer3 = add_peer(p, st, ep("10.0.0.3", 8080));
900 TEST_CHECK(peer3);
901 TEST_EQUAL(p.num_peers(), 3);
902 torrent_peer* peer4 = add_peer(p, st, ep("10.0.0.4", 8080));
903 TEST_CHECK(peer4);
904 TEST_EQUAL(p.num_peers(), 4);
905 torrent_peer* peer5 = add_peer(p, st, ep("10.0.0.5", 8080));
906 TEST_CHECK(peer5);
907 TEST_EQUAL(p.num_peers(), 5);
908
909 auto con_in = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.1.2", 3561));
910 con_in->set_local_ep(ep("10.0.2.1", 3000));
911
912 // since we're already at 5 peers in the peer list, this call should
913 // erase one of the existing ones.
914 p.new_connection(*con_in, 0, &st);
915
916 TEST_EQUAL(con_in->was_disconnected(), false);
917 TEST_EQUAL(p.num_peers(), 5);
918
919 // one of the previous ones should have been removed
920 TEST_EQUAL(has_peer(p, ep("10.0.0.1", 8080))
921 + has_peer(p, ep("10.0.0.2", 8080))
922 + has_peer(p, ep("10.0.0.3", 8080))
923 + has_peer(p, ep("10.0.0.4", 8080))
924 + has_peer(p, ep("10.0.0.5", 8080))
925 , 4);
926 }
927
928 // test new peer when we are at the list size limit
TORRENT_TEST(new_peer_size_limit)929 TORRENT_TEST(new_peer_size_limit)
930 {
931 torrent_state st = init_state();
932 st.max_peerlist_size = 5;
933 mock_torrent t(&st);
934 st.allow_multiple_connections_per_ip = false;
935 peer_list p(allocator);
936 t.m_p = &p;
937
938 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.1", 8080));
939 TEST_CHECK(peer1);
940 TEST_EQUAL(p.num_peers(), 1);
941 torrent_peer* peer2 = add_peer(p, st, ep("10.0.0.2", 8080));
942 TEST_CHECK(peer2);
943 TEST_EQUAL(p.num_peers(), 2);
944 torrent_peer* peer3 = add_peer(p, st, ep("10.0.0.3", 8080));
945 TEST_CHECK(peer3);
946 TEST_EQUAL(p.num_peers(), 3);
947 torrent_peer* peer4 = add_peer(p, st, ep("10.0.0.4", 8080));
948 TEST_CHECK(peer4);
949 TEST_EQUAL(p.num_peers(), 4);
950 torrent_peer* peer5 = add_peer(p, st, ep("10.0.0.5", 8080));
951 TEST_CHECK(peer5);
952 TEST_EQUAL(p.num_peers(), 5);
953 torrent_peer* peer6 = p.add_peer(ep("10.0.0.6", 8080), {}, {}, &st);
954 TEST_CHECK(peer6 == nullptr);
955 TEST_EQUAL(p.num_peers(), 5);
956
957 // one of the connection should have been removed
958 TEST_EQUAL(has_peer(p, ep("10.0.0.1", 8080))
959 + has_peer(p, ep("10.0.0.2", 8080))
960 + has_peer(p, ep("10.0.0.3", 8080))
961 + has_peer(p, ep("10.0.0.4", 8080))
962 + has_peer(p, ep("10.0.0.5", 8080))
963 + has_peer(p, ep("10.0.0.6", 8080))
964 , 5);
965 }
966
967 // TODO: test erasing peers
968 // TODO: test update_peer_port with allow_multiple_connections_per_ip and without
969 // TODO: test add i2p peers
970 // TODO: test allow_i2p_mixed
971 // TODO: test insert_peer failing with all error conditions
972 // TODO: test IPv6
973 // TODO: test connect_to_peer() failing
974 // TODO: test connection_closed
975 // TODO: connect candidates recalculation when incrementing failcount
976