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