1 /*
2 * This file Copyright (C) 2010-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 */
8
9 #include <limits.h> /* INT_MAX */
10 #include <stdio.h>
11 #include <stdlib.h> /* qsort() */
12 #include <string.h> /* strcmp(), memcpy(), strncmp() */
13
14 #include <event2/buffer.h>
15 #include <event2/event.h> /* evtimer */
16
17 #define __LIBTRANSMISSION_ANNOUNCER_MODULE__
18
19 #include "transmission.h"
20 #include "announcer.h"
21 #include "announcer-common.h"
22 #include "crypto-utils.h" /* tr_rand_int(), tr_rand_int_weak() */
23 #include "log.h"
24 #include "peer-mgr.h" /* tr_peerMgrCompactToPex() */
25 #include "ptrarray.h"
26 #include "session.h"
27 #include "torrent.h"
28 #include "tr-assert.h"
29 #include "utils.h"
30
31 struct tr_tier;
32
33 static void tier_build_log_name(struct tr_tier const* tier, char* buf, size_t buflen);
34
35 #define dbgmsg(tier, ...) \
36 do \
37 { \
38 if (tr_logGetDeepEnabled()) \
39 { \
40 char name[128]; \
41 tier_build_log_name(tier, name, TR_N_ELEMENTS(name)); \
42 tr_logAddDeep(__FILE__, __LINE__, name, __VA_ARGS__); \
43 } \
44 } \
45 while (0)
46
47 enum
48 {
49 /* unless the tracker says otherwise, rescrape this frequently */
50 DEFAULT_SCRAPE_INTERVAL_SEC = (60 * 30),
51 /* unless the tracker says otherwise, this is the announce interval */
52 DEFAULT_ANNOUNCE_INTERVAL_SEC = (60 * 10),
53 /* unless the tracker says otherwise, this is the announce min_interval */
54 DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = (60 * 2),
55 /* how many web tasks we allow at one time */
56 MAX_CONCURRENT_TASKS = 48,
57 /* the value of the 'numwant' argument passed in tracker requests. */
58 NUMWANT = 80,
59 /* */
60 UPKEEP_INTERVAL_SECS = 1,
61 /* this is how often to call the UDP tracker upkeep */
62 TAU_UPKEEP_INTERVAL_SECS = 5,
63
64 /* how many infohashes to remove when we get a scrape-too-long error */
65 TR_MULTISCRAPE_STEP = 5
66 };
67
68 /***
69 ****
70 ***/
71
tr_announce_event_get_string(tr_announce_event e)72 char const* tr_announce_event_get_string(tr_announce_event e)
73 {
74 switch (e)
75 {
76 case TR_ANNOUNCE_EVENT_COMPLETED:
77 return "completed";
78
79 case TR_ANNOUNCE_EVENT_STARTED:
80 return "started";
81
82 case TR_ANNOUNCE_EVENT_STOPPED:
83 return "stopped";
84
85 default:
86 return "";
87 }
88 }
89
90 /***
91 ****
92 ***/
93
compareTransfer(uint64_t a_uploaded,uint64_t a_downloaded,uint64_t b_uploaded,uint64_t b_downloaded)94 static int compareTransfer(uint64_t a_uploaded, uint64_t a_downloaded, uint64_t b_uploaded, uint64_t b_downloaded)
95 {
96 /* higher upload count goes first */
97 if (a_uploaded != b_uploaded)
98 {
99 return a_uploaded > b_uploaded ? -1 : 1;
100 }
101
102 /* then higher download count goes first */
103 if (a_downloaded != b_downloaded)
104 {
105 return a_downloaded > b_downloaded ? -1 : 1;
106 }
107
108 return 0;
109 }
110
111 /**
112 * Comparison function for tr_announce_requests.
113 *
114 * The primary key (amount of data transferred) is used to prioritize
115 * tracker announcements of active torrents. The remaining keys are
116 * used to satisfy the uniqueness requirement of a sorted tr_ptrArray.
117 */
compareStops(void const * va,void const * vb)118 static int compareStops(void const* va, void const* vb)
119 {
120 int i;
121 tr_announce_request const* a = va;
122 tr_announce_request const* b = vb;
123
124 /* primary key: volume of data transferred. */
125 if ((i = compareTransfer(a->up, a->down, b->up, b->down)) != 0)
126 {
127 return i;
128 }
129
130 /* secondary key: the torrent's info_hash */
131 if ((i = memcmp(a->info_hash, b->info_hash, SHA_DIGEST_LENGTH)) != 0)
132 {
133 return i;
134 }
135
136 /* tertiary key: the tracker's announce url */
137 return tr_strcmp0(a->url, b->url);
138 }
139
140 /***
141 ****
142 ***/
143
144 struct tr_scrape_info
145 {
146 char* url;
147
148 int multiscrape_max;
149 };
150
scrapeInfoFree(void * va)151 static void scrapeInfoFree(void* va)
152 {
153 struct tr_scrape_info* a = va;
154
155 tr_free(a->url);
156 tr_free(a);
157 }
158
compareScrapeInfo(void const * va,void const * vb)159 static int compareScrapeInfo(void const* va, void const* vb)
160 {
161 struct tr_scrape_info const* a = va;
162 struct tr_scrape_info const* b = vb;
163 return tr_strcmp0(a->url, b->url);
164 }
165
166 /**
167 * "global" (per-tr_session) fields
168 */
169 typedef struct tr_announcer
170 {
171 tr_ptrArray stops; /* tr_announce_request */
172 tr_ptrArray scrape_info; /* struct tr_scrape_info */
173
174 tr_session* session;
175 struct event* upkeepTimer;
176 int slotsAvailable;
177 int key;
178 time_t tauUpkeepAt;
179 }
180 tr_announcer;
181
tr_announcerGetScrapeInfo(struct tr_announcer * announcer,char const * url)182 static struct tr_scrape_info* tr_announcerGetScrapeInfo(struct tr_announcer* announcer, char const* url)
183 {
184 struct tr_scrape_info* info = NULL;
185
186 if (!tr_str_is_empty(url))
187 {
188 bool found;
189 struct tr_scrape_info const key = { .url = (char*)url };
190 int const pos = tr_ptrArrayLowerBound(&announcer->scrape_info, &key, compareScrapeInfo, &found);
191 if (found)
192 {
193 info = tr_ptrArrayNth(&announcer->scrape_info, pos);
194 }
195 else
196 {
197 info = tr_new0(struct tr_scrape_info, 1);
198 info->url = tr_strdup(url);
199 info->multiscrape_max = TR_MULTISCRAPE_MAX;
200 tr_ptrArrayInsert(&announcer->scrape_info, info, pos);
201 }
202 }
203
204 return info;
205 }
206
207 static void onUpkeepTimer(evutil_socket_t foo UNUSED, short bar UNUSED, void* vannouncer);
208
tr_announcerInit(tr_session * session)209 void tr_announcerInit(tr_session* session)
210 {
211 TR_ASSERT(tr_isSession(session));
212
213 tr_announcer* a = tr_new0(tr_announcer, 1);
214 a->stops = TR_PTR_ARRAY_INIT;
215 a->key = tr_rand_int(INT_MAX);
216 a->session = session;
217 a->slotsAvailable = MAX_CONCURRENT_TASKS;
218 a->upkeepTimer = evtimer_new(session->event_base, onUpkeepTimer, a);
219 tr_timerAdd(a->upkeepTimer, UPKEEP_INTERVAL_SECS, 0);
220
221 session->announcer = a;
222 }
223
224 static void flushCloseMessages(tr_announcer* announcer);
225
tr_announcerClose(tr_session * session)226 void tr_announcerClose(tr_session* session)
227 {
228 tr_announcer* announcer = session->announcer;
229
230 flushCloseMessages(announcer);
231
232 tr_tracker_udp_start_shutdown(session);
233
234 event_free(announcer->upkeepTimer);
235 announcer->upkeepTimer = NULL;
236
237 tr_ptrArrayDestruct(&announcer->stops, NULL);
238 tr_ptrArrayDestruct(&announcer->scrape_info, scrapeInfoFree);
239
240 session->announcer = NULL;
241 tr_free(announcer);
242 }
243
244 /***
245 ****
246 ***/
247
248 /* a row in tr_tier's list of trackers */
249 typedef struct
250 {
251 char* key;
252 char* announce;
253 struct tr_scrape_info* scrape_info;
254
255 char* tracker_id_str;
256
257 int seederCount;
258 int leecherCount;
259 int downloadCount;
260 int downloaderCount;
261
262 int consecutiveFailures;
263
264 uint32_t id;
265 }
266 tr_tracker;
267
268 /* format: host+':'+ port */
getKey(char const * url)269 static char* getKey(char const* url)
270 {
271 char* ret;
272 char* scheme = NULL;
273 char* host = NULL;
274 int port = 0;
275
276 tr_urlParse(url, TR_BAD_SIZE, &scheme, &host, &port, NULL);
277 ret = tr_strdup_printf("%s://%s:%d", scheme != NULL ? scheme : "invalid", host != NULL ? host : "invalid", port);
278
279 tr_free(host);
280 tr_free(scheme);
281 return ret;
282 }
283
trackerConstruct(tr_announcer * announcer,tr_tracker * tracker,tr_tracker_info const * inf)284 static void trackerConstruct(tr_announcer* announcer, tr_tracker* tracker, tr_tracker_info const* inf)
285 {
286 memset(tracker, 0, sizeof(tr_tracker));
287 tracker->key = getKey(inf->announce);
288 tracker->announce = tr_strdup(inf->announce);
289 tracker->scrape_info = tr_announcerGetScrapeInfo(announcer, inf->scrape);
290 tracker->id = inf->id;
291 tracker->seederCount = -1;
292 tracker->leecherCount = -1;
293 tracker->downloadCount = -1;
294 }
295
trackerDestruct(tr_tracker * tracker)296 static void trackerDestruct(tr_tracker* tracker)
297 {
298 tr_free(tracker->tracker_id_str);
299 tr_free(tracker->announce);
300 tr_free(tracker->key);
301 }
302
303 /***
304 ****
305 ***/
306
307 struct tr_torrent_tiers;
308
309 /** @brief A group of trackers in a single tier, as per the multitracker spec */
310 typedef struct tr_tier
311 {
312 /* number of up/down/corrupt bytes since the last time we sent an
313 * "event=stopped" message that was acknowledged by the tracker */
314 uint64_t byteCounts[3];
315
316 tr_tracker* trackers;
317 int tracker_count;
318 tr_tracker* currentTracker;
319 int currentTrackerIndex;
320
321 tr_torrent* tor;
322
323 time_t scrapeAt;
324 time_t lastScrapeStartTime;
325 time_t lastScrapeTime;
326 bool lastScrapeSucceeded;
327 bool lastScrapeTimedOut;
328
329 time_t announceAt;
330 time_t manualAnnounceAllowedAt;
331 time_t lastAnnounceStartTime;
332 time_t lastAnnounceTime;
333 bool lastAnnounceSucceeded;
334 bool lastAnnounceTimedOut;
335
336 tr_announce_event* announce_events;
337 int announce_event_count;
338 int announce_event_alloc;
339
340 /* unique lookup key */
341 int key;
342
343 int scrapeIntervalSec;
344 int announceIntervalSec;
345 int announceMinIntervalSec;
346
347 int lastAnnouncePeerCount;
348
349 bool isRunning;
350 bool isAnnouncing;
351 bool isScraping;
352 bool wasCopied;
353
354 char lastAnnounceStr[128];
355 char lastScrapeStr[128];
356 }
357 tr_tier;
358
get_next_scrape_time(tr_session const * session,tr_tier const * tier,int interval)359 static time_t get_next_scrape_time(tr_session const* session, tr_tier const* tier, int interval)
360 {
361 time_t ret;
362 time_t const now = tr_time();
363
364 /* Maybe don't scrape paused torrents */
365 if (!tier->isRunning && !session->scrapePausedTorrents)
366 {
367 ret = 0;
368 }
369 /* Add the interval, and then increment to the nearest 10th second.
370 * The latter step is to increase the odds of several torrents coming
371 * due at the same time to improve multiscrape. */
372 else
373 {
374 ret = now + interval;
375
376 while (ret % 10 != 0)
377 {
378 ++ret;
379 }
380 }
381
382 return ret;
383 }
384
tierConstruct(tr_tier * tier,tr_torrent * tor)385 static void tierConstruct(tr_tier* tier, tr_torrent* tor)
386 {
387 static int nextKey = 1;
388
389 memset(tier, 0, sizeof(tr_tier));
390
391 tier->key = nextKey++;
392 tier->currentTrackerIndex = -1;
393 tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
394 tier->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
395 tier->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
396 tier->scrapeAt = get_next_scrape_time(tor->session, tier, tr_rand_int_weak(180));
397 tier->tor = tor;
398 }
399
tierDestruct(tr_tier * tier)400 static void tierDestruct(tr_tier* tier)
401 {
402 tr_free(tier->announce_events);
403 }
404
tier_build_log_name(tr_tier const * tier,char * buf,size_t buflen)405 static void tier_build_log_name(tr_tier const* tier, char* buf, size_t buflen)
406 {
407 tr_snprintf(buf, buflen, "[%s---%s]", (tier != NULL && tier->tor != NULL) ? tr_torrentName(tier->tor) : "?",
408 (tier != NULL && tier->currentTracker != NULL) ? tier->currentTracker->key : "?");
409 }
410
tierIncrementTracker(tr_tier * tier)411 static void tierIncrementTracker(tr_tier* tier)
412 {
413 /* move our index to the next tracker in the tier */
414 int const i = tier->currentTracker == NULL ? 0 : (tier->currentTrackerIndex + 1) % tier->tracker_count;
415 tier->currentTrackerIndex = i;
416 tier->currentTracker = &tier->trackers[i];
417
418 /* reset some of the tier's fields */
419 tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
420 tier->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
421 tier->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
422 tier->isAnnouncing = false;
423 tier->isScraping = false;
424 tier->lastAnnounceStartTime = 0;
425 tier->lastScrapeStartTime = 0;
426 }
427
428 /***
429 ****
430 ***/
431
432 /**
433 * @brief Opaque, per-torrent data structure for tracker announce information
434 *
435 * this opaque data structure can be found in tr_torrent.tiers
436 */
437 typedef struct tr_torrent_tiers
438 {
439 tr_tier* tiers;
440 int tier_count;
441
442 tr_tracker* trackers;
443 int tracker_count;
444
445 tr_tracker_callback callback;
446 void* callbackData;
447 }
448 tr_torrent_tiers;
449
tiersNew(void)450 static tr_torrent_tiers* tiersNew(void)
451 {
452 return tr_new0(tr_torrent_tiers, 1);
453 }
454
tiersDestruct(tr_torrent_tiers * tt)455 static void tiersDestruct(tr_torrent_tiers* tt)
456 {
457 for (int i = 0; i < tt->tracker_count; ++i)
458 {
459 trackerDestruct(&tt->trackers[i]);
460 }
461
462 tr_free(tt->trackers);
463
464 for (int i = 0; i < tt->tier_count; ++i)
465 {
466 tierDestruct(&tt->tiers[i]);
467 }
468
469 tr_free(tt->tiers);
470 }
471
tiersFree(tr_torrent_tiers * tt)472 static void tiersFree(tr_torrent_tiers* tt)
473 {
474 tiersDestruct(tt);
475 tr_free(tt);
476 }
477
getTier(tr_announcer * announcer,uint8_t const * info_hash,int tierId)478 static tr_tier* getTier(tr_announcer* announcer, uint8_t const* info_hash, int tierId)
479 {
480 tr_tier* tier = NULL;
481
482 if (announcer != NULL)
483 {
484 tr_session* session = announcer->session;
485 tr_torrent* tor = tr_torrentFindFromHash(session, info_hash);
486
487 if (tor != NULL && tor->tiers != NULL)
488 {
489 tr_torrent_tiers* tt = tor->tiers;
490
491 for (int i = 0; tier == NULL && i < tt->tier_count; ++i)
492 {
493 if (tt->tiers[i].key == tierId)
494 {
495 tier = &tt->tiers[i];
496 }
497 }
498 }
499 }
500
501 return tier;
502 }
503
504 /***
505 **** PUBLISH
506 ***/
507
508 static tr_tracker_event const TRACKER_EVENT_INIT =
509 {
510 .messageType = TR_TRACKER_WARNING,
511 .text = NULL,
512 .tracker = NULL,
513 .pex = NULL,
514 .pexCount = 0,
515 .seedProbability = 0
516 };
517
publishMessage(tr_tier * tier,char const * msg,int type)518 static void publishMessage(tr_tier* tier, char const* msg, int type)
519 {
520 if (tier != NULL && tier->tor != NULL && tier->tor->tiers != NULL && tier->tor->tiers->callback != NULL)
521 {
522 tr_torrent_tiers* tiers = tier->tor->tiers;
523 tr_tracker_event event = TRACKER_EVENT_INIT;
524 event.messageType = type;
525 event.text = msg;
526
527 if (tier->currentTracker != NULL)
528 {
529 event.tracker = tier->currentTracker->announce;
530 }
531
532 (*tiers->callback)(tier->tor, &event, tiers->callbackData);
533 }
534 }
535
publishErrorClear(tr_tier * tier)536 static void publishErrorClear(tr_tier* tier)
537 {
538 publishMessage(tier, NULL, TR_TRACKER_ERROR_CLEAR);
539 }
540
publishWarning(tr_tier * tier,char const * msg)541 static void publishWarning(tr_tier* tier, char const* msg)
542 {
543 publishMessage(tier, msg, TR_TRACKER_WARNING);
544 }
545
publishError(tr_tier * tier,char const * msg)546 static void publishError(tr_tier* tier, char const* msg)
547 {
548 publishMessage(tier, msg, TR_TRACKER_ERROR);
549 }
550
getSeedProbability(tr_tier * tier,int seeds,int leechers,int pex_count)551 static int8_t getSeedProbability(tr_tier* tier, int seeds, int leechers, int pex_count)
552 {
553 /* special case optimization:
554 ocelot omits seeds from peer lists sent to seeds on private trackers.
555 so check for that case... */
556 if (leechers == pex_count && tr_torrentIsPrivate(tier->tor) && tr_torrentIsSeed(tier->tor) && seeds + leechers < NUMWANT)
557 {
558 return 0;
559 }
560
561 if (seeds >= 0 && leechers >= 0 && seeds + leechers > 0)
562 {
563 return (int8_t)(100.0 * seeds / (seeds + leechers));
564 }
565
566 return -1; /* unknown */
567 }
568
publishPeersPex(tr_tier * tier,int seeds,int leechers,tr_pex const * pex,int n)569 static void publishPeersPex(tr_tier* tier, int seeds, int leechers, tr_pex const* pex, int n)
570 {
571 if (tier->tor->tiers->callback != NULL)
572 {
573 tr_tracker_event e = TRACKER_EVENT_INIT;
574 e.messageType = TR_TRACKER_PEERS;
575 e.seedProbability = getSeedProbability(tier, seeds, leechers, n);
576 e.pex = pex;
577 e.pexCount = n;
578 dbgmsg(tier, "got %d peers; seed prob %d", n, (int)e.seedProbability);
579
580 (*tier->tor->tiers->callback)(tier->tor, &e, NULL);
581 }
582 }
583
584 /***
585 ****
586 ***/
587
588 struct ann_tracker_info
589 {
590 tr_tracker_info info;
591
592 char* scheme;
593 char* host;
594 char* path;
595 int port;
596 };
597
598 /* primary key: tier
599 * secondary key: udp comes before http */
filter_trackers_compare_func(void const * va,void const * vb)600 static int filter_trackers_compare_func(void const* va, void const* vb)
601 {
602 struct ann_tracker_info const* a = va;
603 struct ann_tracker_info const* b = vb;
604
605 if (a->info.tier != b->info.tier)
606 {
607 return a->info.tier - b->info.tier;
608 }
609
610 return -strcmp(a->scheme, b->scheme);
611 }
612
613 /**
614 * Massages the incoming list of trackers into something we can use.
615 */
filter_trackers(tr_tracker_info * input,int input_count,int * setme_count)616 static tr_tracker_info* filter_trackers(tr_tracker_info* input, int input_count, int* setme_count)
617 {
618 int n = 0;
619 struct tr_tracker_info* ret;
620 struct ann_tracker_info* tmp = tr_new0(struct ann_tracker_info, input_count);
621
622 /*
623 for (int i = 0; i < input_count; ++i)
624 {
625 fprintf(stderr, "IN: [%d][%s]\n", input[i].tier, input[i].announce);
626 }
627 */
628
629 /* build a list of valid trackers */
630 for (int i = 0; i < input_count; ++i)
631 {
632 if (tr_urlIsValidTracker(input[i].announce))
633 {
634 int port;
635 char* scheme;
636 char* host;
637 char* path;
638 bool is_duplicate = false;
639 tr_urlParse(input[i].announce, TR_BAD_SIZE, &scheme, &host, &port, &path);
640
641 /* weed out one common source of duplicates:
642 * "http://tracker/announce" +
643 * "http://tracker:80/announce"
644 */
645 for (int j = 0; !is_duplicate && j < n; ++j)
646 {
647 is_duplicate = tmp[j].port == port && strcmp(tmp[j].scheme, scheme) == 0 && strcmp(tmp[j].host, host) == 0 &&
648 strcmp(tmp[j].path, path) == 0;
649 }
650
651 if (is_duplicate)
652 {
653 tr_free(path);
654 tr_free(host);
655 tr_free(scheme);
656 continue;
657 }
658
659 tmp[n].info = input[i];
660 tmp[n].scheme = scheme;
661 tmp[n].host = host;
662 tmp[n].port = port;
663 tmp[n].path = path;
664 n++;
665 }
666 }
667
668 /* if two announce URLs differ only by scheme, put them in the same tier.
669 * (note: this can leave gaps in the `tier' values, but since the calling
670 * function doesn't care, there's no point in removing the gaps...) */
671 for (int i = 0; i < n; ++i)
672 {
673 for (int j = i + 1; j < n; ++j)
674 {
675 if (tmp[i].info.tier != tmp[j].info.tier && tmp[i].port == tmp[j].port &&
676 tr_strcmp0(tmp[i].host, tmp[j].host) == 0 && tr_strcmp0(tmp[i].path, tmp[j].path) == 0)
677 {
678 tmp[j].info.tier = tmp[i].info.tier;
679 }
680 }
681 }
682
683 /* sort them, for two reasons:
684 * (1) unjumble the tiers from the previous step
685 * (2) move the UDP trackers to the front of each tier */
686 qsort(tmp, n, sizeof(struct ann_tracker_info), filter_trackers_compare_func);
687
688 /* build the output */
689 *setme_count = n;
690 ret = tr_new0(tr_tracker_info, n);
691
692 for (int i = 0; i < n; ++i)
693 {
694 ret[i] = tmp[i].info;
695 }
696
697 /* cleanup */
698 for (int i = 0; i < n; ++i)
699 {
700 tr_free(tmp[i].path);
701 tr_free(tmp[i].host);
702 tr_free(tmp[i].scheme);
703 }
704
705 tr_free(tmp);
706
707 /*
708 for (int i = 0; i < n; ++i)
709 {
710 fprintf (stderr, "OUT: [%d][%s]\n", ret[i].tier, ret[i].announce);
711 }
712 */
713
714 return ret;
715 }
716
addTorrentToTier(tr_torrent_tiers * tt,tr_torrent * tor)717 static void addTorrentToTier(tr_torrent_tiers* tt, tr_torrent* tor)
718 {
719 int n;
720 int tier_count;
721 tr_tier* tier;
722 tr_tracker_info* infos = filter_trackers(tor->info.trackers, tor->info.trackerCount, &n);
723
724 /* build the array of trackers */
725 tt->trackers = tr_new0(tr_tracker, n);
726 tt->tracker_count = n;
727
728 for (int i = 0; i < n; ++i)
729 {
730 trackerConstruct(tor->session->announcer, &tt->trackers[i], &infos[i]);
731 }
732
733 /* count how many tiers there are */
734 tier_count = 0;
735
736 for (int i = 0; i < n; ++i)
737 {
738 if (i == 0 || infos[i].tier != infos[i - 1].tier)
739 {
740 ++tier_count;
741 }
742 }
743
744 /* build the array of tiers */
745 tier = NULL;
746 tt->tiers = tr_new0(tr_tier, tier_count);
747 tt->tier_count = 0;
748
749 for (int i = 0; i < n; ++i)
750 {
751 if (i != 0 && infos[i].tier == infos[i - 1].tier)
752 {
753 ++tier->tracker_count;
754 }
755 else
756 {
757 tier = &tt->tiers[tt->tier_count++];
758 tierConstruct(tier, tor);
759 tier->trackers = &tt->trackers[i];
760 tier->tracker_count = 1;
761 tierIncrementTracker(tier);
762 }
763 }
764
765 /* cleanup */
766 tr_free(infos);
767 }
768
tr_announcerAddTorrent(tr_torrent * tor,tr_tracker_callback callback,void * callbackData)769 tr_torrent_tiers* tr_announcerAddTorrent(tr_torrent* tor, tr_tracker_callback callback, void* callbackData)
770 {
771 TR_ASSERT(tr_isTorrent(tor));
772
773 tr_torrent_tiers* tiers = tiersNew();
774 tiers->callback = callback;
775 tiers->callbackData = callbackData;
776
777 addTorrentToTier(tiers, tor);
778
779 return tiers;
780 }
781
782 /***
783 ****
784 ***/
785
tierCanManualAnnounce(tr_tier const * tier)786 static bool tierCanManualAnnounce(tr_tier const* tier)
787 {
788 return tier->manualAnnounceAllowedAt <= tr_time();
789 }
790
tr_announcerCanManualAnnounce(tr_torrent const * tor)791 bool tr_announcerCanManualAnnounce(tr_torrent const* tor)
792 {
793 TR_ASSERT(tr_isTorrent(tor));
794 TR_ASSERT(tor->tiers != NULL);
795
796 struct tr_torrent_tiers* tt = NULL;
797
798 if (tor->isRunning)
799 {
800 tt = tor->tiers;
801 }
802
803 /* return true if any tier can manual announce */
804 for (int i = 0; tt != NULL && i < tt->tier_count; ++i)
805 {
806 if (tierCanManualAnnounce(&tt->tiers[i]))
807 {
808 return true;
809 }
810 }
811
812 return false;
813 }
814
tr_announcerNextManualAnnounce(tr_torrent const * tor)815 time_t tr_announcerNextManualAnnounce(tr_torrent const* tor)
816 {
817 time_t ret = ~(time_t)0;
818 struct tr_torrent_tiers* tt = tor->tiers;
819
820 /* find the earliest manual announce time from all peers */
821 for (int i = 0; tt != NULL && i < tt->tier_count; ++i)
822 {
823 if (tt->tiers[i].isRunning)
824 {
825 ret = MIN(ret, tt->tiers[i].manualAnnounceAllowedAt);
826 }
827 }
828
829 return ret;
830 }
831
dbgmsg_tier_announce_queue(tr_tier const * tier)832 static void dbgmsg_tier_announce_queue(tr_tier const* tier)
833 {
834 if (tr_logGetDeepEnabled())
835 {
836 char name[128];
837 char* message;
838 struct evbuffer* buf = evbuffer_new();
839
840 tier_build_log_name(tier, name, sizeof(name));
841
842 for (int i = 0; i < tier->announce_event_count; ++i)
843 {
844 tr_announce_event const e = tier->announce_events[i];
845 char const* str = tr_announce_event_get_string(e);
846 evbuffer_add_printf(buf, "[%d:%s]", i, str);
847 }
848
849 message = evbuffer_free_to_str(buf, NULL);
850 tr_logAddDeep(__FILE__, __LINE__, name, "announce queue is %s", message);
851 tr_free(message);
852 }
853 }
854
tier_announce_remove_trailing(tr_tier * tier,tr_announce_event e)855 static void tier_announce_remove_trailing(tr_tier* tier, tr_announce_event e)
856 {
857 while (tier->announce_event_count > 0 && tier->announce_events[tier->announce_event_count - 1] == e)
858 {
859 --tier->announce_event_count;
860 }
861 }
862
tier_announce_event_push(tr_tier * tier,tr_announce_event e,time_t announceAt)863 static void tier_announce_event_push(tr_tier* tier, tr_announce_event e, time_t announceAt)
864 {
865 TR_ASSERT(tier != NULL);
866
867 dbgmsg_tier_announce_queue(tier);
868 dbgmsg(tier, "queued \"%s\"", tr_announce_event_get_string(e));
869
870 if (tier->announce_event_count > 0)
871 {
872 /* special case #1: if we're adding a "stopped" event,
873 * dump everything leading up to it except "completed" */
874 if (e == TR_ANNOUNCE_EVENT_STOPPED)
875 {
876 bool has_completed = false;
877 tr_announce_event const c = TR_ANNOUNCE_EVENT_COMPLETED;
878
879 for (int i = 0; !has_completed && i < tier->announce_event_count; ++i)
880 {
881 has_completed = c == tier->announce_events[i];
882 }
883
884 tier->announce_event_count = 0;
885
886 if (has_completed)
887 {
888 tier->announce_events[tier->announce_event_count++] = c;
889 }
890 }
891
892 /* special case #2: dump all empty strings leading up to this event */
893 tier_announce_remove_trailing(tier, TR_ANNOUNCE_EVENT_NONE);
894
895 /* special case #3: no consecutive duplicates */
896 tier_announce_remove_trailing(tier, e);
897 }
898
899 /* make room in the array for another event */
900 if (tier->announce_event_alloc <= tier->announce_event_count)
901 {
902 tier->announce_event_alloc += 4;
903 tier->announce_events = tr_renew(tr_announce_event, tier->announce_events, tier->announce_event_alloc);
904 }
905
906 /* add it */
907 tier->announce_events[tier->announce_event_count++] = e;
908 tier->announceAt = announceAt;
909
910 dbgmsg_tier_announce_queue(tier);
911 dbgmsg(tier, "announcing in %d seconds", (int)difftime(announceAt, tr_time()));
912 }
913
tier_announce_event_pull(tr_tier * tier)914 static tr_announce_event tier_announce_event_pull(tr_tier* tier)
915 {
916 tr_announce_event const e = tier->announce_events[0];
917
918 tr_removeElementFromArray(tier->announce_events, 0, sizeof(tr_announce_event), tier->announce_event_count);
919 --tier->announce_event_count;
920
921 return e;
922 }
923
torrentAddAnnounce(tr_torrent * tor,tr_announce_event e,time_t announceAt)924 static void torrentAddAnnounce(tr_torrent* tor, tr_announce_event e, time_t announceAt)
925 {
926 struct tr_torrent_tiers* tt = tor->tiers;
927
928 /* walk through each tier and tell them to announce */
929 for (int i = 0; i < tt->tier_count; ++i)
930 {
931 tier_announce_event_push(&tt->tiers[i], e, announceAt);
932 }
933 }
934
tr_announcerTorrentStarted(tr_torrent * tor)935 void tr_announcerTorrentStarted(tr_torrent* tor)
936 {
937 torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STARTED, tr_time());
938 }
939
tr_announcerManualAnnounce(tr_torrent * tor)940 void tr_announcerManualAnnounce(tr_torrent* tor)
941 {
942 torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_NONE, tr_time());
943 }
944
tr_announcerTorrentStopped(tr_torrent * tor)945 void tr_announcerTorrentStopped(tr_torrent* tor)
946 {
947 torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time());
948 }
949
tr_announcerTorrentCompleted(tr_torrent * tor)950 void tr_announcerTorrentCompleted(tr_torrent* tor)
951 {
952 torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time());
953 }
954
tr_announcerChangeMyPort(tr_torrent * tor)955 void tr_announcerChangeMyPort(tr_torrent* tor)
956 {
957 tr_announcerTorrentStarted(tor);
958 }
959
960 /***
961 ****
962 ***/
963
tr_announcerAddBytes(tr_torrent * tor,int type,uint32_t byteCount)964 void tr_announcerAddBytes(tr_torrent* tor, int type, uint32_t byteCount)
965 {
966 TR_ASSERT(tr_isTorrent(tor));
967 TR_ASSERT(type == TR_ANN_UP || type == TR_ANN_DOWN || type == TR_ANN_CORRUPT);
968
969 struct tr_torrent_tiers* tt = tor->tiers;
970
971 for (int i = 0; i < tt->tier_count; ++i)
972 {
973 tt->tiers[i].byteCounts[type] += byteCount;
974 }
975 }
976
977 /***
978 ****
979 ***/
980
announce_request_new(tr_announcer const * announcer,tr_torrent * tor,tr_tier const * tier,tr_announce_event event)981 static tr_announce_request* announce_request_new(tr_announcer const* announcer, tr_torrent* tor, tr_tier const* tier,
982 tr_announce_event event)
983 {
984 tr_announce_request* req = tr_new0(tr_announce_request, 1);
985 req->port = tr_sessionGetPublicPeerPort(announcer->session);
986 req->url = tr_strdup(tier->currentTracker->announce);
987 req->tracker_id_str = tr_strdup(tier->currentTracker->tracker_id_str);
988 memcpy(req->info_hash, tor->info.hash, SHA_DIGEST_LENGTH);
989 memcpy(req->peer_id, tr_torrentGetPeerId(tor), PEER_ID_LEN);
990 req->up = tier->byteCounts[TR_ANN_UP];
991 req->down = tier->byteCounts[TR_ANN_DOWN];
992 req->corrupt = tier->byteCounts[TR_ANN_CORRUPT];
993 req->leftUntilComplete = tr_torrentHasMetadata(tor) ? tor->info.totalSize - tr_torrentHaveTotal(tor) : INT64_MAX;
994 req->event = event;
995 req->numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : NUMWANT;
996 req->key = announcer->key;
997 req->partial_seed = tr_torrentGetCompleteness(tor) == TR_PARTIAL_SEED;
998 tier_build_log_name(tier, req->log_name, sizeof(req->log_name));
999 return req;
1000 }
1001
1002 static void announce_request_free(tr_announce_request* req);
1003
tr_announcerRemoveTorrent(tr_announcer * announcer,tr_torrent * tor)1004 void tr_announcerRemoveTorrent(tr_announcer* announcer, tr_torrent* tor)
1005 {
1006 struct tr_torrent_tiers* tt = tor->tiers;
1007
1008 if (tt != NULL)
1009 {
1010 for (int i = 0; i < tt->tier_count; ++i)
1011 {
1012 tr_tier* tier = &tt->tiers[i];
1013
1014 if (tier->isRunning)
1015 {
1016 tr_announce_event const e = TR_ANNOUNCE_EVENT_STOPPED;
1017 tr_announce_request* req = announce_request_new(announcer, tor, tier, e);
1018
1019 if (tr_ptrArrayFindSorted(&announcer->stops, req, compareStops) != NULL)
1020 {
1021 announce_request_free(req);
1022 }
1023 else
1024 {
1025 tr_ptrArrayInsertSorted(&announcer->stops, req, compareStops);
1026 }
1027 }
1028 }
1029
1030 tiersFree(tor->tiers);
1031 tor->tiers = NULL;
1032 }
1033 }
1034
getRetryInterval(tr_tracker const * t)1035 static int getRetryInterval(tr_tracker const* t)
1036 {
1037 switch (t->consecutiveFailures)
1038 {
1039 case 0:
1040 return 0;
1041
1042 case 1:
1043 return 20;
1044
1045 case 2:
1046 return tr_rand_int_weak(60) + 60 * 5;
1047
1048 case 3:
1049 return tr_rand_int_weak(60) + 60 * 15;
1050
1051 case 4:
1052 return tr_rand_int_weak(60) + 60 * 30;
1053
1054 case 5:
1055 return tr_rand_int_weak(60) + 60 * 60;
1056
1057 default:
1058 return tr_rand_int_weak(60) + 60 * 120;
1059 }
1060 }
1061
1062 struct announce_data
1063 {
1064 int tierId;
1065 time_t timeSent;
1066 tr_announce_event event;
1067 tr_session* session;
1068
1069 /** If the request succeeds, the value for tier's "isRunning" flag */
1070 bool isRunningOnSuccess;
1071 };
1072
on_announce_error(tr_tier * tier,char const * err,tr_announce_event e)1073 static void on_announce_error(tr_tier* tier, char const* err, tr_announce_event e)
1074 {
1075 int interval;
1076
1077 /* increment the error count */
1078 if (tier->currentTracker != NULL)
1079 {
1080 ++tier->currentTracker->consecutiveFailures;
1081 }
1082
1083 /* set the error message */
1084 dbgmsg(tier, "%s", err);
1085 tr_logAddTorInfo(tier->tor, "%s", err);
1086 tr_strlcpy(tier->lastAnnounceStr, err, sizeof(tier->lastAnnounceStr));
1087
1088 /* switch to the next tracker */
1089 tierIncrementTracker(tier);
1090
1091 /* schedule a reannounce */
1092 interval = getRetryInterval(tier->currentTracker);
1093 dbgmsg(tier, "Retrying announce in %d seconds.", interval);
1094 tr_logAddTorInfo(tier->tor, "Retrying announce in %d seconds.", interval);
1095 tier_announce_event_push(tier, e, tr_time() + interval);
1096 }
1097
on_announce_done(tr_announce_response const * response,void * vdata)1098 static void on_announce_done(tr_announce_response const* response, void* vdata)
1099 {
1100 struct announce_data* data = vdata;
1101 tr_announcer* announcer = data->session->announcer;
1102 tr_tier* tier = getTier(announcer, response->info_hash, data->tierId);
1103 time_t const now = tr_time();
1104 tr_announce_event const event = data->event;
1105
1106 if (announcer != NULL)
1107 {
1108 ++announcer->slotsAvailable;
1109 }
1110
1111 if (tier != NULL)
1112 {
1113 tr_tracker* tracker;
1114
1115 dbgmsg(tier,
1116 "Got announce response: "
1117 "connected:%d "
1118 "timeout:%d "
1119 "seeders:%d "
1120 "leechers:%d "
1121 "downloads:%d "
1122 "interval:%d "
1123 "min_interval:%d "
1124 "tracker_id_str:%s "
1125 "pex:%zu "
1126 "pex6:%zu "
1127 "err:%s "
1128 "warn:%s",
1129 (int)response->did_connect,
1130 (int)response->did_timeout,
1131 response->seeders,
1132 response->leechers,
1133 response->downloads,
1134 response->interval,
1135 response->min_interval,
1136 response->tracker_id_str != NULL ? response->tracker_id_str : "none",
1137 response->pex_count,
1138 response->pex6_count,
1139 response->errmsg != NULL ? response->errmsg : "none",
1140 response->warning != NULL ? response->warning : "none");
1141
1142 tier->lastAnnounceTime = now;
1143 tier->lastAnnounceTimedOut = response->did_timeout;
1144 tier->lastAnnounceSucceeded = false;
1145 tier->isAnnouncing = false;
1146 tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1147
1148 if (!response->did_connect)
1149 {
1150 on_announce_error(tier, _("Could not connect to tracker"), event);
1151 }
1152 else if (response->did_timeout)
1153 {
1154 on_announce_error(tier, _("Tracker did not respond"), event);
1155 }
1156 else if (response->errmsg != NULL)
1157 {
1158 /* If the torrent's only tracker returned an error, publish it.
1159 Don't bother publishing if there are other trackers -- it's
1160 all too common for people to load up dozens of dead trackers
1161 in a torrent's metainfo... */
1162 if (tier->tor->info.trackerCount < 2)
1163 {
1164 publishError(tier, response->errmsg);
1165 }
1166
1167 on_announce_error(tier, response->errmsg, event);
1168 }
1169 else
1170 {
1171 int i;
1172 char const* str;
1173 int scrape_fields = 0;
1174 int seeders = 0;
1175 int leechers = 0;
1176 int downloads = 0;
1177 bool const isStopped = event == TR_ANNOUNCE_EVENT_STOPPED;
1178
1179 publishErrorClear(tier);
1180
1181 if ((tracker = tier->currentTracker) != NULL)
1182 {
1183 tracker->consecutiveFailures = 0;
1184
1185 if (response->seeders >= 0)
1186 {
1187 tracker->seederCount = seeders = response->seeders;
1188 ++scrape_fields;
1189 }
1190
1191 if (response->leechers >= 0)
1192 {
1193 tracker->leecherCount = leechers = response->leechers;
1194 ++scrape_fields;
1195 }
1196
1197 if (response->downloads >= 0)
1198 {
1199 tracker->downloadCount = downloads = response->downloads;
1200 ++scrape_fields;
1201 }
1202
1203 if ((str = response->tracker_id_str) != NULL)
1204 {
1205 tr_free(tracker->tracker_id_str);
1206 tracker->tracker_id_str = tr_strdup(str);
1207 }
1208 }
1209
1210 if ((str = response->warning) != NULL)
1211 {
1212 tr_strlcpy(tier->lastAnnounceStr, str, sizeof(tier->lastAnnounceStr));
1213 dbgmsg(tier, "tracker gave \"%s\"", str);
1214 publishWarning(tier, str);
1215 }
1216 else
1217 {
1218 tr_strlcpy(tier->lastAnnounceStr, _("Success"), sizeof(tier->lastAnnounceStr));
1219 }
1220
1221 if ((i = response->min_interval) != 0)
1222 {
1223 tier->announceMinIntervalSec = i;
1224 }
1225
1226 if ((i = response->interval) != 0)
1227 {
1228 tier->announceIntervalSec = i;
1229 }
1230
1231 if (response->pex_count > 0)
1232 {
1233 publishPeersPex(tier, seeders, leechers, response->pex, response->pex_count);
1234 }
1235
1236 if (response->pex6_count > 0)
1237 {
1238 publishPeersPex(tier, seeders, leechers, response->pex6, response->pex6_count);
1239 }
1240
1241 tier->isRunning = data->isRunningOnSuccess;
1242
1243 /* if the tracker included scrape fields in its announce response,
1244 then a separate scrape isn't needed */
1245 if (scrape_fields >= 3 || (scrape_fields >= 1 && tracker->scrape_info == NULL))
1246 {
1247 tr_logAddTorDbg(tier->tor, "Announce response contained scrape info; "
1248 "rescheduling next scrape to %d seconds from now.", tier->scrapeIntervalSec);
1249 tier->scrapeAt = get_next_scrape_time(announcer->session, tier, tier->scrapeIntervalSec);
1250 tier->lastScrapeTime = now;
1251 tier->lastScrapeSucceeded = true;
1252 }
1253 else if (tier->lastScrapeTime + tier->scrapeIntervalSec <= now)
1254 {
1255 tier->scrapeAt = get_next_scrape_time(announcer->session, tier, 0);
1256 }
1257
1258 tier->lastAnnounceSucceeded = true;
1259 tier->lastAnnouncePeerCount = response->pex_count + response->pex6_count;
1260
1261 if (isStopped)
1262 {
1263 /* now that we've successfully stopped the torrent,
1264 * we can reset the up/down/corrupt count we've kept
1265 * for this tracker */
1266 tier->byteCounts[TR_ANN_UP] = 0;
1267 tier->byteCounts[TR_ANN_DOWN] = 0;
1268 tier->byteCounts[TR_ANN_CORRUPT] = 0;
1269 }
1270
1271 if (!isStopped && tier->announce_event_count == 0)
1272 {
1273 /* the queue is empty, so enqueue a perodic update */
1274 i = tier->announceIntervalSec;
1275 dbgmsg(tier, "Sending periodic reannounce in %d seconds", i);
1276 tier_announce_event_push(tier, TR_ANNOUNCE_EVENT_NONE, now + i);
1277 }
1278 }
1279 }
1280
1281 tr_free(data);
1282 }
1283
announce_request_free(tr_announce_request * req)1284 static void announce_request_free(tr_announce_request* req)
1285 {
1286 tr_free(req->tracker_id_str);
1287 tr_free(req->url);
1288 tr_free(req);
1289 }
1290
announce_request_delegate(tr_announcer * announcer,tr_announce_request * request,tr_announce_response_func callback,void * callback_data)1291 static void announce_request_delegate(tr_announcer* announcer, tr_announce_request* request, tr_announce_response_func callback,
1292 void* callback_data)
1293 {
1294 tr_session* session = announcer->session;
1295
1296 #if 0
1297
1298 fprintf(stderr, "ANNOUNCE: event %s isPartialSeed %d port %d key %d numwant %d up %" PRIu64 " down %" PRIu64
1299 " corrupt %" PRIu64 " left %" PRIu64 " url [%s] tracker_id_str [%s] peer_id [%20.20s]\n",
1300 tr_announce_event_get_string(request->event), (int)request->partial_seed, (int)request->port, request->key,
1301 request->numwant, request->up, request->down, request->corrupt, request->leftUntilComplete, request->url,
1302 request->tracker_id_str, request->peer_id);
1303
1304 #endif
1305
1306 if (strncmp(request->url, "http", 4) == 0)
1307 {
1308 tr_tracker_http_announce(session, request, callback, callback_data);
1309 }
1310 else if (strncmp(request->url, "udp://", 6) == 0)
1311 {
1312 tr_tracker_udp_announce(session, request, callback, callback_data);
1313 }
1314 else
1315 {
1316 tr_logAddError("Unsupported url: %s", request->url);
1317 }
1318
1319 announce_request_free(request);
1320 }
1321
tierAnnounce(tr_announcer * announcer,tr_tier * tier)1322 static void tierAnnounce(tr_announcer* announcer, tr_tier* tier)
1323 {
1324 TR_ASSERT(!tier->isAnnouncing);
1325 TR_ASSERT(tier->announce_event_count > 0);
1326
1327 time_t const now = tr_time();
1328
1329 tr_torrent* tor = tier->tor;
1330 tr_announce_event announce_event = tier_announce_event_pull(tier);
1331 tr_announce_request* req = announce_request_new(announcer, tor, tier, announce_event);
1332
1333 struct announce_data* data = tr_new0(struct announce_data, 1);
1334 data->session = announcer->session;
1335 data->tierId = tier->key;
1336 data->isRunningOnSuccess = tor->isRunning;
1337 data->timeSent = now;
1338 data->event = announce_event;
1339
1340 tier->isAnnouncing = true;
1341 tier->lastAnnounceStartTime = now;
1342 --announcer->slotsAvailable;
1343
1344 announce_request_delegate(announcer, req, on_announce_done, data);
1345 }
1346
1347 /***
1348 ****
1349 **** SCRAPE
1350 ****
1351 ***/
1352
multiscrape_too_big(char const * errmsg)1353 static bool multiscrape_too_big(char const* errmsg)
1354 {
1355 /* Found a tracker that returns some bespoke string for this case?
1356 Add your patch here and open a PR */
1357 static char const* const too_long_errors[] =
1358 {
1359 "Bad Request",
1360 "GET string too long",
1361 "Request-URI Too Long"
1362 };
1363
1364 if (errmsg == NULL)
1365 {
1366 return false;
1367 }
1368
1369 for (size_t i = 0; i < TR_N_ELEMENTS(too_long_errors); ++i)
1370 {
1371 if (strstr(errmsg, too_long_errors[i]) != NULL)
1372 {
1373 return true;
1374 }
1375 }
1376
1377 return false;
1378 }
1379
on_scrape_error(tr_session * session,tr_tier * tier,char const * errmsg)1380 static void on_scrape_error(tr_session* session, tr_tier* tier, char const* errmsg)
1381 {
1382 int interval;
1383
1384 /* increment the error count */
1385 if (tier->currentTracker != NULL)
1386 {
1387 ++tier->currentTracker->consecutiveFailures;
1388 }
1389
1390 /* set the error message */
1391 dbgmsg(tier, "Scrape error: %s", errmsg);
1392 tr_logAddTorInfo(tier->tor, "Scrape error: %s", errmsg);
1393 tr_strlcpy(tier->lastScrapeStr, errmsg, sizeof(tier->lastScrapeStr));
1394
1395 /* switch to the next tracker */
1396 tierIncrementTracker(tier);
1397
1398 /* schedule a rescrape */
1399 interval = getRetryInterval(tier->currentTracker);
1400 dbgmsg(tier, "Retrying scrape in %zu seconds.", (size_t)interval);
1401 tr_logAddTorInfo(tier->tor, "Retrying scrape in %zu seconds.", (size_t)interval);
1402 tier->lastScrapeSucceeded = false;
1403 tier->scrapeAt = get_next_scrape_time(session, tier, interval);
1404 }
1405
find_tier(tr_torrent * tor,char const * scrape)1406 static tr_tier* find_tier(tr_torrent* tor, char const* scrape)
1407 {
1408 struct tr_torrent_tiers* tt = tor->tiers;
1409
1410 for (int i = 0; tt != NULL && i < tt->tier_count; ++i)
1411 {
1412 tr_tracker const* const tracker = tt->tiers[i].currentTracker;
1413
1414 if (tracker != NULL &&
1415 tracker->scrape_info != NULL &&
1416 tr_strcmp0(scrape, tracker->scrape_info->url) == 0)
1417 {
1418 return &tt->tiers[i];
1419 }
1420 }
1421
1422 return NULL;
1423 }
1424
on_scrape_done(tr_scrape_response const * response,void * vsession)1425 static void on_scrape_done(tr_scrape_response const* response, void* vsession)
1426 {
1427 time_t const now = tr_time();
1428 tr_session* session = vsession;
1429 tr_announcer* announcer = session->announcer;
1430
1431 for (int i = 0; i < response->row_count; ++i)
1432 {
1433 struct tr_scrape_response_row const* row = &response->rows[i];
1434 tr_torrent* tor = tr_torrentFindFromHash(session, row->info_hash);
1435
1436 if (tor != NULL)
1437 {
1438 tr_tier* tier = find_tier(tor, response->url);
1439
1440 if (tier != NULL)
1441 {
1442 dbgmsg(tier,
1443 "scraped url:%s -- "
1444 "did_connect:%d "
1445 "did_timeout:%d "
1446 "seeders:%d "
1447 "leechers:%d "
1448 "downloads:%d "
1449 "downloaders:%d "
1450 "min_request_interval:%d "
1451 "err:%s ",
1452 response->url,
1453 (int)response->did_connect,
1454 (int)response->did_timeout,
1455 row->seeders,
1456 row->leechers,
1457 row->downloads,
1458 row->downloaders,
1459 response->min_request_interval,
1460 response->errmsg != NULL ? response->errmsg : "none");
1461
1462 tier->isScraping = false;
1463 tier->lastScrapeTime = now;
1464 tier->lastScrapeSucceeded = false;
1465 tier->lastScrapeTimedOut = response->did_timeout;
1466
1467 if (!response->did_connect)
1468 {
1469 on_scrape_error(session, tier, _("Could not connect to tracker"));
1470 }
1471 else if (response->did_timeout)
1472 {
1473 on_scrape_error(session, tier, _("Tracker did not respond"));
1474 }
1475 else if (response->errmsg != NULL)
1476 {
1477 on_scrape_error(session, tier, response->errmsg);
1478 }
1479 else
1480 {
1481 tr_tracker* tracker;
1482
1483 tier->lastScrapeSucceeded = true;
1484 tier->scrapeIntervalSec = MAX(DEFAULT_SCRAPE_INTERVAL_SEC, response->min_request_interval);
1485 tier->scrapeAt = get_next_scrape_time(session, tier, tier->scrapeIntervalSec);
1486 tr_logAddTorDbg(tier->tor, "Scrape successful. Rescraping in %d seconds.", tier->scrapeIntervalSec);
1487
1488 if ((tracker = tier->currentTracker) != NULL)
1489 {
1490 if (row->seeders >= 0)
1491 {
1492 tracker->seederCount = row->seeders;
1493 }
1494
1495 if (row->leechers >= 0)
1496 {
1497 tracker->leecherCount = row->leechers;
1498 }
1499
1500 if (row->downloads >= 0)
1501 {
1502 tracker->downloadCount = row->downloads;
1503 }
1504
1505 tracker->downloaderCount = row->downloaders;
1506 tracker->consecutiveFailures = 0;
1507 }
1508 }
1509 }
1510 }
1511 }
1512
1513 /* Maybe reduce the number of torrents in a multiscrape req */
1514 if (multiscrape_too_big(response->errmsg))
1515 {
1516 char const* url = response->url;
1517 int* multiscrape_max = &tr_announcerGetScrapeInfo(announcer, url)->multiscrape_max;
1518
1519 /* Lower the max only if it hasn't already lowered for a similar error.
1520 For example if N parallel multiscrapes all have the same `max` and
1521 error out, lower the value once for that batch, not N times. */
1522 if (*multiscrape_max >= response->row_count)
1523 {
1524 int const n = MAX(1, *multiscrape_max - TR_MULTISCRAPE_STEP);
1525 if (*multiscrape_max != n)
1526 {
1527 char* scheme = NULL;
1528 char* host = NULL;
1529 int port;
1530 if (tr_urlParse(url, strlen(url), &scheme, &host, &port, NULL))
1531 {
1532 /* don't log the full URL, since that might have a personal announce id */
1533 char* sanitized_url = tr_strdup_printf("%s://%s:%d", scheme, host, port);
1534 tr_logAddNamedInfo(sanitized_url, "Reducing multiscrape max to %d", n);
1535 tr_free(sanitized_url);
1536 tr_free(host);
1537 tr_free(scheme);
1538 }
1539
1540 *multiscrape_max = n;
1541 }
1542 }
1543 }
1544
1545 if (announcer != NULL)
1546 {
1547 ++announcer->slotsAvailable;
1548 }
1549 }
1550
scrape_request_delegate(tr_announcer * announcer,tr_scrape_request const * request,tr_scrape_response_func callback,void * callback_data)1551 static void scrape_request_delegate(tr_announcer* announcer, tr_scrape_request const* request, tr_scrape_response_func callback,
1552 void* callback_data)
1553 {
1554 tr_session* session = announcer->session;
1555
1556 if (strncmp(request->url, "http", 4) == 0)
1557 {
1558 tr_tracker_http_scrape(session, request, callback, callback_data);
1559 }
1560 else if (strncmp(request->url, "udp://", 6) == 0)
1561 {
1562 tr_tracker_udp_scrape(session, request, callback, callback_data);
1563 }
1564 else
1565 {
1566 tr_logAddError("Unsupported url: %s", request->url);
1567 }
1568 }
1569
multiscrape(tr_announcer * announcer,tr_ptrArray * tiers)1570 static void multiscrape(tr_announcer* announcer, tr_ptrArray* tiers)
1571 {
1572 int request_count = 0;
1573 time_t const now = tr_time();
1574 int const tier_count = tr_ptrArraySize(tiers);
1575 int const max_request_count = MIN(announcer->slotsAvailable, tier_count);
1576 tr_scrape_request* requests = tr_new0(tr_scrape_request, max_request_count);
1577
1578 /* batch as many info_hashes into a request as we can */
1579 for (int i = 0; i < tier_count; ++i)
1580 {
1581 tr_tier* tier = tr_ptrArrayNth(tiers, i);
1582 struct tr_scrape_info* const scrape_info = tier->currentTracker->scrape_info;
1583 uint8_t const* hash = tier->tor->info.hash;
1584 bool found = false;
1585
1586 TR_ASSERT(scrape_info != NULL);
1587
1588 /* if there's a request with this scrape URL and a free slot, use it */
1589 for (int j = 0; !found && j < request_count; ++j)
1590 {
1591 tr_scrape_request* req = &requests[j];
1592
1593 if (req->info_hash_count >= scrape_info->multiscrape_max)
1594 {
1595 continue;
1596 }
1597
1598 if (tr_strcmp0(req->url, scrape_info->url) != 0)
1599 {
1600 continue;
1601 }
1602
1603 memcpy(req->info_hash[req->info_hash_count++], hash, SHA_DIGEST_LENGTH);
1604 tier->isScraping = true;
1605 tier->lastScrapeStartTime = now;
1606 found = true;
1607 }
1608
1609 /* otherwise, if there's room for another request, build a new one */
1610 if (!found && request_count < max_request_count)
1611 {
1612 tr_scrape_request* req = &requests[request_count++];
1613 req->url = scrape_info->url;
1614 tier_build_log_name(tier, req->log_name, sizeof(req->log_name));
1615
1616 memcpy(req->info_hash[req->info_hash_count++], hash, SHA_DIGEST_LENGTH);
1617 tier->isScraping = true;
1618 tier->lastScrapeStartTime = now;
1619 }
1620 }
1621
1622 /* send the requests we just built */
1623 for (int i = 0; i < request_count; ++i)
1624 {
1625 scrape_request_delegate(announcer, &requests[i], on_scrape_done, announcer->session);
1626 }
1627
1628 /* cleanup */
1629 tr_free(requests);
1630 }
1631
flushCloseMessages(tr_announcer * announcer)1632 static void flushCloseMessages(tr_announcer* announcer)
1633 {
1634 for (int i = 0, n = tr_ptrArraySize(&announcer->stops); i < n; ++i)
1635 {
1636 announce_request_delegate(announcer, tr_ptrArrayNth(&announcer->stops, i), NULL, NULL);
1637 }
1638
1639 tr_ptrArrayClear(&announcer->stops);
1640 }
1641
tierNeedsToAnnounce(tr_tier const * tier,time_t const now)1642 static bool tierNeedsToAnnounce(tr_tier const* tier, time_t const now)
1643 {
1644 return !tier->isAnnouncing && !tier->isScraping && tier->announceAt != 0 && tier->announceAt <= now &&
1645 tier->announce_event_count > 0;
1646 }
1647
tierNeedsToScrape(tr_tier const * tier,time_t const now)1648 static bool tierNeedsToScrape(tr_tier const* tier, time_t const now)
1649 {
1650 return !tier->isScraping && tier->scrapeAt != 0 && tier->scrapeAt <= now && tier->currentTracker != NULL &&
1651 tier->currentTracker->scrape_info != NULL;
1652 }
1653
compareTiers(void const * va,void const * vb)1654 static int compareTiers(void const* va, void const* vb)
1655 {
1656 int ret;
1657 tr_tier const* a = *(tr_tier const**)va;
1658 tr_tier const* b = *(tr_tier const**)vb;
1659
1660 /* primary key: larger stats come before smaller */
1661 ret = compareTransfer(a->byteCounts[TR_ANN_UP], a->byteCounts[TR_ANN_DOWN], b->byteCounts[TR_ANN_UP],
1662 b->byteCounts[TR_ANN_DOWN]);
1663
1664 /* secondary key: announcements that have been waiting longer go first */
1665 if (ret == 0 && a->announceAt != b->announceAt)
1666 {
1667 ret = a->announceAt < b->announceAt ? -1 : 1;
1668 }
1669
1670 return ret;
1671 }
1672
announceMore(tr_announcer * announcer)1673 static void announceMore(tr_announcer* announcer)
1674 {
1675 int n;
1676 tr_torrent* tor;
1677 tr_ptrArray announceMe = TR_PTR_ARRAY_INIT;
1678 tr_ptrArray scrapeMe = TR_PTR_ARRAY_INIT;
1679 time_t const now = tr_time();
1680
1681 dbgmsg(NULL, "announceMore: slotsAvailable is %d", announcer->slotsAvailable);
1682
1683 if (announcer->slotsAvailable < 1)
1684 {
1685 return;
1686 }
1687
1688 /* build a list of tiers that need to be announced */
1689 tor = NULL;
1690
1691 while ((tor = tr_torrentNext(announcer->session, tor)) != NULL)
1692 {
1693 struct tr_torrent_tiers* tt = tor->tiers;
1694
1695 for (int i = 0; tt != NULL && i < tt->tier_count; ++i)
1696 {
1697 tr_tier* tier = &tt->tiers[i];
1698
1699 if (tierNeedsToAnnounce(tier, now))
1700 {
1701 tr_ptrArrayAppend(&announceMe, tier);
1702 }
1703 else if (tierNeedsToScrape(tier, now))
1704 {
1705 tr_ptrArrayAppend(&scrapeMe, tier);
1706 }
1707 }
1708 }
1709
1710 n = tr_ptrArraySize(&announceMe);
1711
1712 /* if there are more tiers than slots available, prioritize */
1713 if (n > announcer->slotsAvailable)
1714 {
1715 qsort(tr_ptrArrayBase(&announceMe), n, sizeof(tr_tier*), compareTiers);
1716 n = announcer->slotsAvailable;
1717 }
1718
1719 /* announce some */
1720 for (int i = 0; i < n; ++i)
1721 {
1722 tr_tier* tier = tr_ptrArrayNth(&announceMe, i);
1723 tr_logAddTorDbg(tier->tor, "%s", "Announcing to tracker");
1724 dbgmsg(tier, "announcing tier %d of %d", i, n);
1725 tierAnnounce(announcer, tier);
1726 }
1727
1728 /* scrape some */
1729 multiscrape(announcer, &scrapeMe);
1730
1731 /* cleanup */
1732 tr_ptrArrayDestruct(&scrapeMe, NULL);
1733 tr_ptrArrayDestruct(&announceMe, NULL);
1734 }
1735
onUpkeepTimer(evutil_socket_t foo UNUSED,short bar UNUSED,void * vannouncer)1736 static void onUpkeepTimer(evutil_socket_t foo UNUSED, short bar UNUSED, void* vannouncer)
1737 {
1738 tr_announcer* announcer = vannouncer;
1739 tr_session* session = announcer->session;
1740 bool const is_closing = session->isClosed;
1741 time_t const now = tr_time();
1742
1743 tr_sessionLock(session);
1744
1745 /* maybe send out some "stopped" messages for closed torrents */
1746 flushCloseMessages(announcer);
1747
1748 /* maybe send out some announcements to trackers */
1749 if (!is_closing)
1750 {
1751 announceMore(announcer);
1752 }
1753
1754 /* TAU upkeep */
1755 if (announcer->tauUpkeepAt <= now)
1756 {
1757 announcer->tauUpkeepAt = now + TAU_UPKEEP_INTERVAL_SECS;
1758 tr_tracker_udp_upkeep(session);
1759 }
1760
1761 /* set up the next timer */
1762 tr_timerAdd(announcer->upkeepTimer, UPKEEP_INTERVAL_SECS, 0);
1763
1764 tr_sessionUnlock(session);
1765 }
1766
1767 /***
1768 ****
1769 ***/
1770
tr_announcerStats(tr_torrent const * torrent,int * setmeTrackerCount)1771 tr_tracker_stat* tr_announcerStats(tr_torrent const* torrent, int* setmeTrackerCount)
1772 {
1773 TR_ASSERT(tr_isTorrent(torrent));
1774
1775 time_t const now = tr_time();
1776
1777 int out = 0;
1778 tr_tracker_stat* ret;
1779 struct tr_torrent_tiers* tt = torrent->tiers;
1780
1781 /* alloc the stats */
1782 *setmeTrackerCount = tt->tracker_count;
1783 ret = tr_new0(tr_tracker_stat, tt->tracker_count);
1784
1785 /* populate the stats */
1786 for (int i = 0; i < tt->tier_count; ++i)
1787 {
1788 tr_tier const* const tier = &tt->tiers[i];
1789
1790 for (int j = 0; j < tier->tracker_count; ++j)
1791 {
1792 tr_tracker const* const tracker = &tier->trackers[j];
1793 tr_tracker_stat* st = &ret[out++];
1794
1795 st->id = tracker->id;
1796 tr_strlcpy(st->host, tracker->key, sizeof(st->host));
1797 tr_strlcpy(st->announce, tracker->announce, sizeof(st->announce));
1798 st->tier = i;
1799 st->isBackup = tracker != tier->currentTracker;
1800 st->lastScrapeStartTime = tier->lastScrapeStartTime;
1801
1802 if (tracker->scrape_info != NULL)
1803 {
1804 tr_strlcpy(st->scrape, tracker->scrape_info->url, sizeof(st->scrape));
1805 }
1806 else
1807 {
1808 st->scrape[0] = '\0';
1809 }
1810
1811 st->seederCount = tracker->seederCount;
1812 st->leecherCount = tracker->leecherCount;
1813 st->downloadCount = tracker->downloadCount;
1814
1815 if (st->isBackup)
1816 {
1817 st->scrapeState = TR_TRACKER_INACTIVE;
1818 st->announceState = TR_TRACKER_INACTIVE;
1819 st->nextScrapeTime = 0;
1820 st->nextAnnounceTime = 0;
1821 }
1822 else
1823 {
1824 if ((st->hasScraped = tier->lastScrapeTime != 0))
1825 {
1826 st->lastScrapeTime = tier->lastScrapeTime;
1827 st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
1828 st->lastScrapeTimedOut = tier->lastScrapeTimedOut;
1829 tr_strlcpy(st->lastScrapeResult, tier->lastScrapeStr, sizeof(st->lastScrapeResult));
1830 }
1831
1832 if (tier->isScraping)
1833 {
1834 st->scrapeState = TR_TRACKER_ACTIVE;
1835 }
1836 else if (tier->scrapeAt == 0)
1837 {
1838 st->scrapeState = TR_TRACKER_INACTIVE;
1839 }
1840 else if (tier->scrapeAt > now)
1841 {
1842 st->scrapeState = TR_TRACKER_WAITING;
1843 st->nextScrapeTime = tier->scrapeAt;
1844 }
1845 else
1846 {
1847 st->scrapeState = TR_TRACKER_QUEUED;
1848 }
1849
1850 st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
1851
1852 if ((st->hasAnnounced = tier->lastAnnounceTime != 0))
1853 {
1854 st->lastAnnounceTime = tier->lastAnnounceTime;
1855 tr_strlcpy(st->lastAnnounceResult, tier->lastAnnounceStr, sizeof(st->lastAnnounceResult));
1856 st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded;
1857 st->lastAnnounceTimedOut = tier->lastAnnounceTimedOut;
1858 st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
1859 }
1860
1861 if (tier->isAnnouncing)
1862 {
1863 st->announceState = TR_TRACKER_ACTIVE;
1864 }
1865 else if (!torrent->isRunning || tier->announceAt == 0)
1866 {
1867 st->announceState = TR_TRACKER_INACTIVE;
1868 }
1869 else if (tier->announceAt > now)
1870 {
1871 st->announceState = TR_TRACKER_WAITING;
1872 st->nextAnnounceTime = tier->announceAt;
1873 }
1874 else
1875 {
1876 st->announceState = TR_TRACKER_QUEUED;
1877 }
1878 }
1879 }
1880 }
1881
1882 return ret;
1883 }
1884
tr_announcerStatsFree(tr_tracker_stat * trackers,int trackerCount UNUSED)1885 void tr_announcerStatsFree(tr_tracker_stat* trackers, int trackerCount UNUSED)
1886 {
1887 tr_free(trackers);
1888 }
1889
1890 /***
1891 ****
1892 ***/
1893
copy_tier_attributes_impl(struct tr_tier * tgt,int trackerIndex,tr_tier const * src)1894 static void copy_tier_attributes_impl(struct tr_tier* tgt, int trackerIndex, tr_tier const* src)
1895 {
1896 /* sanity clause */
1897 TR_ASSERT(trackerIndex < tgt->tracker_count);
1898 TR_ASSERT(tr_strcmp0(tgt->trackers[trackerIndex].announce, src->currentTracker->announce) == 0);
1899
1900 tr_tier const keep = *tgt;
1901
1902 /* bitwise copy will handle most of tr_tier's fields... */
1903 *tgt = *src;
1904
1905 /* ...fix the fields that can't be cleanly bitwise-copied */
1906 tgt->wasCopied = true;
1907 tgt->trackers = keep.trackers;
1908 tgt->tracker_count = keep.tracker_count;
1909 tgt->announce_events = tr_memdup(src->announce_events, sizeof(tr_announce_event) * src->announce_event_count);
1910 tgt->announce_event_count = src->announce_event_count;
1911 tgt->announce_event_alloc = src->announce_event_count;
1912 tgt->currentTrackerIndex = trackerIndex;
1913 tgt->currentTracker = &tgt->trackers[trackerIndex];
1914 tgt->currentTracker->seederCount = src->currentTracker->seederCount;
1915 tgt->currentTracker->leecherCount = src->currentTracker->leecherCount;
1916 tgt->currentTracker->downloadCount = src->currentTracker->downloadCount;
1917 tgt->currentTracker->downloaderCount = src->currentTracker->downloaderCount;
1918 }
1919
copy_tier_attributes(struct tr_torrent_tiers * tt,tr_tier const * src)1920 static void copy_tier_attributes(struct tr_torrent_tiers* tt, tr_tier const* src)
1921 {
1922 bool found = false;
1923
1924 /* find a tier (if any) which has a match for src->currentTracker */
1925 for (int i = 0; !found && i < tt->tier_count; ++i)
1926 {
1927 for (int j = 0; !found && j < tt->tiers[i].tracker_count; ++j)
1928 {
1929 if ((found = tr_strcmp0(src->currentTracker->announce, tt->tiers[i].trackers[j].announce) == 0))
1930 {
1931 copy_tier_attributes_impl(&tt->tiers[i], j, src);
1932 }
1933 }
1934 }
1935 }
1936
tr_announcerResetTorrent(tr_announcer * announcer UNUSED,tr_torrent * tor)1937 void tr_announcerResetTorrent(tr_announcer* announcer UNUSED, tr_torrent* tor)
1938 {
1939 TR_ASSERT(tor->tiers != NULL);
1940
1941 time_t const now = tr_time();
1942
1943 struct tr_torrent_tiers* tt = tor->tiers;
1944 tr_torrent_tiers old = *tt;
1945
1946 /* remove the old tiers / trackers */
1947 tt->tiers = NULL;
1948 tt->trackers = NULL;
1949 tt->tier_count = 0;
1950 tt->tracker_count = 0;
1951
1952 /* create the new tiers / trackers */
1953 addTorrentToTier(tt, tor);
1954
1955 /* copy the old tiers' states into their replacements */
1956 for (int i = 0; i < old.tier_count; ++i)
1957 {
1958 if (old.tiers[i].currentTracker != NULL)
1959 {
1960 copy_tier_attributes(tt, &old.tiers[i]);
1961 }
1962 }
1963
1964 /* kickstart any tiers that didn't get started */
1965 if (tor->isRunning)
1966 {
1967 for (int i = 0; i < tt->tier_count; ++i)
1968 {
1969 if (!tt->tiers[i].wasCopied)
1970 {
1971 tier_announce_event_push(&tt->tiers[i], TR_ANNOUNCE_EVENT_STARTED, now);
1972 }
1973 }
1974 }
1975
1976 /* cleanup */
1977 tiersDestruct(&old);
1978 }
1979