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