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__anona42d9b260111::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__anona42d9b260111::mock_peer_connection76 bool should_log(peer_log_alert::direction_t) const noexcept override
77 { return true; }
78
peer_log__anona42d9b260111::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__anona42d9b260111::mock_peer_connection96 bool was_disconnected() const { return m_disconnect_called; }
set_local_ep__anona42d9b260111::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__anona42d9b260111::mock_peer_connection110 void get_peer_info(peer_info&) const override {}
remote__anona42d9b260111::mock_peer_connection111 tcp::endpoint const& remote() const override { return m_remote; }
local_endpoint__anona42d9b260111::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__anona42d9b260111::mock_peer_connection115 peer_id const& pid() const override { return m_id; }
our_pid__anona42d9b260111::mock_peer_connection116 peer_id our_pid() const override { return m_our_id; }
set_holepunch_mode__anona42d9b260111::mock_peer_connection117 void set_holepunch_mode() override {}
peer_info_struct__anona42d9b260111::mock_peer_connection118 torrent_peer* peer_info_struct() const override { return m_tp; }
set_peer_info__anona42d9b260111::mock_peer_connection119 void set_peer_info(torrent_peer* pi) override { m_tp = pi; }
is_outgoing__anona42d9b260111::mock_peer_connection120 bool is_outgoing() const override { return m_outgoing; }
add_stat__anona42d9b260111::mock_peer_connection121 void add_stat(std::int64_t downloaded, std::int64_t uploaded) override
122 { m_stat.add_stat(downloaded, uploaded); }
fast_reconnect__anona42d9b260111::mock_peer_connection123 bool fast_reconnect() const override { return true; }
is_choked__anona42d9b260111::mock_peer_connection124 bool is_choked() const override { return m_choked; }
failed__anona42d9b260111::mock_peer_connection125 bool failed() const override { return false; }
statistics__anona42d9b260111::mock_peer_connection126 lt::stat const& statistics() const override { return m_stat; }
127 };
128
129 struct mock_torrent
130 {
mock_torrent__anona42d9b260111::mock_torrent131 explicit mock_torrent(torrent_state* st) : m_p(nullptr), m_state(st) {}
132 virtual ~mock_torrent() = default;
133
connect_to_peer__anona42d9b260111::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.is_paused = false;
174 st.max_peerlist_size = 1000;
175 st.allow_multiple_connections_per_ip = false;
176 st.port = 9999;
177 return st;
178 }
179
add_peer(peer_list & p,torrent_state & st,tcp::endpoint const & ep)180 torrent_peer* add_peer(peer_list& p, torrent_state& st, tcp::endpoint const& ep)
181 {
182 int cc = p.num_connect_candidates();
183 torrent_peer* peer = p.add_peer(ep, {}, {}, &st);
184 if (peer)
185 {
186 TEST_EQUAL(p.num_connect_candidates(), cc + 1);
187 TEST_EQUAL(peer->port, ep.port());
188 }
189 st.erased.clear();
190 return peer;
191 }
192
connect_peer(peer_list & p,mock_torrent & t,torrent_state & st)193 void connect_peer(peer_list& p, mock_torrent& t, torrent_state& st)
194 {
195 torrent_peer* tp = p.connect_one_peer(0, &st);
196 TEST_CHECK(tp);
197 if (!tp) return;
198 t.connect_to_peer(tp);
199 st.erased.clear();
200 TEST_CHECK(tp->connection);
201 }
202
203 static torrent_peer_allocator allocator;
204
205 } // anonymous namespace
206
207 // test multiple peers with the same IP
208 // when disallowing it
TORRENT_TEST(multiple_ips_disallowed)209 TORRENT_TEST(multiple_ips_disallowed)
210 {
211 torrent_state st = init_state();
212 mock_torrent t(&st);
213 peer_list p(allocator);
214 t.m_p = &p;
215 TEST_EQUAL(p.num_connect_candidates(), 0);
216 torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), {}, {}, &st);
217
218 TEST_EQUAL(p.num_peers(), 1);
219 TEST_EQUAL(p.num_connect_candidates(), 1);
220 st.erased.clear();
221
222 torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), {}, {}, &st);
223 TEST_EQUAL(p.num_peers(), 1);
224 TEST_EQUAL(peer1, peer2);
225 TEST_EQUAL(p.num_connect_candidates(), 1);
226 st.erased.clear();
227 }
228
229 // test multiple peers with the same IP
230 // when allowing it
TORRENT_TEST(multiple_ips_allowed)231 TORRENT_TEST(multiple_ips_allowed)
232 {
233 torrent_state st = init_state();
234 mock_torrent t(&st);
235 st.allow_multiple_connections_per_ip = true;
236 peer_list p(allocator);
237 t.m_p = &p;
238 torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), {}, {}, &st);
239 TEST_EQUAL(p.num_connect_candidates(), 1);
240 TEST_EQUAL(p.num_peers(), 1);
241 st.erased.clear();
242
243 torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), {}, {}, &st);
244 TEST_EQUAL(p.num_peers(), 2);
245 TEST_CHECK(peer1 != peer2);
246 TEST_EQUAL(p.num_connect_candidates(), 2);
247 st.erased.clear();
248 }
249
250 // test adding two peers with the same IP, but different ports, to
251 // make sure they can be connected at the same time
252 // with allow_multiple_connections_per_ip enabled
TORRENT_TEST(multiple_ips_allowed2)253 TORRENT_TEST(multiple_ips_allowed2)
254 {
255 torrent_state st = init_state();
256 mock_torrent t(&st);
257 st.allow_multiple_connections_per_ip = true;
258 peer_list p(allocator);
259 t.m_p = &p;
260 torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), {}, {}, &st);
261 TEST_EQUAL(p.num_connect_candidates(), 1);
262 st.erased.clear();
263
264 TEST_EQUAL(p.num_peers(), 1);
265 torrent_peer* tp = p.connect_one_peer(0, &st);
266 TEST_CHECK(tp);
267 t.connect_to_peer(tp);
268 st.erased.clear();
269
270 // we only have one peer, we can't
271 // connect another one
272 tp = p.connect_one_peer(0, &st);
273 TEST_CHECK(tp == nullptr);
274 st.erased.clear();
275
276 torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), {}, {}, &st);
277 TEST_EQUAL(p.num_peers(), 2);
278 TEST_CHECK(peer1 != peer2);
279 TEST_EQUAL(p.num_connect_candidates(), 1);
280 st.erased.clear();
281
282 tp = p.connect_one_peer(0, &st);
283 TEST_CHECK(tp);
284 t.connect_to_peer(tp);
285 TEST_EQUAL(p.num_connect_candidates(), 0);
286 st.erased.clear();
287 }
288
289 // test adding two peers with the same IP, but different ports, to
290 // make sure they can not be connected at the same time
291 // with allow_multiple_connections_per_ip disabled
TORRENT_TEST(multiple_ips_disallowed2)292 TORRENT_TEST(multiple_ips_disallowed2)
293 {
294 torrent_state st = init_state();
295 mock_torrent t(&st);
296 st.allow_multiple_connections_per_ip = false;
297 peer_list p(allocator);
298 t.m_p = &p;
299 torrent_peer* peer1 = p.add_peer(ep("10.0.0.2", 3000), {}, {}, &st);
300 TEST_EQUAL(p.num_connect_candidates(), 1);
301 TEST_EQUAL(peer1->port, 3000);
302 st.erased.clear();
303
304 TEST_EQUAL(p.num_peers(), 1);
305 torrent_peer* tp = p.connect_one_peer(0, &st);
306 TEST_CHECK(tp);
307 t.connect_to_peer(tp);
308 st.erased.clear();
309
310 // we only have one peer, we can't
311 // connect another one
312 tp = p.connect_one_peer(0, &st);
313 TEST_CHECK(tp == nullptr);
314 st.erased.clear();
315
316 torrent_peer* peer2 = p.add_peer(ep("10.0.0.2", 9020), {}, {}, &st);
317 TEST_EQUAL(p.num_peers(), 1);
318 TEST_EQUAL(peer2->port, 9020);
319 TEST_CHECK(peer1 == peer2);
320 TEST_EQUAL(p.num_connect_candidates(), 0);
321 st.erased.clear();
322 }
323
324 // test incoming connection
325 // and update_peer_port
TORRENT_TEST(update_peer_port)326 TORRENT_TEST(update_peer_port)
327 {
328 torrent_state st = init_state();
329 mock_torrent t(&st);
330 st.allow_multiple_connections_per_ip = false;
331 peer_list p(allocator);
332 t.m_p = &p;
333 TEST_EQUAL(p.num_connect_candidates(), 0);
334 auto c = std::make_shared<mock_peer_connection>(&t, true, ep("10.0.0.1", 8080));
335 p.new_connection(*c, 0, &st);
336 TEST_EQUAL(p.num_connect_candidates(), 0);
337 TEST_EQUAL(p.num_peers(), 1);
338 st.erased.clear();
339
340 p.update_peer_port(4000, c->peer_info_struct(), peer_info::incoming, &st);
341 TEST_EQUAL(p.num_connect_candidates(), 0);
342 TEST_EQUAL(p.num_peers(), 1);
343 TEST_EQUAL(c->peer_info_struct()->port, 4000);
344 st.erased.clear();
345 }
346
347 // test incoming connection
348 // and update_peer_port, causing collission
TORRENT_TEST(update_peer_port_collide)349 TORRENT_TEST(update_peer_port_collide)
350 {
351 torrent_state st = init_state();
352 mock_torrent t(&st);
353 st.allow_multiple_connections_per_ip = true;
354 peer_list p(allocator);
355 t.m_p = &p;
356
357 torrent_peer* peer2 = p.add_peer(ep("10.0.0.1", 4000), {}, {}, &st);
358 TEST_CHECK(peer2);
359
360 TEST_EQUAL(p.num_connect_candidates(), 1);
361 auto c = std::make_shared<mock_peer_connection>(&t, true, ep("10.0.0.1", 8080));
362 p.new_connection(*c, 0, &st);
363 TEST_EQUAL(p.num_connect_candidates(), 1);
364 // at this point we have two peers, because we think they have different
365 // ports
366 TEST_EQUAL(p.num_peers(), 2);
367 st.erased.clear();
368
369 // this peer will end up having the same port as the existing peer in the list
370 p.update_peer_port(4000, c->peer_info_struct(), peer_info::incoming, &st);
371 TEST_EQUAL(p.num_connect_candidates(), 0);
372 // the expected behavior is to replace that one
373 TEST_EQUAL(p.num_peers(), 1);
374 TEST_EQUAL(c->peer_info_struct()->port, 4000);
375 st.erased.clear();
376 }
377
378 namespace {
shared_from_this(lt::peer_connection_interface * p)379 std::shared_ptr<mock_peer_connection> shared_from_this(lt::peer_connection_interface* p)
380 {
381 return std::static_pointer_cast<mock_peer_connection>(
382 static_cast<mock_peer_connection*>(p)->shared_from_this());
383 }
384 } // anonymous namespace
385
386 // test ip filter
TORRENT_TEST(ip_filter)387 TORRENT_TEST(ip_filter)
388 {
389 torrent_state st = init_state();
390 mock_torrent t(&st);
391 st.allow_multiple_connections_per_ip = false;
392 peer_list p(allocator);
393 t.m_p = &p;
394
395 // add peer 1
396 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.2", 3000));
397 torrent_peer* peer2 = add_peer(p, st, ep("11.0.0.2", 9020));
398
399 TEST_CHECK(peer1 != peer2);
400
401 connect_peer(p, t, st);
402 connect_peer(p, t, st);
403
404 auto con1 = shared_from_this(peer1->connection);
405 TEST_EQUAL(con1->was_disconnected(), false);
406 auto con2 = shared_from_this(peer2->connection);
407 TEST_EQUAL(con2->was_disconnected(), false);
408
409 // now, filter one of the IPs and make sure the peer is removed
410 ip_filter filter;
411 filter.add_rule(addr4("11.0.0.0"), addr4("255.255.255.255"), 1);
412 std::vector<address> banned;
413 p.apply_ip_filter(filter, &st, banned);
414 // we just erased a peer, because it was filtered by the ip filter
415 TEST_EQUAL(st.erased.size(), 1);
416 TEST_EQUAL(p.num_connect_candidates(), 0);
417 TEST_EQUAL(p.num_peers(), 1);
418 TEST_EQUAL(banned.size(), 1);
419 TEST_EQUAL(banned[0], addr4("11.0.0.2"));
420 TEST_EQUAL(con2->was_disconnected(), true);
421 TEST_EQUAL(con1->was_disconnected(), false);
422 }
423
424 // test port filter
TORRENT_TEST(port_filter)425 TORRENT_TEST(port_filter)
426 {
427 torrent_state st = init_state();
428 mock_torrent t(&st);
429 st.allow_multiple_connections_per_ip = false;
430 peer_list p(allocator);
431 t.m_p = &p;
432
433 // add peer 1
434 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.2", 3000));
435 torrent_peer* peer2 = add_peer(p, st, ep("11.0.0.2", 9020));
436
437 TEST_CHECK(peer1 != peer2);
438
439 connect_peer(p, t, st);
440 connect_peer(p, t, st);
441
442 auto con1 = shared_from_this(peer1->connection);
443 TEST_EQUAL(con1->was_disconnected(), false);
444 auto con2 = shared_from_this(peer2->connection);
445 TEST_EQUAL(con2->was_disconnected(), false);
446
447 // now, filter one of the IPs and make sure the peer is removed
448 port_filter filter;
449 filter.add_rule(9000, 10000, 1);
450 std::vector<address> banned;
451 p.apply_port_filter(filter, &st, banned);
452 // we just erased a peer, because it was filtered by the ip filter
453 TEST_EQUAL(st.erased.size(), 1);
454 TEST_EQUAL(p.num_connect_candidates(), 0);
455 TEST_EQUAL(p.num_peers(), 1);
456 TEST_EQUAL(banned.size(), 1);
457 TEST_EQUAL(banned[0], addr4("11.0.0.2"));
458 TEST_EQUAL(con2->was_disconnected(), true);
459 TEST_EQUAL(con1->was_disconnected(), false);
460 }
461
462 // test banning peers
TORRENT_TEST(ban_peers)463 TORRENT_TEST(ban_peers)
464 {
465 torrent_state st = init_state();
466 mock_torrent t(&st);
467 st.allow_multiple_connections_per_ip = false;
468 peer_list p(allocator);
469 t.m_p = &p;
470
471 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.1", 4000));
472
473 TEST_EQUAL(p.num_connect_candidates(), 1);
474 auto c = std::make_shared<mock_peer_connection>(&t, true, ep("10.0.0.1", 8080));
475 p.new_connection(*c, 0, &st);
476 TEST_EQUAL(p.num_connect_candidates(), 0);
477 TEST_EQUAL(p.num_peers(), 1);
478 st.erased.clear();
479
480 // now, ban the peer
481 bool ok = p.ban_peer(c->peer_info_struct());
482 TEST_EQUAL(ok, true);
483 TEST_EQUAL(peer1->banned, true);
484 // we still have it in the list
485 TEST_EQUAL(p.num_peers(), 1);
486 // it's just not a connect candidate, nor allowed to receive incoming connections
487 TEST_EQUAL(p.num_connect_candidates(), 0);
488
489 p.connection_closed(*c, 0, &st);
490 TEST_EQUAL(p.num_peers(), 1);
491 TEST_EQUAL(p.num_connect_candidates(), 0);
492 st.erased.clear();
493
494 c = std::make_shared<mock_peer_connection>(&t, true, ep("10.0.0.1", 8080));
495 ok = p.new_connection(*c, 0, &st);
496 // since it's banned, we should not allow this incoming connection
497 TEST_EQUAL(ok, false);
498 TEST_EQUAL(p.num_connect_candidates(), 0);
499 st.erased.clear();
500 }
501
502 // test erase_peers when we fill up the peer list
TORRENT_TEST(erase_peers)503 TORRENT_TEST(erase_peers)
504 {
505 torrent_state st = init_state();
506 mock_torrent t(&st);
507 st.max_peerlist_size = 100;
508 st.allow_multiple_connections_per_ip = true;
509 peer_list p(allocator);
510 t.m_p = &p;
511
512 for (int i = 0; i < 100; ++i)
513 {
514 TEST_EQUAL(st.erased.size(), 0);
515 tcp::endpoint ep = rand_tcp_ep();
516 torrent_peer* peer = add_peer(p, st, ep);
517 TEST_CHECK(peer);
518 if (peer == nullptr || st.erased.size() > 0)
519 {
520 std::printf("unexpected rejection of peer: %s | %d in list. "
521 "added peer %p, erased %d peers\n"
522 , print_endpoint(ep).c_str(), p.num_peers(), static_cast<void*>(peer)
523 , int(st.erased.size()));
524 }
525 }
526 TEST_EQUAL(p.num_peers(), 100);
527
528 // trigger the eviction of one peer
529 torrent_peer* peer = p.add_peer(rand_tcp_ep(), {}, {}, &st);
530 // we either removed an existing peer, or rejected this one
531 // either is valid behavior when the list is full
532 TEST_CHECK(st.erased.size() == 1 || peer == nullptr);
533 }
534
535 // test set_ip_filter
TORRENT_TEST(set_ip_filter)536 TORRENT_TEST(set_ip_filter)
537 {
538 torrent_state st = init_state();
539 std::vector<address> banned;
540
541 mock_torrent t(&st);
542 peer_list p(allocator);
543 t.m_p = &p;
544
545 for (int i = 0; i < 100; ++i)
546 {
547 p.add_peer(tcp::endpoint(
548 address_v4(std::uint32_t((10 << 24) + ((i + 10) << 16))), 353), {}, {}, &st);
549 TEST_EQUAL(st.erased.size(), 0);
550 st.erased.clear();
551 }
552 TEST_EQUAL(p.num_peers(), 100);
553 TEST_EQUAL(p.num_connect_candidates(), 100);
554
555 // trigger the removal of one peer
556 ip_filter filter;
557 filter.add_rule(addr4("10.13.0.0"), addr4("10.13.255.255"), ip_filter::blocked);
558 p.apply_ip_filter(filter, &st, banned);
559 TEST_EQUAL(st.erased.size(), 1);
560 TEST_EQUAL(st.erased[0]->address(), addr4("10.13.0.0"));
561 TEST_EQUAL(p.num_peers(), 99);
562 TEST_EQUAL(p.num_connect_candidates(), 99);
563 }
564
565 // test set_port_filter
TORRENT_TEST(set_port_filter)566 TORRENT_TEST(set_port_filter)
567 {
568 torrent_state st = init_state();
569 std::vector<address> banned;
570
571 mock_torrent t(&st);
572 peer_list p(allocator);
573 t.m_p = &p;
574
575 for (int i = 0; i < 100; ++i)
576 {
577 p.add_peer(tcp::endpoint(
578 address_v4(std::uint32_t((10 << 24) + ((i + 10) << 16))), std::uint16_t(i + 10)), {}, {}, &st);
579 TEST_EQUAL(st.erased.size(), 0);
580 st.erased.clear();
581 }
582 TEST_EQUAL(p.num_peers(), 100);
583 TEST_EQUAL(p.num_connect_candidates(), 100);
584
585 // trigger the removal of one peer
586 port_filter filter;
587 filter.add_rule(13, 13, port_filter::blocked);
588 p.apply_port_filter(filter, &st, banned);
589 TEST_EQUAL(st.erased.size(), 1);
590 TEST_EQUAL(st.erased[0]->address(), addr4("10.13.0.0"));
591 TEST_EQUAL(st.erased[0]->port, 13);
592 TEST_EQUAL(p.num_peers(), 99);
593 TEST_EQUAL(p.num_connect_candidates(), 99);
594 }
595
596 // test set_max_failcount
TORRENT_TEST(set_max_failcount)597 TORRENT_TEST(set_max_failcount)
598 {
599 torrent_state st = init_state();
600
601 mock_torrent t(&st);
602 peer_list p(allocator);
603 t.m_p = &p;
604
605 for (int i = 0; i < 100; ++i)
606 {
607 torrent_peer* peer = p.add_peer(tcp::endpoint(
608 address_v4(std::uint32_t((10 << 24) + ((i + 10) << 16))), std::uint16_t(i + 10)), {}, {}, &st);
609 TEST_EQUAL(st.erased.size(), 0);
610 st.erased.clear();
611 // every other peer has a failcount of 1
612 if (i % 2) p.inc_failcount(peer);
613 }
614 TEST_EQUAL(p.num_peers(), 100);
615 TEST_EQUAL(p.num_connect_candidates(), 100);
616
617 // set the max failcount to 1 and observe how half the peers no longer
618 // are connect candidates
619 st.max_failcount = 1;
620 p.set_max_failcount(&st);
621
622 TEST_EQUAL(p.num_connect_candidates(), 50);
623 TEST_EQUAL(p.num_peers(), 100);
624 }
625
626 // test set_seed
TORRENT_TEST(set_seed)627 TORRENT_TEST(set_seed)
628 {
629 torrent_state st = init_state();
630
631 mock_torrent t(&st);
632 peer_list p(allocator);
633 t.m_p = &p;
634
635 for (int i = 0; i < 100; ++i)
636 {
637 torrent_peer* peer = p.add_peer(tcp::endpoint(
638 address_v4(std::uint32_t((10 << 24) + ((i + 10) << 16))), std::uint16_t(i + 10)), {}, {}, &st);
639 TEST_EQUAL(st.erased.size(), 0);
640 st.erased.clear();
641 // make every other peer a seed
642 if (i % 2) p.set_seed(peer, true);
643 }
644 TEST_EQUAL(p.num_peers(), 100);
645 TEST_EQUAL(p.num_connect_candidates(), 100);
646
647 // now, the torrent completes and we're no longer interested in
648 // connecting to seeds. Make sure half the peers are no longer
649 // considered connect candidates
650 st.is_finished = true;
651
652 // this will make the peer_list recalculate the connect candidates
653 std::vector<torrent_peer*> peers;
654 p.connect_one_peer(1, &st);
655
656 TEST_EQUAL(p.num_connect_candidates(), 50);
657 TEST_EQUAL(p.num_peers(), 100);
658 }
659
660 // test has_peer
TORRENT_TEST(has_peer)661 TORRENT_TEST(has_peer)
662 {
663 torrent_state st = init_state();
664 std::vector<address> banned;
665
666 mock_torrent t(&st);
667 peer_list p(allocator);
668 t.m_p = &p;
669
670 torrent_peer* peer1 = add_peer(p, st, ep("10.10.0.1", 10));
671 torrent_peer* peer2 = add_peer(p, st, ep("10.10.0.2", 11));
672
673 TEST_EQUAL(p.num_peers(), 2);
674 TEST_EQUAL(p.num_connect_candidates(), 2);
675
676 TEST_EQUAL(p.has_peer(peer1), true);
677 TEST_EQUAL(p.has_peer(peer2), true);
678
679 ip_filter filter;
680 filter.add_rule(addr4("10.10.0.1"), addr4("10.10.0.1"), ip_filter::blocked);
681 p.apply_ip_filter(filter, &st, banned);
682 TEST_EQUAL(st.erased.size(), 1);
683 st.erased.clear();
684
685 TEST_EQUAL(p.num_peers(), 1);
686 TEST_EQUAL(p.num_connect_candidates(), 1);
687
688 TEST_EQUAL(p.has_peer(peer1), false);
689 TEST_EQUAL(p.has_peer(peer2), true);
690 }
691
692 // test connect_candidates torrent_finish
TORRENT_TEST(connect_candidates_finish)693 TORRENT_TEST(connect_candidates_finish)
694 {
695 torrent_state st = init_state();
696 std::vector<address> banned;
697
698 mock_torrent t(&st);
699 peer_list p(allocator);
700 t.m_p = &p;
701
702 torrent_peer* peer1 = add_peer(p, st, ep("10.10.0.1", 10));
703 TEST_CHECK(peer1);
704 p.set_seed(peer1, true);
705 torrent_peer* peer2 = add_peer(p, st, ep("10.10.0.2", 11));
706 TEST_CHECK(peer2);
707 p.set_seed(peer2, true);
708 torrent_peer* peer3 = add_peer(p, st, ep("10.10.0.3", 11));
709 TEST_CHECK(peer3);
710 p.set_seed(peer3, true);
711 torrent_peer* peer4 = add_peer(p, st, ep("10.10.0.4", 11));
712 TEST_CHECK(peer4);
713 torrent_peer* peer5 = add_peer(p, st, ep("10.10.0.5", 11));
714 TEST_CHECK(peer5);
715
716 TEST_EQUAL(p.num_peers(), 5);
717 TEST_EQUAL(p.num_connect_candidates(), 5);
718
719 st.is_finished = true;
720 // we're finished downloading now, only the non-seeds are
721 // connect candidates
722
723 // connect to one of them
724 connect_peer(p, t, st);
725
726 TEST_EQUAL(p.num_peers(), 5);
727 // and there should be one left
728 TEST_EQUAL(p.num_connect_candidates(), 1);
729 }
730
731 // test self-connection
TORRENT_TEST(self_connection)732 TORRENT_TEST(self_connection)
733 {
734 torrent_state st = init_state();
735 mock_torrent t(&st);
736 st.allow_multiple_connections_per_ip = false;
737 peer_list p(allocator);
738 t.m_p = &p;
739
740 // add and connect peer
741 torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 3000));
742 connect_peer(p, t, st);
743
744 auto con_out = shared_from_this(peer->connection);
745 con_out->set_local_ep(ep("10.0.0.2", 8080));
746
747 auto con_in = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 8080));
748 con_in->set_local_ep(ep("10.0.0.2", 3000));
749
750 p.new_connection(*con_in, 0, &st);
751
752 // from the peer_list's point of view, this looks like we made one
753 // outgoing connection and received an incoming one. Since they share
754 // the exact same endpoints (IP ports) but just swapped source and
755 // destination, the peer list is supposed to figure out that we connected
756 // to ourself and disconnect it
757 TEST_EQUAL(con_out->was_disconnected(), true);
758 TEST_EQUAL(con_in->was_disconnected(), true);
759 }
760
761 // test double connection (both incoming)
TORRENT_TEST(double_connection)762 TORRENT_TEST(double_connection)
763 {
764 torrent_state st = init_state();
765 mock_torrent t(&st);
766 st.allow_multiple_connections_per_ip = false;
767 peer_list p(allocator);
768 t.m_p = &p;
769
770 // we are 10.0.0.1 and the other peer is 10.0.0.2
771
772 // first incoming connection
773 auto con1 = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 7528));
774 con1->set_local_ep(ep("10.0.0.1", 8080));
775
776 p.new_connection(*con1, 0, &st);
777
778 // and the incoming connection
779 auto con2 = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 3561));
780 con2->set_local_ep(ep("10.0.0.1", 8080));
781
782 p.new_connection(*con2, 0, &st);
783
784 // the second incoming connection should be closed
785 TEST_EQUAL(con1->was_disconnected(), false);
786 TEST_EQUAL(con2->was_disconnected(), true);
787 }
788
789 // test double connection (we loose)
TORRENT_TEST(double_connection_loose)790 TORRENT_TEST(double_connection_loose)
791 {
792 torrent_state st = init_state();
793 mock_torrent t(&st);
794 st.allow_multiple_connections_per_ip = false;
795 peer_list p(allocator);
796 t.m_p = &p;
797
798 // we are 10.0.0.1 and the other peer is 10.0.0.2
799
800 // our outgoing connection
801 torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 3000));
802 connect_peer(p, t, st);
803
804 auto con_out = shared_from_this(peer->connection);
805 con_out->set_local_ep(ep("10.0.0.1", 3163));
806
807 // and the incoming connection
808 auto con_in = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 3561));
809 con_in->set_local_ep(ep("10.0.0.1", 8080));
810
811 p.new_connection(*con_in, 0, &st);
812
813 // the rules are documented in peer_list.cpp
814 TEST_EQUAL(con_out->was_disconnected(), true);
815 TEST_EQUAL(con_in->was_disconnected(), false);
816 }
817
818 // test double connection with identical ports (random)
TORRENT_TEST(double_connection_random)819 TORRENT_TEST(double_connection_random)
820 {
821 int in = 0;
822 int out = 0;
823 for (int i = 0; i < 30; ++i)
824 {
825 torrent_state st = init_state();
826 mock_torrent t(&st);
827 st.allow_multiple_connections_per_ip = false;
828 peer_list p(allocator);
829 t.m_p = &p;
830
831 // we are 10.0.0.1 and the other peer is 10.0.0.2
832
833 // our outgoing connection
834 torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 3000));
835 connect_peer(p, t, st);
836
837 auto con_out = static_cast<mock_peer_connection*>(peer->connection)->shared_from_this();
838 con_out->set_local_ep(ep("10.0.0.1", 3000));
839
840 // and the incoming connection
841 auto con_in = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 3000));
842 con_in->set_local_ep(ep("10.0.0.1", 3000));
843
844 p.new_connection(*con_in, 0, &st);
845
846 // the rules are documented in peer_list.cpp
847 out += con_out->was_disconnected();
848 in += con_in->was_disconnected();
849 }
850 // we should have gone different ways randomly
851 TEST_CHECK(out > 0);
852 TEST_CHECK(in > 0);
853 }
854
855 // test double connection (we win)
TORRENT_TEST(double_connection_win)856 TORRENT_TEST(double_connection_win)
857 {
858 torrent_state st = init_state();
859 mock_torrent t(&st);
860 st.allow_multiple_connections_per_ip = false;
861 peer_list p(allocator);
862 t.m_p = &p;
863
864 // we are 10.0.0.1 and the other peer is 10.0.0.2
865
866 // our outgoing connection
867 torrent_peer* peer = add_peer(p, st, ep("10.0.0.2", 8080));
868 connect_peer(p, t, st);
869
870 auto con_out = shared_from_this(peer->connection);
871 con_out->set_local_ep(ep("10.0.0.1", 3163));
872
873 //and the incoming connection
874 auto con_in = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.0.2", 3561));
875 con_in->set_local_ep(ep("10.0.0.1", 3000));
876
877 p.new_connection(*con_in, 0, &st);
878
879 // the rules are documented in peer_list.cpp
880 TEST_EQUAL(con_out->was_disconnected(), false);
881 TEST_EQUAL(con_in->was_disconnected(), true);
882 }
883
884 // test incoming connection when we are at the list size limit
TORRENT_TEST(incoming_size_limit)885 TORRENT_TEST(incoming_size_limit)
886 {
887 torrent_state st = init_state();
888 st.max_peerlist_size = 5;
889 mock_torrent t(&st);
890 st.allow_multiple_connections_per_ip = false;
891 peer_list p(allocator);
892 t.m_p = &p;
893
894 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.1", 8080));
895 TEST_CHECK(peer1);
896 TEST_EQUAL(p.num_peers(), 1);
897 torrent_peer* peer2 = add_peer(p, st, ep("10.0.0.2", 8080));
898 TEST_CHECK(peer2);
899 TEST_EQUAL(p.num_peers(), 2);
900 torrent_peer* peer3 = add_peer(p, st, ep("10.0.0.3", 8080));
901 TEST_CHECK(peer3);
902 TEST_EQUAL(p.num_peers(), 3);
903 torrent_peer* peer4 = add_peer(p, st, ep("10.0.0.4", 8080));
904 TEST_CHECK(peer4);
905 TEST_EQUAL(p.num_peers(), 4);
906 torrent_peer* peer5 = add_peer(p, st, ep("10.0.0.5", 8080));
907 TEST_CHECK(peer5);
908 TEST_EQUAL(p.num_peers(), 5);
909
910 auto con_in = std::make_shared<mock_peer_connection>(&t, false, ep("10.0.1.2", 3561));
911 con_in->set_local_ep(ep("10.0.2.1", 3000));
912
913 // since we're already at 5 peers in the peer list, this call should
914 // erase one of the existing ones.
915 p.new_connection(*con_in, 0, &st);
916
917 TEST_EQUAL(con_in->was_disconnected(), false);
918 TEST_EQUAL(p.num_peers(), 5);
919
920 // one of the previous ones should have been removed
921 TEST_EQUAL(has_peer(p, ep("10.0.0.1", 8080))
922 + has_peer(p, ep("10.0.0.2", 8080))
923 + has_peer(p, ep("10.0.0.3", 8080))
924 + has_peer(p, ep("10.0.0.4", 8080))
925 + has_peer(p, ep("10.0.0.5", 8080))
926 , 4);
927 }
928
929 // test new peer when we are at the list size limit
TORRENT_TEST(new_peer_size_limit)930 TORRENT_TEST(new_peer_size_limit)
931 {
932 torrent_state st = init_state();
933 st.max_peerlist_size = 5;
934 mock_torrent t(&st);
935 st.allow_multiple_connections_per_ip = false;
936 peer_list p(allocator);
937 t.m_p = &p;
938
939 torrent_peer* peer1 = add_peer(p, st, ep("10.0.0.1", 8080));
940 TEST_CHECK(peer1);
941 TEST_EQUAL(p.num_peers(), 1);
942 torrent_peer* peer2 = add_peer(p, st, ep("10.0.0.2", 8080));
943 TEST_CHECK(peer2);
944 TEST_EQUAL(p.num_peers(), 2);
945 torrent_peer* peer3 = add_peer(p, st, ep("10.0.0.3", 8080));
946 TEST_CHECK(peer3);
947 TEST_EQUAL(p.num_peers(), 3);
948 torrent_peer* peer4 = add_peer(p, st, ep("10.0.0.4", 8080));
949 TEST_CHECK(peer4);
950 TEST_EQUAL(p.num_peers(), 4);
951 torrent_peer* peer5 = add_peer(p, st, ep("10.0.0.5", 8080));
952 TEST_CHECK(peer5);
953 TEST_EQUAL(p.num_peers(), 5);
954 torrent_peer* peer6 = p.add_peer(ep("10.0.0.6", 8080), {}, {}, &st);
955 TEST_CHECK(peer6 == nullptr);
956 TEST_EQUAL(p.num_peers(), 5);
957
958 // one of the connection should have been removed
959 TEST_EQUAL(has_peer(p, ep("10.0.0.1", 8080))
960 + has_peer(p, ep("10.0.0.2", 8080))
961 + has_peer(p, ep("10.0.0.3", 8080))
962 + has_peer(p, ep("10.0.0.4", 8080))
963 + has_peer(p, ep("10.0.0.5", 8080))
964 + has_peer(p, ep("10.0.0.6", 8080))
965 , 5);
966 }
967
968 // TODO: test erasing peers
969 // TODO: test update_peer_port with allow_multiple_connections_per_ip and without
970 // TODO: test add i2p peers
971 // TODO: test allow_i2p_mixed
972 // TODO: test insert_peer failing with all error conditions
973 // TODO: test IPv6
974 // TODO: test connect_to_peer() failing
975 // TODO: test connection_closed
976 // TODO: connect candidates recalculation when incrementing failcount
977