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