1 /*
2 
3 Copyright (c) 2010, 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 "test.hpp"
34 #include "settings.hpp"
35 #include "setup_swarm.hpp"
36 #include "setup_transfer.hpp" // for addr()
37 #include "utils.hpp" // for print_alerts
38 #include "create_torrent.hpp"
39 #include "simulator/simulator.hpp"
40 #include "simulator/http_server.hpp"
41 #include "simulator/utils.hpp"
42 #include "libtorrent/alert_types.hpp"
43 #include "libtorrent/announce_entry.hpp"
44 #include "libtorrent/session.hpp"
45 #include "libtorrent/create_torrent.hpp"
46 #include "libtorrent/file_storage.hpp"
47 #include "libtorrent/torrent_info.hpp"
48 
49 #include <iostream>
50 
51 using namespace lt;
52 using namespace sim;
53 
54 using chrono::duration_cast;
55 
56 // seconds
57 const int duration = 10000;
58 
59 template <typename Tp1, typename Tp2>
eq(Tp1 const lhs,Tp2 const rhs)60 bool eq(Tp1 const lhs, Tp2 const rhs)
61 {
62 	return std::abs(lt::duration_cast<seconds>(lhs - rhs).count()) <= 1;
63 }
64 
test_interval(int interval)65 void test_interval(int interval)
66 {
67 	using sim::asio::ip::address_v4;
68 	sim::default_config network_cfg;
69 	sim::simulation sim{network_cfg};
70 
71 	bool ran_to_completion = false;
72 
73 	sim::asio::io_service web_server(sim, address_v4::from_string("2.2.2.2"));
74 	// listen on port 8080
75 	sim::http_server http(web_server, 8080);
76 
77 	// the timestamps of all announces
78 	std::vector<lt::time_point> announces;
79 
80 	http.register_handler("/announce"
81 		, [&announces,interval,&ran_to_completion](std::string /* method */
82 			, std::string /* req */
83 		, std::map<std::string, std::string>&)
84 	{
85 		// don't collect events once we're done. We're not interested in the
86 		// tracker stopped announce for instance
87 		if (!ran_to_completion)
88 			announces.push_back(lt::clock_type::now());
89 
90 		char response[500];
91 		int const size = std::snprintf(response, sizeof(response), "d8:intervali%de5:peers0:e", interval);
92 		return sim::send_response(200, "OK", size) + response;
93 	});
94 
95 	std::vector<lt::time_point> announce_alerts;
96 
97 	lt::settings_pack default_settings = settings();
98 	// since the test tracker is only listening on IPv4 we need to configure the
99 	// client to do the same so that the number of tracker_announce_alerts matches
100 	// the number of announces seen by the tracker
101 	default_settings.set_str(settings_pack::listen_interfaces, "0.0.0.0:6881");
102 	lt::add_torrent_params default_add_torrent;
103 
104 	setup_swarm(1, swarm_test::upload, sim, default_settings, default_add_torrent
105 		// add session
106 		, [](lt::settings_pack&) {}
107 		// add torrent
108 		, [](lt::add_torrent_params& params) {
109 			params.trackers.push_back("http://2.2.2.2:8080/announce");
110 		}
111 		// on alert
112 		, [&](lt::alert const* a, lt::session&) {
113 
114 			if (ran_to_completion) return;
115 			if (lt::alert_cast<lt::tracker_announce_alert>(a))
116 			{
117 				announce_alerts.push_back(a->timestamp());
118 			}
119 		}
120 		// terminate
121 		, [&](int const ticks, lt::session&) -> bool {
122 			if (ticks > duration + 1)
123 			{
124 				ran_to_completion = true;
125 				return true;
126 			}
127 			return false;
128 		});
129 
130 	TEST_CHECK(ran_to_completion);
131 	TEST_EQUAL(announce_alerts.size(), announces.size());
132 
133 	lt::time_point last_announce = announces[0];
134 	lt::time_point last_alert = announce_alerts[0];
135 	for (int i = 1; i < int(announces.size()); ++i)
136 	{
137 		// make sure the interval is within 1 second of what it's supposed to be
138 		// (this accounts for network latencies, and the second-granularity
139 		// timestamps)
140 		TEST_CHECK(eq(duration_cast<lt::seconds>(announces[i] - last_announce), lt::seconds(interval)));
141 		last_announce = announces[i];
142 
143 		TEST_CHECK(eq(duration_cast<lt::milliseconds>(announce_alerts[i] - last_alert), lt::seconds(interval)));
144 		last_alert = announce_alerts[i];
145 	}
146 }
147 
148 template <typename AddTorrent, typename OnAlert>
test_event(swarm_test_t const type,AddTorrent add_torrent,OnAlert on_alert)149 std::vector<std::string> test_event(swarm_test_t const type
150 	, AddTorrent add_torrent
151 	, OnAlert on_alert)
152 {
153 	using sim::asio::ip::address_v4;
154 	sim::default_config network_cfg;
155 	sim::simulation sim{network_cfg};
156 
157 	sim::asio::io_service web_server(sim, address_v4::from_string("2.2.2.2"));
158 	// listen on port 8080
159 	sim::http_server http(web_server, 8080);
160 
161 	// the request strings of all announces
162 	std::vector<std::string> announces;
163 
164 	const int interval = 500;
165 
166 	http.register_handler("/announce"
167 	, [&](std::string method, std::string req
168 		, std::map<std::string, std::string>&)
169 	{
170 		TEST_EQUAL(method, "GET");
171 		announces.push_back(req);
172 
173 		char response[500];
174 		int const size = std::snprintf(response, sizeof(response), "d8:intervali%de5:peers0:e", interval);
175 		return sim::send_response(200, "OK", size) + response;
176 	});
177 
178 	lt::settings_pack default_settings = settings();
179 	lt::add_torrent_params default_add_torrent;
180 
181 	setup_swarm(2, type, sim, default_settings, default_add_torrent
182 		// add session
183 		, [](lt::settings_pack&) { }
184 		// add torrent
185 		, add_torrent
186 		// on alert
187 		, on_alert
188 		// terminate
189 		, [&](int const ticks, lt::session& ses) -> bool
190 		{
191 			return ticks > duration;
192 		});
193 
194 	// this is some basic sanity checking of the announces that should always be
195 	// true.
196 	// the first announce should be event=started then no other announce should
197 	// have event=started.
198 	// only the last announce should have event=stopped.
199 	TEST_CHECK(announces.size() > 2);
200 
201 	if (announces.size() <= 2) return {};
202 	TEST_CHECK(announces.front().find("&event=started") != std::string::npos);
203 	for (auto const& a : span<std::string>(announces).subspan(1))
204 		TEST_CHECK(a.find("&event=started") == std::string::npos);
205 
206 	TEST_CHECK(announces.back().find("&event=stopped") != std::string::npos);
207 	for (auto const& a : span<std::string>(announces).first(announces.size() - 1))
208 		TEST_CHECK(a.find("&event=stopped") == std::string::npos);
209 	return announces;
210 }
211 
TORRENT_TEST(event_completed_downloading)212 TORRENT_TEST(event_completed_downloading)
213 {
214 	auto const announces = test_event(swarm_test::download
215 		, [](lt::add_torrent_params& params) {
216 			params.trackers.push_back("http://2.2.2.2:8080/announce");
217 		}
218 		, [&](lt::alert const*, lt::session&) {}
219 	);
220 
221 	// make sure there's exactly one event=completed
222 	TEST_CHECK(std::count_if(announces.begin(), announces.end(), [](std::string const& s)
223 		{ return s.find("&event=completed") != std::string::npos; }) == 1);
224 }
225 
TORRENT_TEST(event_completed_downloading_replace_trackers)226 TORRENT_TEST(event_completed_downloading_replace_trackers)
227 {
228 	auto const announces = test_event(swarm_test::download
229 		, [](lt::add_torrent_params& params) {}
230 		, [&](lt::alert const* a, lt::session&) {
231 			if (auto const* at = alert_cast<add_torrent_alert>(a))
232 				at->handle.replace_trackers({announce_entry{"http://2.2.2.2:8080/announce"}});
233 		}
234 	);
235 
236 	// make sure there's exactly one event=completed
237 	TEST_CHECK(std::count_if(announces.begin(), announces.end(), [](std::string const& s)
238 		{ return s.find("&event=completed") != std::string::npos; }) == 1);
239 }
240 
TORRENT_TEST(event_completed_seeding)241 TORRENT_TEST(event_completed_seeding)
242 {
243 	auto const announces = test_event(swarm_test::upload | swarm_test::no_auto_stop
244 		, [](lt::add_torrent_params& params) {
245 			params.trackers.push_back("http://2.2.2.2:8080/announce");
246 		}
247 		, [&](lt::alert const*, lt::session&) {}
248 		);
249 
250 	// make sure there are no event=completed, since we added the torrent as a
251 	// seed
252 	TEST_CHECK(std::count_if(announces.begin(), announces.end(), [](std::string const& s)
253 		{ return s.find("&event=completed") != std::string::npos; }) == 0);
254 }
255 
256 
TORRENT_TEST(event_completed_seeding_replace_trackers)257 TORRENT_TEST(event_completed_seeding_replace_trackers)
258 {
259 	auto const announces = test_event(swarm_test::upload | swarm_test::no_auto_stop
260 		, [](lt::add_torrent_params& params) {}
261 		, [&](lt::alert const* a, lt::session&) {
262 			if (auto const* at = alert_cast<add_torrent_alert>(a))
263 				at->handle.replace_trackers({announce_entry{"http://2.2.2.2:8080/announce"}});
264 		}
265 	);
266 
267 	// make sure there are no event=completed, since we added the torrent as a
268 	// seed
269 	TEST_CHECK(std::count_if(announces.begin(), announces.end(), [](std::string const& s)
270 		{ return s.find("&event=completed") != std::string::npos; }) == 0);
271 }
272 
TORRENT_TEST(announce_interval_440)273 TORRENT_TEST(announce_interval_440)
274 {
275 	test_interval(440);
276 }
277 
TORRENT_TEST(announce_interval_1800)278 TORRENT_TEST(announce_interval_1800)
279 {
280 	test_interval(1800);
281 }
282 
TORRENT_TEST(announce_interval_1200)283 TORRENT_TEST(announce_interval_1200)
284 {
285 	test_interval(3600);
286 }
287 
288 struct sim_config : sim::default_config
289 {
sim_configsim_config290 	explicit sim_config(bool ipv6 = true) : ipv6(ipv6) {}
291 
hostname_lookupsim_config292 	chrono::high_resolution_clock::duration hostname_lookup(
293 		asio::ip::address const& requestor
294 		, std::string hostname
295 		, std::vector<asio::ip::address>& result
296 		, boost::system::error_code& ec) override
297 	{
298 		if (hostname == "tracker.com")
299 		{
300 			result.push_back(address_v4::from_string("123.0.0.2"));
301 			if (ipv6)
302 				result.push_back(address_v6::from_string("ff::dead:beef"));
303 			return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
304 		}
305 		if (hostname == "localhost")
306 		{
307 			result.push_back(address_v4::from_string("127.0.0.1"));
308 			if (ipv6)
309 				result.push_back(address_v6::from_string("::1"));
310 			return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(1));
311 		}
312 		if (hostname == "xn--tracker-.com")
313 		{
314 			result.push_back(address_v4::from_string("123.0.0.2"));
315 			return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
316 		}
317 		if (hostname == "redirector.com")
318 		{
319 			result.push_back(address_v4::from_string("123.0.0.4"));
320 			return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
321 		}
322 
323 		return default_config::hostname_lookup(requestor, hostname, result, ec);
324 	}
325 
326 	bool ipv6;
327 };
328 
on_alert_notify(lt::session * ses)329 void on_alert_notify(lt::session* ses)
330 {
331 	ses->get_io_service().post([ses] {
332 		std::vector<lt::alert*> alerts;
333 		ses->pop_alerts(&alerts);
334 
335 		for (lt::alert* a : alerts)
336 		{
337 			lt::time_duration d = a->timestamp().time_since_epoch();
338 			std::uint32_t const millis = std::uint32_t(
339 				lt::duration_cast<lt::milliseconds>(d).count());
340 			std::printf("%4d.%03d: %s\n", millis / 1000, millis % 1000,
341 				a->message().c_str());
342 		}
343 	});
344 }
345 
346 static const int num_interfaces = 3;
347 
test_ipv6_support(char const * listen_interfaces,int const expect_v4,int const expect_v6)348 void test_ipv6_support(char const* listen_interfaces
349 	, int const expect_v4, int const expect_v6)
350 {
351 	using sim::asio::ip::address_v4;
352 	sim_config network_cfg;
353 	sim::simulation sim{network_cfg};
354 
355 	sim::asio::io_service web_server_v4(sim, address_v4::from_string("123.0.0.2"));
356 	sim::asio::io_service web_server_v6(sim, address_v6::from_string("ff::dead:beef"));
357 
358 	// listen on port 8080
359 	sim::http_server http_v4(web_server_v4, 8080);
360 	sim::http_server http_v6(web_server_v6, 8080);
361 
362 	int v4_announces = 0;
363 	int v6_announces = 0;
364 
365 	// if we're not listening we'll just report port 0
366 	std::string const expect_port = (listen_interfaces && listen_interfaces == ""_sv)
367 		? "&port=1" : "&port=6881";
368 
369 	http_v4.register_handler("/announce"
370 	, [&v4_announces,expect_port](std::string method, std::string req
371 		, std::map<std::string, std::string>&)
372 	{
373 		++v4_announces;
374 		TEST_EQUAL(method, "GET");
375 		TEST_CHECK(req.find(expect_port) != std::string::npos);
376 		char response[500];
377 		int const size = std::snprintf(response, sizeof(response), "d8:intervali1800e5:peers0:e");
378 		return sim::send_response(200, "OK", size) + response;
379 	});
380 
381 	http_v6.register_handler("/announce"
382 	, [&v6_announces,expect_port](std::string method, std::string req
383 		, std::map<std::string, std::string>&)
384 	{
385 		++v6_announces;
386 		TEST_EQUAL(method, "GET");
387 
388 		TEST_CHECK(req.find(expect_port) != std::string::npos);
389 		char response[500];
390 		int const size = std::snprintf(response, sizeof(response), "d8:intervali1800e5:peers0:e");
391 		return sim::send_response(200, "OK", size) + response;
392 	});
393 
394 	{
395 		lt::session_proxy zombie;
396 
397 		std::vector<asio::ip::address> ips;
398 
399 		for (int i = 0; i < num_interfaces; i++)
400 		{
401 			char ep[30];
402 			std::snprintf(ep, sizeof(ep), "123.0.0.%d", i + 1);
403 			ips.push_back(address::from_string(ep));
404 			std::snprintf(ep, sizeof(ep), "ffff::1337:%d", i + 1);
405 			ips.push_back(address::from_string(ep));
406 		}
407 
408 		asio::io_service ios(sim, ips);
409 		lt::settings_pack sett = settings();
410 		if (listen_interfaces)
411 		{
412 			sett.set_str(settings_pack::listen_interfaces, listen_interfaces);
413 		}
414 		std::unique_ptr<lt::session> ses(new lt::session(sett, ios));
415 
416 		ses->set_alert_notify(std::bind(&on_alert_notify, ses.get()));
417 
418 		lt::add_torrent_params p;
419 		p.name = "test-torrent";
420 		p.save_path = ".";
421 		p.info_hash.assign("abababababababababab");
422 
423 //TODO: parameterize http vs. udp here
424 		p.trackers.push_back("http://tracker.com:8080/announce");
425 		ses->async_add_torrent(p);
426 
427 		// stop the torrent 5 seconds in
428 		sim::timer t1(sim, lt::seconds(5)
429 			, [&ses](boost::system::error_code const&)
430 		{
431 			std::vector<lt::torrent_handle> torrents = ses->get_torrents();
432 			for (auto const& t : torrents)
433 			{
434 				t.pause();
435 			}
436 		});
437 
438 		// then shut down 10 seconds in
439 		sim::timer t2(sim, lt::seconds(10)
440 			, [&ses,&zombie](boost::system::error_code const&)
441 		{
442 			zombie = ses->abort();
443 			ses.reset();
444 		});
445 
446 		sim.run();
447 	}
448 
449 	TEST_EQUAL(v4_announces, expect_v4);
450 	TEST_EQUAL(v6_announces, expect_v6);
451 }
452 
test_udpv6_support(char const * listen_interfaces,int const expect_v4,int const expect_v6)453 void test_udpv6_support(char const* listen_interfaces
454 	, int const expect_v4, int const expect_v6)
455 {
456 	using sim::asio::ip::address_v4;
457 	sim_config network_cfg;
458 	sim::simulation sim{network_cfg};
459 
460 	sim::asio::io_service web_server_v4(sim, address_v4::from_string("123.0.0.2"));
461 	sim::asio::io_service web_server_v6(sim, address_v6::from_string("ff::dead:beef"));
462 
463 	int v4_announces = 0;
464 	int v6_announces = 0;
465 
466 	{
467 		lt::session_proxy zombie;
468 
469 		std::vector<asio::ip::address> ips;
470 
471 		for (int i = 0; i < num_interfaces; i++)
472 		{
473 			char ep[30];
474 			std::snprintf(ep, sizeof(ep), "123.0.0.%d", i + 1);
475 			ips.push_back(address::from_string(ep));
476 			std::snprintf(ep, sizeof(ep), "ffff::1337:%d", i + 1);
477 			ips.push_back(address::from_string(ep));
478 		}
479 
480 		asio::io_service ios(sim, ips);
481 		lt::settings_pack sett = settings();
482 		if (listen_interfaces)
483 		{
484 			sett.set_str(settings_pack::listen_interfaces, listen_interfaces);
485 		}
486 		std::unique_ptr<lt::session> ses(new lt::session(sett, ios));
487 
488 		// since we don't have a udp tracker to run in the sim, looking for the
489 		// alerts is the closest proxy
490 		ses->set_alert_notify([&]{
491 			ses->get_io_service().post([&] {
492 				std::vector<lt::alert*> alerts;
493 				ses->pop_alerts(&alerts);
494 
495 				for (lt::alert* a : alerts)
496 				{
497 					lt::time_duration d = a->timestamp().time_since_epoch();
498 					std::uint32_t const millis = std::uint32_t(
499 						lt::duration_cast<lt::milliseconds>(d).count());
500 					std::printf("%4d.%03d: %s\n", millis / 1000, millis % 1000,
501 						a->message().c_str());
502 					if (auto tr = alert_cast<tracker_announce_alert>(a))
503 					{
504 						if (is_v4(tr->local_endpoint))
505 							++v4_announces;
506 						else
507 							++v6_announces;
508 					}
509 					else if (alert_cast<tracker_error_alert>(a))
510 					{
511 						TEST_ERROR("unexpected tracker error");
512 					}
513 				}
514 			});
515 		});
516 
517 		lt::add_torrent_params p;
518 		p.name = "test-torrent";
519 		p.save_path = ".";
520 		p.info_hash.assign("abababababababababab");
521 
522 		p.trackers.push_back("udp://tracker.com:8080/announce");
523 		ses->async_add_torrent(p);
524 
525 		// stop the torrent 5 seconds in
526 		sim::timer t1(sim, lt::seconds(5)
527 			, [&ses](boost::system::error_code const&)
528 		{
529 			std::vector<lt::torrent_handle> torrents = ses->get_torrents();
530 			for (auto const& t : torrents)
531 			{
532 				t.pause();
533 			}
534 		});
535 
536 		// then shut down 10 seconds in
537 		sim::timer t2(sim, lt::seconds(10)
538 			, [&ses,&zombie](boost::system::error_code const&)
539 		{
540 			zombie = ses->abort();
541 			ses.reset();
542 		});
543 
544 		sim.run();
545 	}
546 
547 	TEST_EQUAL(v4_announces, expect_v4);
548 	TEST_EQUAL(v6_announces, expect_v6);
549 }
550 
551 // this test makes sure that a tracker whose host name resolves to both IPv6 and
552 // IPv4 addresses will be announced to twice, once for each address family
TORRENT_TEST(ipv6_support)553 TORRENT_TEST(ipv6_support)
554 {
555 	// null means default
556 	test_ipv6_support(nullptr, num_interfaces * 2, num_interfaces * 2);
557 }
558 
TORRENT_TEST(announce_no_listen)559 TORRENT_TEST(announce_no_listen)
560 {
561 	// if we don't listen on any sockets at all we should not announce to trackers
562 	test_ipv6_support("", 0, 0);
563 }
564 
TORRENT_TEST(announce_udp_no_listen)565 TORRENT_TEST(announce_udp_no_listen)
566 {
567 	// if we don't listen on any sockets at all we should not announce to trackers
568 	test_udpv6_support("", 0, 0);
569 }
570 
TORRENT_TEST(ipv6_support_bind_v4_v6_any)571 TORRENT_TEST(ipv6_support_bind_v4_v6_any)
572 {
573 	// 2 because there's one announce on startup and one when shutting down
574 	// IPv6 will send announces for each interface
575 	test_ipv6_support("0.0.0.0:6881,[::0]:6881", num_interfaces * 2, num_interfaces * 2);
576 }
577 
TORRENT_TEST(ipv6_support_bind_v6_any)578 TORRENT_TEST(ipv6_support_bind_v6_any)
579 {
580 	test_ipv6_support("[::0]:6881", 0, num_interfaces * 2);
581 }
582 
TORRENT_TEST(ipv6_support_bind_v4)583 TORRENT_TEST(ipv6_support_bind_v4)
584 {
585 	test_ipv6_support("123.0.0.3:6881", 2, 0);
586 }
587 
TORRENT_TEST(ipv6_support_bind_v6)588 TORRENT_TEST(ipv6_support_bind_v6)
589 {
590 	test_ipv6_support("[ffff::1337:1]:6881", 0, 2);
591 }
592 
TORRENT_TEST(ipv6_support_bind_v6_3interfaces)593 TORRENT_TEST(ipv6_support_bind_v6_3interfaces)
594 {
595 	test_ipv6_support("[ffff::1337:1]:6881,[ffff::1337:2]:6881,[ffff::1337:3]:6881", 0, 3 * 2);
596 }
597 
TORRENT_TEST(ipv6_support_bind_v4_v6)598 TORRENT_TEST(ipv6_support_bind_v4_v6)
599 {
600 	test_ipv6_support("123.0.0.3:6881,[ffff::1337:1]:6881", 2, 2);
601 }
602 
TORRENT_TEST(ipv6_support_bind_v6_v4)603 TORRENT_TEST(ipv6_support_bind_v6_v4)
604 {
605 	test_ipv6_support("[ffff::1337:1]:6881,123.0.0.3:6881", 2, 2);
606 }
607 
608 // this runs a simulation of a torrent with tracker(s), making sure the request
609 // received by the tracker matches the expectation.
610 // The Setup function is run first, giving the test an opportunity to add
611 // trackers to the torrent. It's expected to return the number of seconds to
612 // wait until test2 is called.
613 // The Announce function is called on http requests. Test1 is run on the session
614 // 5 seconds after startup. The tracker is running at 123.0.0.2 (or tracker.com)
615 // port 8080.
616 template <typename Setup, typename Announce, typename Test1, typename Test2>
tracker_test(Setup setup,Announce a,Test1 test1,Test2 test2,char const * url_path="/announce",char const * redirect="http://123.0.0.2/announce")617 void tracker_test(Setup setup, Announce a, Test1 test1, Test2 test2
618 	, char const* url_path = "/announce"
619 	, char const* redirect = "http://123.0.0.2/announce")
620 {
621 	using sim::asio::ip::address_v4;
622 	sim_config network_cfg;
623 	sim::simulation sim{network_cfg};
624 
625 	sim::asio::io_service tracker_ios(sim, address_v4::from_string("123.0.0.2"));
626 	sim::asio::io_service tracker_ios6(sim, address_v6::from_string("ff::dead:beef"));
627 	sim::asio::io_service redirector_ios(sim, address_v4::from_string("123.0.0.4"));
628 
629 	sim::asio::io_service tracker_lo_ios(sim, address_v4::from_string("127.0.0.1"));
630 	sim::asio::io_service tracker_lo_ios6(sim, address_v6::from_string("::1"));
631 
632 	// listen on port 8080
633 	sim::http_server http(tracker_ios, 8080);
634 	sim::http_server http6(tracker_ios6, 8080);
635 	sim::http_server http_lo(tracker_lo_ios, 8080);
636 	sim::http_server http6_lo(tracker_lo_ios6, 8080);
637 	sim::http_server http_redirect(redirector_ios, 8080);
638 
639 	http.register_handler(url_path, a);
640 	http6.register_handler(url_path, a);
641 	http_lo.register_handler(url_path, a);
642 	http6_lo.register_handler(url_path, a);
643 	http_redirect.register_redirect(url_path, redirect);
644 
645 	lt::session_proxy zombie;
646 
647 	asio::io_service ios(sim, { address_v4::from_string("123.0.0.3")
648 		, address_v6::from_string("ffff::1337") });
649 
650 	lt::settings_pack sett = settings();
651 	std::unique_ptr<lt::session> ses(new lt::session(sett, ios));
652 
653 	ses->set_alert_notify(std::bind(&on_alert_notify, ses.get()));
654 
655 	lt::add_torrent_params p;
656 	p.name = "test-torrent";
657 	p.save_path = ".";
658 	p.info_hash.assign("abababababababababab");
659 	int const delay = setup(p, *ses);
660 	ses->async_add_torrent(p);
661 
662 	// run the test 5 seconds in
663 	sim::timer t1(sim, lt::seconds(5)
664 		, [&ses,&test1](boost::system::error_code const&)
665 	{
666 		std::vector<lt::torrent_handle> torrents = ses->get_torrents();
667 		TEST_EQUAL(torrents.size(), 1);
668 		torrent_handle h = torrents.front();
669 		test1(h);
670 	});
671 
672 	sim::timer t2(sim, lt::seconds(5 + delay)
673 		, [&ses,&test2](boost::system::error_code const&)
674 	{
675 		std::vector<lt::torrent_handle> torrents = ses->get_torrents();
676 		TEST_EQUAL(torrents.size(), 1);
677 		torrent_handle h = torrents.front();
678 		test2(h);
679 	});
680 
681 	// then shut down 10 seconds in
682 	sim::timer t3(sim, lt::seconds(10 + delay)
683 		, [&ses,&zombie](boost::system::error_code const&)
684 	{
685 		zombie = ses->abort();
686 		ses.reset();
687 	});
688 
689 	sim.run();
690 }
691 
692 template <typename Announce, typename Test1, typename Test2>
tracker_test(Announce a,Test1 test1,Test2 test2,char const * url_path="/announce")693 void tracker_test(Announce a, Test1 test1, Test2 test2, char const* url_path = "/announce")
694 {
695 	tracker_test([](lt::add_torrent_params& p, lt::session&) {
696 		p.trackers.push_back("http://tracker.com:8080/announce");
697 		return 5;
698 	},
699 	a, test1, test2, url_path);
700 }
701 
702 template <typename Announce, typename Test>
announce_entry_test(Announce a,Test t,char const * url_path="/announce")703 void announce_entry_test(Announce a, Test t, char const* url_path = "/announce")
704 {
705 	tracker_test(a
706 		, [&t] (torrent_handle h) {
707 			std::vector<announce_entry> tr = h.trackers();
708 
709 			TEST_EQUAL(tr.size(), 1);
710 			announce_entry const& ae = tr[0];
711 			t(ae);
712 		}
713 		, [](torrent_handle){}
714 		, url_path);
715 }
716 
717 // test that we correctly omit announcing an event=stopped to a tracker we never
718 // managed to send an event=start to
TORRENT_TEST(omit_stop_event)719 TORRENT_TEST(omit_stop_event)
720 {
721 	using sim::asio::ip::address_v4;
722 	sim_config network_cfg;
723 	sim::simulation sim{network_cfg};
724 
725 	lt::session_proxy zombie;
726 
727 	asio::io_service ios(sim, { address_v4::from_string("123.0.0.3"), address_v6::from_string("ff::dead:beef")});
728 	lt::settings_pack sett = settings();
729 	std::unique_ptr<lt::session> ses(new lt::session(sett, ios));
730 
731 	print_alerts(*ses);
732 
733 	lt::add_torrent_params p;
734 	p.name = "test-torrent";
735 	p.save_path = ".";
736 	p.info_hash.assign("abababababababababab");
737 	p.trackers.push_back("udp://tracker.com:8080/announce");
738 	ses->async_add_torrent(p);
739 
740 	// run the test 5 seconds in
741 	sim::timer t1(sim, lt::seconds(5)
742 		, [&ses](boost::system::error_code const&)
743 	{
744 		std::vector<lt::torrent_handle> torrents = ses->get_torrents();
745 		TEST_EQUAL(torrents.size(), 1);
746 		torrent_handle h = torrents.front();
747 	});
748 
749 	int stop_announces = 0;
750 
751 	sim::timer t2(sim, lt::seconds(1800)
752 		, [&](boost::system::error_code const&)
753 	{
754 		// make sure we don't announce a stopped event when stopping
755 		print_alerts(*ses, [&](lt::session&, lt::alert const* a) {
756 			if (alert_cast<lt::tracker_announce_alert>(a))
757 			++stop_announces;
758 		});
759 		std::vector<lt::torrent_handle> torrents = ses->get_torrents();
760 		TEST_EQUAL(torrents.size(), 1);
761 		torrent_handle h = torrents.front();
762 		h.set_flags(torrent_flags::paused, torrent_flags::paused | torrent_flags::auto_managed);
763 	});
764 
765 	// then shut down 10 seconds in
766 	sim::timer t3(sim, lt::seconds(1810)
767 		, [&](boost::system::error_code const&)
768 	{
769 		zombie = ses->abort();
770 		ses.reset();
771 	});
772 
773 	sim.run();
774 
775 	TEST_EQUAL(stop_announces, 0);
776 }
777 
TORRENT_TEST(test_error)778 TORRENT_TEST(test_error)
779 {
780 	announce_entry_test(
781 		[](std::string method, std::string req
782 			, std::map<std::string, std::string>&)
783 		{
784 			TEST_EQUAL(method, "GET");
785 
786 			char response[500];
787 			int const size = std::snprintf(response, sizeof(response), "d14:failure reason4:teste");
788 			return sim::send_response(200, "OK", size) + response;
789 		}
790 		, [](announce_entry const& ae)
791 		{
792 			TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
793 			TEST_EQUAL(ae.endpoints.size(), 2);
794 			for (auto const& aep : ae.endpoints)
795 			{
796 				TEST_EQUAL(aep.is_working(), false);
797 				TEST_EQUAL(aep.message, "test");
798 				TEST_EQUAL(aep.last_error, error_code(errors::tracker_failure));
799 				TEST_EQUAL(aep.fails, 1);
800 			}
801 		});
802 }
803 
TORRENT_TEST(test_no_announce_path)804 TORRENT_TEST(test_no_announce_path)
805 {
806 	tracker_test(
807 		[](lt::add_torrent_params& p, lt::session&) {
808 			p.trackers.push_back("http://tracker.com:8080");
809 			return 5;
810 		},
811 		[](std::string method, std::string req, std::map<std::string, std::string>&)
812 		{
813 			TEST_EQUAL(method, "GET");
814 
815 			char response[500];
816 			int const size = std::snprintf(response, sizeof(response), "d5:peers6:aaaaaae");
817 			return sim::send_response(200, "OK", size) + response;
818 		}
819 		, [](torrent_handle h)
820 		{
821 			std::vector<announce_entry> tr = h.trackers();
822 
823 			TEST_EQUAL(tr.size(), 1);
824 			announce_entry const& ae = tr[0];
825 			TEST_EQUAL(ae.url, "http://tracker.com:8080");
826 			TEST_EQUAL(ae.endpoints.size(), 2);
827 			for (auto const& aep : ae.endpoints)
828 			{
829 				TEST_EQUAL(aep.is_working(), true);
830 				TEST_EQUAL(aep.message, "");
831 				TEST_EQUAL(aep.last_error, error_code());
832 				TEST_EQUAL(aep.fails, 0);
833 			}
834 		}
835 		, [](torrent_handle){}
836 		, "/");
837 }
838 
TORRENT_TEST(test_warning)839 TORRENT_TEST(test_warning)
840 {
841 	announce_entry_test(
842 		[](std::string method, std::string req
843 			, std::map<std::string, std::string>&)
844 		{
845 			TEST_EQUAL(method, "GET");
846 
847 			char response[500];
848 			int const size = std::snprintf(response, sizeof(response), "d5:peers6:aaaaaa15:warning message5:test2e");
849 			return sim::send_response(200, "OK", size) + response;
850 		}
851 		, [](announce_entry const& ae)
852 		{
853 			TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
854 			TEST_EQUAL(ae.endpoints.size(), 2);
855 			for (auto const& aep : ae.endpoints)
856 			{
857 				TEST_EQUAL(aep.is_working(), true);
858 				TEST_EQUAL(aep.message, "test2");
859 				TEST_EQUAL(aep.last_error, error_code());
860 				TEST_EQUAL(aep.fails, 0);
861 			}
862 		});
863 }
864 
TORRENT_TEST(test_scrape_data_in_announce)865 TORRENT_TEST(test_scrape_data_in_announce)
866 {
867 	announce_entry_test(
868 		[](std::string method, std::string req
869 			, std::map<std::string, std::string>&)
870 		{
871 			TEST_EQUAL(method, "GET");
872 
873 			char response[500];
874 			int const size = std::snprintf(response, sizeof(response),
875 				"d5:peers6:aaaaaa8:completei1e10:incompletei2e10:downloadedi3e11:downloadersi4ee");
876 			return sim::send_response(200, "OK", size) + response;
877 		}
878 		, [](announce_entry const& ae)
879 		{
880 			TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
881 			TEST_EQUAL(ae.endpoints.size(), 2);
882 			for (auto const& aep : ae.endpoints)
883 			{
884 				TEST_EQUAL(aep.is_working(), true);
885 				TEST_EQUAL(aep.message, "");
886 				TEST_EQUAL(aep.last_error, error_code());
887 				TEST_EQUAL(aep.fails, 0);
888 				TEST_EQUAL(aep.scrape_complete, 1);
889 				TEST_EQUAL(aep.scrape_incomplete, 2);
890 				TEST_EQUAL(aep.scrape_downloaded, 3);
891 			}
892 		});
893 }
894 
TORRENT_TEST(test_scrape)895 TORRENT_TEST(test_scrape)
896 {
897 	tracker_test(
898 		[](std::string method, std::string req
899 			, std::map<std::string, std::string>&)
900 		{
901 			TEST_EQUAL(method, "GET");
902 
903 			char response[500];
904 			int const size = std::snprintf(response, sizeof(response),
905 				"d5:filesd20:ababababababababababd8:completei1e10:downloadedi3e10:incompletei2eeee");
906 			return sim::send_response(200, "OK", size) + response;
907 		}
908 		, [](torrent_handle h)
909 		{
910 			h.scrape_tracker();
911 		}
912 		, [](torrent_handle h)
913 		{
914 			std::vector<announce_entry> tr = h.trackers();
915 
916 			TEST_EQUAL(tr.size(), 1);
917 			announce_entry const& ae = tr[0];
918 			TEST_EQUAL(ae.endpoints.size(), 2);
919 			for (auto const& aep : ae.endpoints)
920 			{
921 				TEST_EQUAL(aep.scrape_incomplete, 2);
922 				TEST_EQUAL(aep.scrape_complete, 1);
923 				TEST_EQUAL(aep.scrape_downloaded, 3);
924 			}
925 		}
926 		, "/scrape");
927 }
928 
TORRENT_TEST(test_http_status)929 TORRENT_TEST(test_http_status)
930 {
931 	announce_entry_test(
932 		[](std::string method, std::string req
933 			, std::map<std::string, std::string>&)
934 		{
935 			TEST_EQUAL(method, "GET");
936 			return sim::send_response(410, "Not A Tracker", 0);
937 		}
938 		, [](announce_entry const& ae)
939 		{
940 			TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
941 			TEST_EQUAL(ae.endpoints.size(), 2);
942 			for (auto const& aep : ae.endpoints)
943 			{
944 				TEST_EQUAL(aep.is_working(), false);
945 				TEST_EQUAL(aep.message, "Not A Tracker");
946 				TEST_EQUAL(aep.last_error, error_code(410, http_category()));
947 				TEST_EQUAL(aep.fails, 1);
948 			}
949 		});
950 }
951 
TORRENT_TEST(test_interval)952 TORRENT_TEST(test_interval)
953 {
954 	announce_entry_test(
955 		[](std::string method, std::string req
956 			, std::map<std::string, std::string>&)
957 		{
958 			TEST_EQUAL(method, "GET");
959 			char response[500];
960 			int const size = std::snprintf(response, sizeof(response)
961 				, "d10:tracker id8:testteste");
962 			return sim::send_response(200, "OK", size) + response;
963 		}
964 		, [](announce_entry const& ae)
965 		{
966 			TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
967 			TEST_EQUAL(ae.endpoints.size(), 2);
968 			for (auto const& aep : ae.endpoints)
969 			{
970 				TEST_EQUAL(aep.is_working(), true);
971 				TEST_EQUAL(aep.message, "");
972 				TEST_EQUAL(aep.last_error, error_code());
973 				TEST_EQUAL(aep.fails, 0);
974 			}
975 
976 			TEST_EQUAL(ae.trackerid, "testtest");
977 		});
978 }
979 
TORRENT_TEST(test_invalid_bencoding)980 TORRENT_TEST(test_invalid_bencoding)
981 {
982 	announce_entry_test(
983 		[](std::string method, std::string req
984 			, std::map<std::string, std::string>&)
985 		{
986 			TEST_EQUAL(method, "GET");
987 			char response[500];
988 			int const size = std::snprintf(response, sizeof(response)
989 				, "d10:tracer idteste");
990 			return sim::send_response(200, "OK", size) + response;
991 		}
992 		, [](announce_entry const& ae)
993 		{
994 			TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
995 			TEST_EQUAL(ae.endpoints.size(), 2);
996 			for (auto const& aep : ae.endpoints)
997 			{
998 				TEST_EQUAL(aep.is_working(), false);
999 				TEST_EQUAL(aep.message, "");
1000 				TEST_EQUAL(aep.last_error, error_code(bdecode_errors::expected_value
1001 					, bdecode_category()));
1002 				TEST_EQUAL(aep.fails, 1);
1003 			}
1004 		});
1005 }
1006 
TORRENT_TEST(try_next)1007 TORRENT_TEST(try_next)
1008 {
1009 // test that we move on to try the next tier if the first one fails
1010 
1011 	bool got_announce = false;
1012 	tracker_test(
1013 		[](lt::add_torrent_params& p, lt::session&)
1014 		{
1015 		// TODO: 3 use tracker_tiers here to put the trackers in different tiers
1016 			p.trackers.push_back("udp://failing-tracker.com/announce");
1017 			p.trackers.push_back("http://failing-tracker.com/announce");
1018 
1019 			// this is the working tracker
1020 			p.trackers.push_back("http://tracker.com:8080/announce");
1021 			return 60;
1022 		},
1023 		[&](std::string method, std::string req
1024 			, std::map<std::string, std::string>&)
1025 		{
1026 			got_announce = true;
1027 			TEST_EQUAL(method, "GET");
1028 
1029 			char response[500];
1030 			// respond with an empty peer list
1031 			int const size = std::snprintf(response, sizeof(response), "d5:peers0:e");
1032 			return sim::send_response(200, "OK", size) + response;
1033 		}
1034 		, [](torrent_handle h) {}
1035 		, [](torrent_handle h)
1036 		{
1037 			torrent_status st = h.status();
1038 			TEST_EQUAL(st.current_tracker, "http://tracker.com:8080/announce");
1039 
1040 			std::vector<announce_entry> tr = h.trackers();
1041 
1042 			TEST_EQUAL(tr.size(), 3);
1043 
1044 			for (int i = 0; i < int(tr.size()); ++i)
1045 			{
1046 				std::printf("tracker \"%s\"\n", tr[i].url.c_str());
1047 				if (tr[i].url == "http://tracker.com:8080/announce")
1048 				{
1049 					for (auto const& aep : tr[i].endpoints)
1050 					{
1051 						TEST_EQUAL(aep.fails, 0);
1052 					}
1053 					TEST_EQUAL(tr[i].verified, true);
1054 				}
1055 				else if (tr[i].url == "http://failing-tracker.com/announce")
1056 				{
1057 					for (auto const& aep : tr[i].endpoints)
1058 					{
1059 						TEST_CHECK(aep.fails >= 1);
1060 						TEST_EQUAL(aep.last_error
1061 							, error_code(boost::asio::error::host_not_found));
1062 					}
1063 					TEST_EQUAL(tr[i].verified, false);
1064 				}
1065 				else if (tr[i].url == "udp://failing-tracker.com/announce")
1066 				{
1067 					TEST_EQUAL(tr[i].verified, false);
1068 					for (auto const& aep : tr[i].endpoints)
1069 					{
1070 						TEST_CHECK(aep.fails >= 1);
1071 						TEST_EQUAL(aep.last_error
1072 							, error_code(boost::asio::error::host_not_found));
1073 					}
1074 				}
1075 				else
1076 				{
1077 					TEST_ERROR(("unexpected tracker URL: " + tr[i].url).c_str());
1078 				}
1079 			}
1080 		});
1081 	TEST_EQUAL(got_announce, true);
1082 }
1083 
TORRENT_TEST(clear_error)1084 TORRENT_TEST(clear_error)
1085 {
1086 	// make sure we clear the error from a previous attempt when succeeding
1087 	// a tracker announce
1088 
1089 	int num_announces = 0;
1090 	std::string last_message;
1091 	tracker_test(
1092 		[](lt::add_torrent_params& p, lt::session& ses)
1093 		{
1094 			settings_pack pack;
1095 			// make sure we just listen on a single listen interface
1096 			pack.set_str(settings_pack::listen_interfaces, "123.0.0.3:0");
1097 			pack.set_int(settings_pack::min_announce_interval, 1);
1098 			pack.set_int(settings_pack::tracker_backoff, 1);
1099 			ses.apply_settings(pack);
1100 			p.trackers.push_back("http://tracker.com:8080/announce");
1101 			return 60;
1102 		},
1103 		[&](std::string method, std::string req, std::map<std::string, std::string>&)
1104 		{
1105 			// don't count the stopped event when shutting down
1106 			if (req.find("&event=stopped&") != std::string::npos)
1107 			{
1108 				return sim::send_response(200, "OK", 2) + "de";
1109 			}
1110 			if (num_announces++ == 0)
1111 			{
1112 				// the first announce fails
1113 				return std::string{};
1114 			}
1115 
1116 			// the second announce succeeds, with an empty peer list
1117 			char response[500];
1118 			int const size = std::snprintf(response, sizeof(response), "d8:intervali1800e5:peers0:e");
1119 			return sim::send_response(200, "OK", size) + response;
1120 		}
1121 		, [](torrent_handle h) {
1122 
1123 		}
1124 		, [&](torrent_handle h)
1125 		{
1126 			std::vector<announce_entry> const tr = h.trackers();
1127 			TEST_EQUAL(tr.size(), 1);
1128 
1129 			std::printf("tracker \"%s\"\n", tr[0].url.c_str());
1130 			TEST_EQUAL(tr[0].url, "http://tracker.com:8080/announce");
1131 			TEST_EQUAL(tr[0].endpoints.size(), 1);
1132 			auto const& aep = tr[0].endpoints[0];
1133 			std::printf("message: \"%s\" error: \"%s\"\n"
1134 				, aep.message.c_str(), aep.last_error.message().c_str());
1135 			TEST_EQUAL(aep.fails, 0);
1136 			TEST_CHECK(!aep.last_error);
1137 			TEST_EQUAL(aep.message, "");
1138 			last_message = aep.message;
1139 		});
1140 	TEST_EQUAL(num_announces, 2);
1141 	TEST_EQUAL(last_message, "");
1142 }
1143 
make_torrent(bool priv)1144 std::shared_ptr<torrent_info> make_torrent(bool priv)
1145 {
1146 	file_storage fs;
1147 	fs.add_file("foobar", 13241);
1148 	lt::create_torrent ct(fs);
1149 
1150 	ct.add_tracker("http://tracker.com:8080/announce");
1151 
1152 	for (piece_index_t i(0); i < piece_index_t(ct.num_pieces()); ++i)
1153 		ct.set_hash(i, sha1_hash(nullptr));
1154 
1155 	ct.set_priv(priv);
1156 
1157 	entry e = ct.generate();
1158 	std::vector<char> buf;
1159 	bencode(std::back_inserter(buf), e);
1160 	return std::make_shared<torrent_info>(buf, from_span);
1161 }
1162 
1163 // make sure we _do_ send our IPv6 address to trackers for private torrents
TORRENT_TEST(tracker_ipv6_argument)1164 TORRENT_TEST(tracker_ipv6_argument)
1165 {
1166 	bool got_announce = false;
1167 	bool got_ipv6 = false;
1168 	bool got_ipv4 = false;
1169 	tracker_test(
1170 		[](lt::add_torrent_params& p, lt::session& ses)
1171 		{
1172 			settings_pack pack;
1173 			pack.set_bool(settings_pack::anonymous_mode, false);
1174 			pack.set_str(settings_pack::listen_interfaces, "123.0.0.3:0,[ffff::1337]:0");
1175 			ses.apply_settings(pack);
1176 			p.ti = make_torrent(true);
1177 			p.info_hash.clear();
1178 			return 60;
1179 		},
1180 		[&](std::string method, std::string req
1181 			, std::map<std::string, std::string>&)
1182 		{
1183 			got_announce = true;
1184 			bool const stop_event = req.find("&event=stopped") != std::string::npos;
1185 			// stop events don't need to advertise the IPv6/IPv4 address
1186 			{
1187 				std::string::size_type const pos = req.find("&ipv6=");
1188 				TEST_CHECK(pos != std::string::npos || stop_event);
1189 				got_ipv6 |= pos != std::string::npos;
1190 				// make sure the IPv6 argument is url encoded
1191 				TEST_EQUAL(req.substr(pos + 6, req.substr(pos + 6).find_first_of('&'))
1192 					, "ffff%3a%3a1337");
1193 			}
1194 
1195 			{
1196 				std::string::size_type const pos = req.find("&ipv4=");
1197 				TEST_CHECK(pos != std::string::npos || stop_event);
1198 				got_ipv4 |= pos != std::string::npos;
1199 				TEST_EQUAL(req.substr(pos + 6, req.substr(pos + 6).find_first_of('&')), "123.0.0.3");
1200 			}
1201 			return sim::send_response(200, "OK", 11) + "d5:peers0:e";
1202 		}
1203 		, [](torrent_handle) {}
1204 		, [](torrent_handle) {});
1205 	TEST_EQUAL(got_announce, true);
1206 	TEST_EQUAL(got_ipv6, true);
1207 }
1208 
TORRENT_TEST(tracker_key_argument)1209 TORRENT_TEST(tracker_key_argument)
1210 {
1211 	std::set<std::string> keys;
1212 	tracker_test(
1213 		[](lt::add_torrent_params& p, lt::session&)
1214 		{
1215 			p.ti = make_torrent(true);
1216 			p.info_hash.clear();
1217 			return 60;
1218 		},
1219 		[&](std::string, std::string req
1220 			, std::map<std::string, std::string>&)
1221 		{
1222 			auto const pos = req.find("&key=");
1223 			TEST_CHECK(pos != std::string::npos);
1224 			keys.insert(req.substr(pos + 5, req.find_first_of('&', pos + 5) - pos - 5));
1225 			return sim::send_response(200, "OK", 11) + "d5:peers0:e";
1226 		}
1227 		, [](torrent_handle h) {}
1228 		, [](torrent_handle h) {});
1229 
1230 	// make sure we got the same key for all listen socket interface
1231 	TEST_EQUAL(keys.size(), 1);
1232 }
1233 
1234 // make sure we do _not_ send our IPv6 address to trackers for non-private
1235 // torrents
TORRENT_TEST(tracker_ipv6_argument_non_private)1236 TORRENT_TEST(tracker_ipv6_argument_non_private)
1237 {
1238 	bool got_announce = false;
1239 	bool got_ipv6 = false;
1240 	tracker_test(
1241 		[](lt::add_torrent_params& p, lt::session& ses)
1242 		{
1243 			settings_pack pack;
1244 			pack.set_bool(settings_pack::anonymous_mode, false);
1245 			ses.apply_settings(pack);
1246 			p.ti = make_torrent(false);
1247 			p.info_hash.clear();
1248 			return 60;
1249 		},
1250 		[&](std::string method, std::string req
1251 			, std::map<std::string, std::string>&)
1252 		{
1253 			got_announce = true;
1254 			std::string::size_type pos = req.find("&ipv6=");
1255 			TEST_CHECK(pos == std::string::npos);
1256 			got_ipv6 |= pos != std::string::npos;
1257 			return sim::send_response(200, "OK", 11) + "d5:peers0:e";
1258 		}
1259 		, [](torrent_handle) {}
1260 		, [](torrent_handle) {});
1261 	TEST_EQUAL(got_announce, true);
1262 	TEST_EQUAL(got_ipv6, false);
1263 }
1264 
TORRENT_TEST(tracker_ipv6_argument_privacy_mode)1265 TORRENT_TEST(tracker_ipv6_argument_privacy_mode)
1266 {
1267 	bool got_announce = false;
1268 	bool got_ipv6 = false;
1269 	tracker_test(
1270 		[](lt::add_torrent_params& p, lt::session& ses)
1271 		{
1272 			settings_pack pack;
1273 			pack.set_bool(settings_pack::anonymous_mode, true);
1274 			ses.apply_settings(pack);
1275 			p.ti = make_torrent(true);
1276 			p.info_hash.clear();
1277 			return 60;
1278 		},
1279 		[&](std::string method, std::string req
1280 			, std::map<std::string, std::string>&)
1281 		{
1282 			got_announce = true;
1283 			std::string::size_type pos = req.find("&ipv6=");
1284 			TEST_CHECK(pos == std::string::npos);
1285 			got_ipv6 |= pos != std::string::npos;
1286 			return sim::send_response(200, "OK", 11) + "d5:peers0:e";
1287 		}
1288 		, [](torrent_handle) {}
1289 		, [](torrent_handle) {});
1290 	TEST_EQUAL(got_announce, true);
1291 	TEST_EQUAL(got_ipv6, false);
1292 }
1293 
TORRENT_TEST(tracker_user_agent_privacy_mode_public_torrent)1294 TORRENT_TEST(tracker_user_agent_privacy_mode_public_torrent)
1295 {
1296 	bool got_announce = false;
1297 	tracker_test(
1298 		[](lt::add_torrent_params& p, lt::session& ses)
1299 		{
1300 			settings_pack pack;
1301 			pack.set_bool(settings_pack::anonymous_mode, true);
1302 			pack.set_str(settings_pack::user_agent, "test_agent/1.2.3");
1303 			ses.apply_settings(pack);
1304 			p.ti = make_torrent(false);
1305 			p.info_hash.clear();
1306 			return 60;
1307 		},
1308 		[&](std::string method, std::string req
1309 			, std::map<std::string, std::string>& headers)
1310 		{
1311 			got_announce = true;
1312 
1313 			// in anonymous mode we should not send a user agent
1314 			TEST_CHECK(headers["user-agent"] == "");
1315 			return sim::send_response(200, "OK", 11) + "d5:peers0:e";
1316 		}
1317 		, [](torrent_handle h) {}
1318 		, [](torrent_handle h) {});
1319 	TEST_EQUAL(got_announce, true);
1320 }
1321 
TORRENT_TEST(tracker_user_agent_privacy_mode_private_torrent)1322 TORRENT_TEST(tracker_user_agent_privacy_mode_private_torrent)
1323 {
1324 	bool got_announce = false;
1325 	tracker_test(
1326 		[](lt::add_torrent_params& p, lt::session& ses)
1327 		{
1328 			settings_pack pack;
1329 			pack.set_bool(settings_pack::anonymous_mode, true);
1330 			pack.set_str(settings_pack::user_agent, "test_agent/1.2.3");
1331 			ses.apply_settings(pack);
1332 			p.ti = make_torrent(true);
1333 			p.info_hash.clear();
1334 			return 60;
1335 		},
1336 		[&](std::string method, std::string req
1337 			, std::map<std::string, std::string>& headers)
1338 		{
1339 			got_announce = true;
1340 
1341 			// in anonymous mode we should still send the user agent for private
1342 			// torrents (since private trackers sometimes require it)
1343 			TEST_CHECK(headers["user-agent"] == "test_agent/1.2.3");
1344 			return sim::send_response(200, "OK", 11) + "d5:peers0:e";
1345 		}
1346 		, [](torrent_handle h) {}
1347 		, [](torrent_handle h) {});
1348 	TEST_EQUAL(got_announce, true);
1349 }
1350 
test_ssrf(char const * announce_path,bool const feature_on,char const * tracker_url)1351 bool test_ssrf(char const* announce_path, bool const feature_on
1352 	, char const* tracker_url)
1353 {
1354 	bool got_announce = false;
1355 	tracker_test(
1356 		[&](lt::add_torrent_params& p, lt::session& ses)
1357 		{
1358 			settings_pack pack;
1359 			pack.set_bool(settings_pack::ssrf_mitigation, feature_on);
1360 			ses.apply_settings(pack);
1361 			p.trackers.emplace_back(tracker_url);
1362 			return 60;
1363 		},
1364 		[&](std::string method, std::string req
1365 			, std::map<std::string, std::string>& headers)
1366 		{
1367 			got_announce = true;
1368 			return sim::send_response(200, "OK", 11) + "d5:peers0:e";
1369 		}
1370 		, [](torrent_handle h) {}
1371 		, [](torrent_handle h) {}
1372 		, announce_path);
1373 	return got_announce;
1374 }
1375 
TORRENT_TEST(ssrf_localhost)1376 TORRENT_TEST(ssrf_localhost)
1377 {
1378 	TEST_CHECK(test_ssrf("/announce", true, "http://localhost:8080/announce"));
1379 	TEST_CHECK(!test_ssrf("/unusual-announce-path", true, "http://localhost:8080/unusual-announce-path"));
1380 	TEST_CHECK(test_ssrf("/unusual-announce-path", false, "http://localhost:8080/unusual-announce-path"));
1381 
1382 	TEST_CHECK(!test_ssrf("/short", true, "http://localhost:8080/short"));
1383 	TEST_CHECK(test_ssrf("/short", false, "http://localhost:8080/short"));
1384 }
1385 
TORRENT_TEST(ssrf_IPv4)1386 TORRENT_TEST(ssrf_IPv4)
1387 {
1388 	TEST_CHECK(test_ssrf("/announce", true, "http://127.0.0.1:8080/announce"));
1389 	TEST_CHECK(!test_ssrf("/unusual-announce-path", true, "http://127.0.0.1:8080/unusual-announce-path"));
1390 	TEST_CHECK(test_ssrf("/unusual-announce-path", false, "http://127.0.0.1:8080/unusual-announce-path"));
1391 }
1392 
TORRENT_TEST(ssrf_IPv6)1393 TORRENT_TEST(ssrf_IPv6)
1394 {
1395 	TEST_CHECK(test_ssrf("/announce", true, "http://[::1]:8080/announce"));
1396 	TEST_CHECK(!test_ssrf("/unusual-announce-path", true, "http://[::1]:8080/unusual-announce-path"));
1397 	TEST_CHECK(test_ssrf("/unusual-announce-path", false, "http://[::1]:8080/unusual-announce-path"));
1398 }
1399 
TORRENT_TEST(ssrf_query_string)1400 TORRENT_TEST(ssrf_query_string)
1401 {
1402 	// tracker URLs that come pre-baked with query string arguments will be
1403 	// rejected when SSRF-mitigation is enabled
1404 	TEST_CHECK(!test_ssrf("/announce", true, "http://tracker.com:8080/announce?info_hash=abc"));
1405 	TEST_CHECK(!test_ssrf("/announce", true, "http://tracker.com:8080/announce?iNfo_HaSh=abc"));
1406 	TEST_CHECK(!test_ssrf("/announce", true, "http://tracker.com:8080/announce?event=abc"));
1407 	TEST_CHECK(!test_ssrf("/announce", true, "http://tracker.com:8080/announce?EvEnT=abc"));
1408 
1409 	TEST_CHECK(test_ssrf("/announce", false, "http://tracker.com:8080/announce?info_hash=abc"));
1410 	TEST_CHECK(test_ssrf("/announce", false, "http://tracker.com:8080/announce?iNfo_HaSh=abc"));
1411 	TEST_CHECK(test_ssrf("/announce", false, "http://tracker.com:8080/announce?event=abc"));
1412 }
1413 
test_idna(char const * tracker_url,char const * redirect,bool const feature_on)1414 bool test_idna(char const* tracker_url, char const* redirect
1415 	, bool const feature_on)
1416 {
1417 	bool got_announce = false;
1418 	tracker_test(
1419 		[&](lt::add_torrent_params& p, lt::session& ses)
1420 		{
1421 			settings_pack pack;
1422 			pack.set_bool(settings_pack::allow_idna, feature_on);
1423 			ses.apply_settings(pack);
1424 			p.trackers.emplace_back(tracker_url);
1425 			return 60;
1426 		},
1427 		[&](std::string method, std::string req
1428 			, std::map<std::string, std::string>& headers)
1429 		{
1430 			got_announce = true;
1431 			return sim::send_response(200, "OK", 11) + "d5:peers0:e";
1432 		}
1433 		, [](torrent_handle h) {}
1434 		, [](torrent_handle h) {}
1435 		, "/announce"
1436 		, redirect ? redirect : ""
1437 		);
1438 	return got_announce;
1439 }
1440 
TORRENT_TEST(tracker_idna)1441 TORRENT_TEST(tracker_idna)
1442 {
1443 	TEST_EQUAL(test_idna("http://tracker.com:8080/announce", nullptr, true), true);
1444 	TEST_EQUAL(test_idna("http://tracker.com:8080/announce", nullptr, false), true);
1445 
1446 	TEST_EQUAL(test_idna("http://xn--tracker-.com:8080/announce", nullptr, true), true);
1447 	TEST_EQUAL(test_idna("http://xn--tracker-.com:8080/announce", nullptr, false), false);
1448 }
1449 
TORRENT_TEST(tracker_idna_redirect)1450 TORRENT_TEST(tracker_idna_redirect)
1451 {
1452 	TEST_EQUAL(test_idna("http://redirector.com:8080/announce", "http://xn--tracker-.com:8080/announce", true), true);
1453 	TEST_EQUAL(test_idna("http://redirector.com:8080/announce", "http://xn--tracker-.com:8080/announce", false), false);
1454 }
1455 
1456 // This test sets up two peers, one seed an one downloader. The downloader has
1457 // two trackers, both in tier 0. The behavior we expect is that it picks one of
1458 // the trackers at random and announces to it. Since both trackers are working,
1459 // it should not announce to the tracker it did not initially pick.
1460 
1461 struct tracker_ent
1462 {
1463 	std::string url;
1464 	int tier;
1465 };
1466 
1467 template <typename TestFun>
test_tracker_tiers(lt::settings_pack pack,std::vector<address> local_addresses,std::vector<tracker_ent> trackers,TestFun test)1468 void test_tracker_tiers(lt::settings_pack pack
1469 	, std::vector<address> local_addresses
1470 	, std::vector<tracker_ent> trackers
1471 	, TestFun test)
1472 {
1473 	using namespace libtorrent;
1474 
1475 	pack.set_int(settings_pack::alert_mask, alert_category::error
1476 		| alert_category::status
1477 		| alert_category::torrent_log);
1478 
1479 	// setup the simulation
1480 	struct sim_config : sim::default_config
1481 	{
1482 		chrono::high_resolution_clock::duration hostname_lookup(
1483 			asio::ip::address const& requestor
1484 			, std::string hostname
1485 			, std::vector<asio::ip::address>& result
1486 			, boost::system::error_code& ec)
1487 		{
1488 			if (hostname == "ipv6-only-tracker.com")
1489 			{
1490 				result.push_back(addr("f8e0::1"));
1491 			}
1492 			else if (hostname == "ipv4-only-tracker.com")
1493 			{
1494 				result.push_back(addr("3.0.0.1"));
1495 			}
1496 			else if (hostname == "dual-tracker.com")
1497 			{
1498 				result.push_back(addr("f8e0::2"));
1499 				result.push_back(addr("3.0.0.2"));
1500 			}
1501 			else return default_config::hostname_lookup(requestor, hostname, result, ec);
1502 
1503 			return lt::duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
1504 		}
1505 	};
1506 
1507 	sim_config network_cfg;
1508 	sim::simulation sim{network_cfg};
1509 	sim::asio::io_service ios0 { sim, local_addresses};
1510 
1511 	sim::asio::io_service tracker1(sim, addr("3.0.0.1"));
1512 	sim::asio::io_service tracker2(sim, addr("3.0.0.2"));
1513 	sim::asio::io_service tracker3(sim, addr("3.0.0.3"));
1514 	sim::asio::io_service tracker4(sim, addr("3.0.0.4"));
1515 	sim::asio::io_service tracker5(sim, addr("f8e0::1"));
1516 	sim::asio::io_service tracker6(sim, addr("f8e0::2"));
1517 	sim::http_server http1(tracker1, 8080);
1518 	sim::http_server http2(tracker2, 8080);
1519 	sim::http_server http3(tracker3, 8080);
1520 	sim::http_server http4(tracker4, 8080);
1521 	sim::http_server http5(tracker5, 8080);
1522 	sim::http_server http6(tracker6, 8080);
1523 
1524 	int received_announce[6] = {0, 0, 0, 0, 0, 0};
1525 
1526 	auto const return_no_peers = [&](std::string method, std::string req
1527 		, std::map<std::string, std::string>&, int const tracker_index)
1528 	{
1529 		++received_announce[tracker_index];
1530 		std::string const ret = "d8:intervali60e5:peers0:e";
1531 		return sim::send_response(200, "OK", static_cast<int>(ret.size())) + ret;
1532 	};
1533 
1534 	using namespace std::placeholders;
1535 	http1.register_handler("/announce", std::bind(return_no_peers, _1, _2, _3, 0));
1536 	http2.register_handler("/announce", std::bind(return_no_peers, _1, _2, _3, 1));
1537 	http3.register_handler("/announce", std::bind(return_no_peers, _1, _2, _3, 2));
1538 	http4.register_handler("/announce", std::bind(return_no_peers, _1, _2, _3, 3));
1539 	http5.register_handler("/announce", std::bind(return_no_peers, _1, _2, _3, 4));
1540 	http6.register_handler("/announce", std::bind(return_no_peers, _1, _2, _3, 5));
1541 
1542 	lt::session_proxy zombie;
1543 
1544 	// create session
1545 	pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:6881,[::]:6881");
1546 	auto ses = std::make_shared<lt::session>(pack, ios0);
1547 
1548 	// only monitor alerts for session 0 (the downloader)
1549 	print_alerts(*ses);
1550 
1551 	// the first peer is a downloader, the second peer is a seed
1552 	lt::add_torrent_params params = ::create_torrent(1);
1553 	params.flags &= ~lt::torrent_flags::auto_managed;
1554 	params.flags &= ~lt::torrent_flags::paused;
1555 
1556 	for (auto const& t : trackers)
1557 		params.ti->add_tracker("http://" + t.url + ":8080/announce", t.tier);
1558 
1559 	params.save_path = save_path(0);
1560 	ses->async_add_torrent(params);
1561 
1562 
1563 	sim::timer t(sim, lt::seconds(30), [&](boost::system::error_code const&)
1564 	{
1565 		test(received_announce);
1566 
1567 		zombie = ses->abort();
1568 		ses.reset();
1569 	});
1570 
1571 	sim.run();
1572 }
1573 
one_of(int a,int b)1574 bool one_of(int a, int b)
1575 {
1576 	return (a == 1 && b == 0) || (a == 0 && b == 1);
1577 }
1578 
TORRENT_TEST(tracker_tiers_multi_homed)1579 TORRENT_TEST(tracker_tiers_multi_homed)
1580 {
1581 	settings_pack pack = settings();
1582 	pack.set_bool(settings_pack::announce_to_all_tiers, false);
1583 	pack.set_bool(settings_pack::announce_to_all_trackers, false);
1584 	test_tracker_tiers(pack, { addr("50.0.0.1"), addr("f8e0::10") }
1585 		, { {"3.0.0.1", 0}, {"3.0.0.2", 0}, {"3.0.0.3", 1}, {"3.0.0.4", 1}}
1586 		, [](int (&a)[6]) {
1587 		TEST_CHECK(one_of(a[0], a[1]));
1588 		TEST_EQUAL(a[2], 0);
1589 		TEST_EQUAL(a[3], 0);
1590 		TEST_EQUAL(a[4], 0);
1591 		TEST_EQUAL(a[5], 0);
1592 	});
1593 }
1594 
TORRENT_TEST(tracker_tiers_all_trackers_multi_homed)1595 TORRENT_TEST(tracker_tiers_all_trackers_multi_homed)
1596 {
1597 	settings_pack pack = settings();
1598 	pack.set_bool(settings_pack::announce_to_all_tiers, false);
1599 	pack.set_bool(settings_pack::announce_to_all_trackers, true);
1600 	test_tracker_tiers(pack, { addr("50.0.0.1"), addr("f8e0::10") }
1601 		, { {"3.0.0.1", 0}, {"3.0.0.2", 0}, {"3.0.0.3", 1}, {"3.0.0.4", 1}}
1602 		, [](int (&a)[6]) {
1603 		TEST_EQUAL(a[0], 1);
1604 		TEST_EQUAL(a[1], 1);
1605 		TEST_EQUAL(a[2], 0);
1606 		TEST_EQUAL(a[3], 0);
1607 		TEST_EQUAL(a[4], 0);
1608 		TEST_EQUAL(a[5], 0);
1609 	});
1610 }
1611 
TORRENT_TEST(tracker_tiers_all_tiers_multi_homed)1612 TORRENT_TEST(tracker_tiers_all_tiers_multi_homed)
1613 {
1614 	settings_pack pack = settings();
1615 	pack.set_bool(settings_pack::announce_to_all_tiers, true);
1616 	pack.set_bool(settings_pack::announce_to_all_trackers, false);
1617 	test_tracker_tiers(pack, { addr("50.0.0.1"), addr("f8e0::10") }
1618 		, { {"3.0.0.1", 0}, {"3.0.0.2", 0}, {"3.0.0.3", 1}, {"3.0.0.4", 1}}
1619 		, [](int (&a)[6]) {
1620 		TEST_CHECK(one_of(a[0], a[1]));
1621 		TEST_CHECK(one_of(a[2], a[3]));
1622 		TEST_EQUAL(a[4], 0);
1623 		TEST_EQUAL(a[5], 0);
1624 	});
1625 }
TORRENT_TEST(tracker_tiers_all_trackers_and_tiers_multi_homed)1626 TORRENT_TEST(tracker_tiers_all_trackers_and_tiers_multi_homed)
1627 {
1628 	settings_pack pack = settings();
1629 	pack.set_bool(settings_pack::announce_to_all_tiers, true);
1630 	pack.set_bool(settings_pack::announce_to_all_trackers, true);
1631 	test_tracker_tiers(pack, { addr("50.0.0.1"), addr("f8e0::10") }
1632 		, { {"3.0.0.1", 0}, {"3.0.0.2", 0}, {"3.0.0.3", 1}, {"3.0.0.4", 1}}
1633 		, [](int (&a)[6]) {
1634 		TEST_EQUAL(a[0], 1);
1635 		TEST_EQUAL(a[1], 1);
1636 		TEST_EQUAL(a[2], 1);
1637 		TEST_EQUAL(a[3], 1);
1638 		TEST_EQUAL(a[4], 0);
1639 		TEST_EQUAL(a[5], 0);
1640 	});
1641 }
1642 
TORRENT_TEST(tracker_tiers)1643 TORRENT_TEST(tracker_tiers)
1644 {
1645 	settings_pack pack = settings();
1646 	pack.set_bool(settings_pack::announce_to_all_tiers, false);
1647 	pack.set_bool(settings_pack::announce_to_all_trackers, false);
1648 	test_tracker_tiers(pack, { addr("50.0.0.1") }
1649 		, { {"3.0.0.1", 0}, {"3.0.0.2", 0}, {"3.0.0.3", 1}, {"3.0.0.4", 1}}
1650 		, [](int (&a)[6]) {
1651 		TEST_CHECK(one_of(a[0], a[1]));
1652 		TEST_EQUAL(a[2], 0);
1653 		TEST_EQUAL(a[3], 0);
1654 		TEST_EQUAL(a[4], 0);
1655 		TEST_EQUAL(a[5], 0);
1656 	});
1657 }
1658 
TORRENT_TEST(tracker_tiers_all_trackers)1659 TORRENT_TEST(tracker_tiers_all_trackers)
1660 {
1661 	settings_pack pack = settings();
1662 	pack.set_bool(settings_pack::announce_to_all_tiers, false);
1663 	pack.set_bool(settings_pack::announce_to_all_trackers, true);
1664 	test_tracker_tiers(pack, { addr("50.0.0.1") }
1665 		, { {"3.0.0.1", 0}, {"3.0.0.2", 0}, {"3.0.0.3", 1}, {"3.0.0.4", 1}}
1666 		, [](int (&a)[6]) {
1667 		TEST_EQUAL(a[0], 1);
1668 		TEST_EQUAL(a[1], 1);
1669 		TEST_EQUAL(a[2], 0);
1670 		TEST_EQUAL(a[3], 0);
1671 		TEST_EQUAL(a[4], 0);
1672 		TEST_EQUAL(a[5], 0);
1673 	});
1674 }
1675 
TORRENT_TEST(tracker_tiers_all_tiers)1676 TORRENT_TEST(tracker_tiers_all_tiers)
1677 {
1678 	settings_pack pack = settings();
1679 	pack.set_bool(settings_pack::announce_to_all_tiers, true);
1680 	pack.set_bool(settings_pack::announce_to_all_trackers, false);
1681 	test_tracker_tiers(pack, { addr("50.0.0.1") }
1682 		, { {"3.0.0.1", 0}, {"3.0.0.2", 0}, {"3.0.0.3", 1}, {"3.0.0.4", 1}}
1683 		, [](int (&a)[6]) {
1684 		TEST_CHECK(one_of(a[0], a[1]));
1685 		TEST_CHECK(one_of(a[2], a[3]));
1686 		TEST_EQUAL(a[4], 0);
1687 		TEST_EQUAL(a[5], 0);
1688 	});
1689 }
1690 
TORRENT_TEST(tracker_tiers_all_trackers_and_tiers)1691 TORRENT_TEST(tracker_tiers_all_trackers_and_tiers)
1692 {
1693 	settings_pack pack = settings();
1694 	pack.set_bool(settings_pack::announce_to_all_tiers, true);
1695 	pack.set_bool(settings_pack::announce_to_all_trackers, true);
1696 	test_tracker_tiers(pack, { addr("50.0.0.1") }
1697 		, { {"3.0.0.1", 0}, {"3.0.0.2", 0}, {"3.0.0.3", 1}, {"3.0.0.4", 1}}
1698 		, [](int (&a)[6]) {
1699 		TEST_EQUAL(a[0], 1);
1700 		TEST_EQUAL(a[1], 1);
1701 		TEST_EQUAL(a[2], 1);
1702 		TEST_EQUAL(a[3], 1);
1703 		TEST_EQUAL(a[4], 0);
1704 		TEST_EQUAL(a[5], 0);
1705 	});
1706 }
1707 
1708 // in this case, we only have an IPv4 address, and the first tracker resolves
1709 // only to an IPv6 address. Make sure we move on to the next one in the tier
TORRENT_TEST(tracker_tiers_unreachable_tracker)1710 TORRENT_TEST(tracker_tiers_unreachable_tracker)
1711 {
1712 	settings_pack pack = settings();
1713 	pack.set_bool(settings_pack::announce_to_all_tiers, false);
1714 	pack.set_bool(settings_pack::announce_to_all_trackers, false);
1715 	test_tracker_tiers(pack, { addr("50.0.0.1") }
1716 		, { {"f8e0::1", 0}, {"3.0.0.2", 0}, {"3.0.0.3", 1}, {"3.0.0.4", 1}}
1717 		, [](int (&a)[6]) {
1718 		TEST_EQUAL(a[0], 0);
1719 		TEST_EQUAL(a[1], 1);
1720 		TEST_EQUAL(a[2], 0);
1721 		TEST_EQUAL(a[3], 0);
1722 		TEST_EQUAL(a[4], 0);
1723 		TEST_EQUAL(a[5], 0);
1724 	});
1725 }
1726 
1727 // in this test, we have both v6 and v4 connectivity, and we have two trackers
1728 // One is v6 only and one is dual. Since the first tracker was announced to
1729 // using IPv6, the second tracker will *only* be used for IPv4, and not to
1730 // announce IPv6 to again.
TORRENT_TEST(tracker_tiers_v4_and_v6_same_tier)1731 TORRENT_TEST(tracker_tiers_v4_and_v6_same_tier)
1732 {
1733 	settings_pack pack = settings();
1734 	pack.set_bool(settings_pack::announce_to_all_tiers, false);
1735 	pack.set_bool(settings_pack::announce_to_all_trackers, false);
1736 	test_tracker_tiers(pack, { addr("50.0.0.1"), addr("f8e0::10") }
1737 		, { {"ipv6-only-tracker.com", 0}, {"dual-tracker.com", 0}}
1738 		, [](int (&a)[6]) {
1739 		TEST_EQUAL(a[0], 0);
1740 		TEST_EQUAL(a[1], 1);
1741 		TEST_EQUAL(a[2], 0);
1742 		TEST_EQUAL(a[3], 0);
1743 		TEST_EQUAL(a[4], 1);
1744 		TEST_EQUAL(a[5], 0);
1745 	});
1746 }
1747 
TORRENT_TEST(tracker_tiers_v4_and_v6_different_tiers)1748 TORRENT_TEST(tracker_tiers_v4_and_v6_different_tiers)
1749 {
1750 	settings_pack pack = settings();
1751 	pack.set_bool(settings_pack::announce_to_all_tiers, false);
1752 	pack.set_bool(settings_pack::announce_to_all_trackers, false);
1753 	test_tracker_tiers(pack, { addr("50.0.0.1"), addr("f8e0::10") }
1754 		, { {"ipv6-only-tracker.com", 0}, {"dual-tracker.com", 1}}
1755 		, [](int (&a)[6]) {
1756 		TEST_EQUAL(a[0], 0);
1757 		TEST_EQUAL(a[1], 1);
1758 		TEST_EQUAL(a[2], 0);
1759 		TEST_EQUAL(a[3], 0);
1760 		TEST_EQUAL(a[4], 1);
1761 		TEST_EQUAL(a[5], 0);
1762 	});
1763 }
1764 
1765 // in the same scenario as above, if we announce to all trackers, we expect to
1766 // continue to visit all trackers in the tier, and announce to that additional
1767 // IPv6 address as well
TORRENT_TEST(tracker_tiers_v4_and_v6_all_trackers)1768 TORRENT_TEST(tracker_tiers_v4_and_v6_all_trackers)
1769 {
1770 	settings_pack pack = settings();
1771 	pack.set_bool(settings_pack::announce_to_all_tiers, false);
1772 	pack.set_bool(settings_pack::announce_to_all_trackers, true);
1773 	test_tracker_tiers(pack, { addr("50.0.0.1"), addr("f8e0::10") }
1774 		, { {"ipv6-only-tracker.com", 0}, {"dual-tracker.com", 0}}
1775 		, [](int (&a)[6]) {
1776 		TEST_EQUAL(a[0], 0);
1777 		TEST_EQUAL(a[1], 1);
1778 		TEST_EQUAL(a[2], 0);
1779 		TEST_EQUAL(a[3], 0);
1780 		TEST_EQUAL(a[4], 1);
1781 		TEST_EQUAL(a[5], 1);
1782 	});
1783 }
1784 
TORRENT_TEST(tracker_tiers_v4_and_v6_different_tiers_all_trackers)1785 TORRENT_TEST(tracker_tiers_v4_and_v6_different_tiers_all_trackers)
1786 {
1787 	settings_pack pack = settings();
1788 	pack.set_bool(settings_pack::announce_to_all_tiers, false);
1789 	pack.set_bool(settings_pack::announce_to_all_trackers, true);
1790 	test_tracker_tiers(pack, { addr("50.0.0.1"), addr("f8e0::10") }
1791 		, { {"ipv6-only-tracker.com", 0}, {"dual-tracker.com", 1}}
1792 		, [](int (&a)[6]) {
1793 		TEST_EQUAL(a[0], 0);
1794 		TEST_EQUAL(a[1], 1);
1795 		TEST_EQUAL(a[2], 0);
1796 		TEST_EQUAL(a[3], 0);
1797 		TEST_EQUAL(a[4], 1);
1798 		TEST_EQUAL(a[5], 0);
1799 	});
1800 }
1801 
TORRENT_TEST(tracker_tiers_v4_and_v6_different_tiers_all_tiers)1802 TORRENT_TEST(tracker_tiers_v4_and_v6_different_tiers_all_tiers)
1803 {
1804 	settings_pack pack = settings();
1805 	pack.set_bool(settings_pack::announce_to_all_tiers, true);
1806 	pack.set_bool(settings_pack::announce_to_all_trackers, false);
1807 	test_tracker_tiers(pack, { addr("50.0.0.1"), addr("f8e0::10") }
1808 		, { {"ipv6-only-tracker.com", 0}, {"dual-tracker.com", 1}}
1809 		, [](int (&a)[6]) {
1810 		TEST_EQUAL(a[0], 0);
1811 		TEST_EQUAL(a[1], 1);
1812 		TEST_EQUAL(a[2], 0);
1813 		TEST_EQUAL(a[3], 0);
1814 		TEST_EQUAL(a[4], 1);
1815 		TEST_EQUAL(a[5], 1);
1816 	});
1817 }
1818 
1819 // TODO: test external IP
1820 // TODO: test with different queuing settings
1821 // TODO: test when a torrent transitions from downloading to finished and
1822 // finished to seeding
1823 // TODO: test that left, downloaded and uploaded are reported correctly
1824 
1825 // TODO: test scrape
1826 
1827