source: trunk/libtransmission/announcer.c @ 10697

Last change on this file since 10697 was 10697, checked in by charles, 12 years ago

(trunk libT) revert r10661 s.t. DHT peers are preferred slightly over PEX peers. This reversion comes after reading "Understanding the Properties of the BitTorrent? Overlay" (2007, Al-Hamra) @ http://arxiv.org/pdf/0707.1820 at Juliusz's suggestion. Also tweak the announce's 'numwant' threshold based on findings from the same paper.

File size: 59.5 KB
Line 
1/*
2 * This file Copyright (C) 2009-2010 Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id:$
11 */
12
13#include <assert.h>
14#include <limits.h>
15
16#include <event.h>
17#include <evhttp.h> /* for HTTP_OK */
18
19#include "transmission.h"
20#include "announcer.h"
21#include "crypto.h"
22#include "net.h"
23#include "ptrarray.h"
24#include "publish.h"
25#include "session.h"
26#include "tr-dht.h"
27#include "tr-lpd.h"
28#include "torrent.h"
29#include "utils.h"
30#include "web.h"
31
32#define STARTED "started"
33
34#define dbgmsg( tier, ... ) \
35if( tr_deepLoggingIsActive( ) ) do { \
36  char name[128]; \
37  tr_snprintf( name, sizeof( name ), "[%s--%s]", tr_torrentName( tier->tor ), \
38      ( tier->currentTracker ? tier->currentTracker->host->name : "" ) ); \
39  tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \
40} while( 0 )
41
42enum
43{
44    /* unless the tracker says otherwise, rescrape this frequently */
45    DEFAULT_SCRAPE_INTERVAL_SEC = ( 60 * 30 ),
46
47    /* unless the tracker says otherwise, this is the announce interval */
48    DEFAULT_ANNOUNCE_INTERVAL_SEC = ( 60 * 10 ),
49
50    /* unless the tracker says otherwise, this is the announce min_interval */
51    DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = ( 60 * 2 ),
52
53    /* the length of the 'key' argument passed in tracker requests */
54    KEYLEN = 8,
55
56    /* how many web tasks we allow at one time */
57    MAX_CONCURRENT_TASKS = 48,
58
59    /* if a tracker takes more than this long to respond,
60     * we treat it as nonresponsive */
61    MAX_TRACKER_RESPONSE_TIME_SECS = ( 60 * 2 ),
62
63    /* the value of the 'numwant' argument passed in tracker requests. */
64    NUMWANT = 80,
65
66    /* how long to put slow (nonresponsive) trackers in the penalty box */
67    SLOW_HOST_PENALTY_SECS = ( 60 * 10 ),
68
69    UPKEEP_INTERVAL_SECS = 1,
70
71    /* this is an upper limit for the frequency of LDS announces */
72    LPD_HOUSEKEEPING_INTERVAL_SECS = 30
73
74};
75
76/***
77****
78***/
79
80static int
81compareTransfer( int a_uploaded, int a_downloaded,
82                 int b_uploaded, int b_downloaded )
83{
84    /* higher upload count goes first */
85    if( a_uploaded != b_uploaded )
86        return a_uploaded > b_uploaded ? -1 : 1;
87
88    /* then higher download count goes first */
89    if( a_downloaded != b_downloaded )
90        return a_downloaded > b_downloaded ? -1 : 1;
91
92    return 0;
93}
94
95/***
96****
97***/
98
99/**
100 * used by tr_announcer to recognize nonresponsive
101 * trackers and de-prioritize them
102 */
103typedef struct
104{
105    char * name;
106
107    /* how many seconds it took to get the last tracker response */
108    int lastResponseInterval;
109
110    /* the last time we sent an announce or scrape message */
111    time_t lastRequestTime;
112
113    /* the last successful announce/scrape time for this host */
114    time_t lastSuccessfulRequest;
115}
116tr_host;
117
118static int
119compareHosts( const void * va, const void * vb )
120{
121    const tr_host * a = va;
122    const tr_host * b = vb;
123    return strcmp( a->name, b->name );
124}
125
126static int
127compareHostToName( const void * va, const void * vb )
128{
129    const tr_host * a = va;
130    return strcmp( a->name, vb );
131}
132
133/* format: hostname + ':' + port */
134static char *
135getHostName( const char * url )
136{
137    int port = 0;
138    char * host = NULL;
139    char * ret;
140    tr_urlParse( url, strlen( url ), NULL, &host, &port, NULL );
141    ret = tr_strdup_printf( "%s:%d", ( host ? host : "invalid" ), port );
142    tr_free( host );
143    return ret;
144}
145
146static tr_host*
147hostNew( const char * name )
148{
149    tr_host * host = tr_new0( tr_host, 1 );
150    host->name = tr_strdup( name );
151    return host;
152}
153
154static void
155hostFree( void * vhost )
156{
157    tr_host * host = vhost;
158
159    tr_free( host->name );
160    tr_free( host );
161}
162
163/***
164****
165***/
166
167/**
168 * Since we can't poll a tr_torrent's fields after it's destroyed,
169 * we pre-build the "stop" announcement message when a torrent
170 * is removed from Transmission
171 */
172struct stop_message
173{
174    tr_host * host;
175    char * url;
176    int up;
177    int down;
178};
179
180static void
181stopFree( struct stop_message * stop )
182{
183    tr_free( stop->url );
184    tr_free( stop );
185}
186
187static int
188compareStops( const void * va, const void * vb )
189{
190    const struct stop_message * a = va;
191    const struct stop_message * b = vb;
192    return compareTransfer( a->up, a->down, b->up, b->down);
193}
194
195/***
196****
197***/
198
199/**
200 * "global" (per-tr_session) fields
201 */
202typedef struct tr_announcer
203{
204    tr_ptrArray hosts; /* tr_host */
205    tr_ptrArray stops; /* struct stop_message */
206    tr_session * session;
207    struct event * upkeepTimer;
208    int slotsAvailable;
209    time_t lpdHouseKeepingAt;
210}
211tr_announcer;
212
213tr_bool
214tr_announcerHasBacklog( const struct tr_announcer * announcer )
215{
216    return announcer->slotsAvailable < 1;
217}
218
219static tr_host *
220getHost( tr_announcer * announcer, const char * url )
221{
222    char * name = getHostName( url );
223    tr_host * host;
224
225    host = tr_ptrArrayFindSorted( &announcer->hosts, name, compareHostToName );
226    if( host == NULL )
227    {
228        host = hostNew( name );
229        tr_ptrArrayInsertSorted( &announcer->hosts, host, compareHosts );
230    }
231
232    tr_free( name );
233    return host;
234}
235
236static void
237onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer );
238
239static inline time_t
240calcRescheduleWithJitter( const int minPeriod )
241{
242    const double jitterFac = 0.1;
243
244    assert( minPeriod > 0 );
245
246    return tr_time()
247        + minPeriod
248        + tr_cryptoWeakRandInt( (int) ( minPeriod * jitterFac ) + 1 );
249}
250
251void
252tr_announcerInit( tr_session * session )
253{
254    tr_announcer * a;
255
256    const time_t relaxUntil =
257        calcRescheduleWithJitter( LPD_HOUSEKEEPING_INTERVAL_SECS / 3 );
258
259    assert( tr_isSession( session ) );
260
261    a = tr_new0( tr_announcer, 1 );
262    a->hosts = TR_PTR_ARRAY_INIT;
263    a->stops = TR_PTR_ARRAY_INIT;
264    a->session = session;
265    a->slotsAvailable = MAX_CONCURRENT_TASKS;
266    a->lpdHouseKeepingAt = relaxUntil;
267    a->upkeepTimer = tr_new0( struct event, 1 );
268    evtimer_set( a->upkeepTimer, onUpkeepTimer, a );
269    tr_timerAdd( a->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
270
271    session->announcer = a;
272}
273
274static void flushCloseMessages( tr_announcer * announcer );
275
276void
277tr_announcerClose( tr_session * session )
278{
279    tr_announcer * announcer = session->announcer;
280
281    flushCloseMessages( announcer );
282
283    evtimer_del( announcer->upkeepTimer );
284    tr_free( announcer->upkeepTimer );
285    announcer->upkeepTimer = NULL;
286
287    tr_ptrArrayDestruct( &announcer->stops, NULL );
288    tr_ptrArrayDestruct( &announcer->hosts, hostFree );
289
290    session->announcer = NULL;
291    tr_free( announcer );
292}
293
294/***
295****
296***/
297
298/* a row in tr_tier's list of trackers */
299typedef struct
300{
301    tr_host * host;
302
303    char * announce;
304    char * scrape;
305
306    char * tracker_id;
307
308    int seederCount;
309    int leecherCount;
310    int downloadCount;
311    int downloaderCount;
312
313    uint32_t id;
314
315    /* sent as the "key" argument in tracker requests
316     * to verify us if our IP address changes.
317     * This is immutable for the life of the tracker object.
318     * The +1 is for '\0' */
319    char key_param[KEYLEN + 1];
320}
321tr_tracker_item;
322
323static void
324trackerItemCopyAttributes( tr_tracker_item * t, const tr_tracker_item * o )
325{
326    assert( t != o );
327    assert( t != NULL );
328    assert( o != NULL );
329
330    t->seederCount = o->seederCount;
331    t->leecherCount = o->leecherCount;
332    t->downloadCount = o->downloadCount;
333    t->downloaderCount = o->downloaderCount;
334    memcpy( t->key_param, o->key_param, sizeof( t->key_param ) );
335}
336
337static void
338generateKeyParam( char * msg, size_t msglen )
339{
340    size_t i;
341    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
342    const int poolSize = strlen( pool );
343
344    for( i=0; i<msglen; ++i )
345        *msg++ = pool[tr_cryptoRandInt( poolSize )];
346    *msg = '\0';
347}
348
349static tr_tracker_item*
350trackerNew( tr_announcer  * announcer,
351            const char    * announce,
352            const char    * scrape,
353            uint32_t        id )
354{
355    tr_tracker_item * tracker = tr_new0( tr_tracker_item, 1  );
356    tracker->host = getHost( announcer, announce );
357    tracker->announce = tr_strdup( announce );
358    tracker->scrape = tr_strdup( scrape );
359    tracker->id = id;
360    generateKeyParam( tracker->key_param, KEYLEN );
361    tracker->seederCount = -1;
362    tracker->leecherCount = -1;
363    tracker->downloadCount = -1;
364    return tracker;
365}
366
367static void
368trackerFree( void * vtracker )
369{
370    tr_tracker_item * tracker = vtracker;
371
372    tr_free( tracker->tracker_id );
373    tr_free( tracker->announce );
374    tr_free( tracker->scrape );
375    tr_free( tracker );
376}
377
378/***
379****
380***/
381
382struct tr_torrent_tiers;
383
384/** @brief A group of trackers in a single tier, as per the multitracker spec */
385typedef struct
386{
387    /* number of up/down/corrupt bytes since the last time we sent an
388     * "event=stopped" message that was acknowledged by the tracker */
389    uint64_t byteCounts[3];
390
391    tr_ptrArray trackers; /* tr_tracker_item */
392    tr_tracker_item * currentTracker;
393    int currentTrackerIndex;
394
395    tr_torrent * tor;
396
397    time_t scrapeAt;
398    time_t lastScrapeStartTime;
399    time_t lastScrapeTime;
400    tr_bool lastScrapeSucceeded;
401    tr_bool lastScrapeTimedOut;
402
403    time_t announceAt;
404    time_t manualAnnounceAllowedAt;
405    time_t lastAnnounceStartTime;
406    time_t lastAnnounceTime;
407    tr_bool lastAnnounceSucceeded;
408    tr_bool lastAnnounceTimedOut;
409
410    tr_ptrArray announceEvents; /* const char* */
411
412    /* unique lookup key */
413    int key;
414
415    int scrapeIntervalSec;
416    int announceIntervalSec;
417    int announceMinIntervalSec;
418
419    int lastAnnouncePeerCount;
420
421    tr_bool isRunning;
422    tr_bool isAnnouncing;
423    tr_bool isScraping;
424    tr_bool wasCopied;
425
426    char lastAnnounceStr[128];
427    char lastScrapeStr[128];
428}
429tr_tier;
430
431static tr_tier *
432tierNew( tr_torrent * tor )
433{
434    tr_tier * t;
435    static int nextKey = 1;
436    const time_t now = tr_time( );
437
438    t = tr_new0( tr_tier, 1 );
439    t->key = nextKey++;
440    t->announceEvents = TR_PTR_ARRAY_INIT;
441    t->trackers = TR_PTR_ARRAY_INIT;
442    t->currentTracker = NULL;
443    t->currentTrackerIndex = -1;
444    t->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
445    t->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
446    t->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
447    t->scrapeAt = now + tr_cryptoWeakRandInt( 60*5 );
448    t->tor = tor;
449
450    return t;
451}
452
453static void
454tierFree( void * vtier )
455{
456    tr_tier * tier = vtier;
457    tr_ptrArrayDestruct( &tier->trackers, trackerFree );
458    tr_ptrArrayDestruct( &tier->announceEvents, NULL );
459    tr_free( tier );
460}
461
462static void
463tierCopyAttributes( tr_tier * t, const tr_tier * o )
464{
465    int i, n;
466    tr_tier bak;
467
468    assert( t != NULL );
469    assert( o != NULL );
470    assert( t != o );
471
472    bak = *t;
473    *t = *o;
474    t->tor = bak.tor;
475    t->trackers = bak.trackers;
476    t->announceEvents = bak.announceEvents;
477    t->currentTracker = bak.currentTracker;
478    t->currentTrackerIndex = bak.currentTrackerIndex;
479
480    tr_ptrArrayClear( &t->announceEvents );
481    for( i=0, n=tr_ptrArraySize(&o->announceEvents); i<n; ++i )
482        tr_ptrArrayAppend( &t->announceEvents, tr_ptrArrayNth((tr_ptrArray*)&o->announceEvents,i) );
483}
484
485static void
486tierIncrementTracker( tr_tier * tier )
487{
488    /* move our index to the next tracker in the tier */
489    const int i = ( tier->currentTrackerIndex + 1 )
490                        % tr_ptrArraySize( &tier->trackers );
491    tier->currentTracker = tr_ptrArrayNth( &tier->trackers, i );
492    tier->currentTrackerIndex = i;
493
494    /* reset some of the tier's fields */
495    tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
496    tier->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC;
497    tier->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
498    tier->isAnnouncing = FALSE;
499    tier->isScraping = FALSE;
500    tier->lastAnnounceStartTime = 0;
501    tier->lastScrapeStartTime = 0;
502}
503
504static void
505tierAddTracker( tr_announcer * announcer,
506                tr_tier      * tier,
507                const char   * announce,
508                const char   * scrape,
509                uint32_t       id )
510{
511    tr_tracker_item * tracker = trackerNew( announcer, announce, scrape, id );
512
513    tr_ptrArrayAppend( &tier->trackers, tracker );
514    dbgmsg( tier, "adding tracker %s", announce );
515
516    if( !tier->currentTracker )
517        tierIncrementTracker( tier );
518}
519
520
521/***
522****
523***/
524
525/**
526 * @brief Opaque, per-torrent data structure for tracker announce information
527 *
528 * this opaque data structure can be found in tr_torrent.tiers
529 */
530typedef struct tr_torrent_tiers
531{
532    tr_ptrArray tiers;
533    tr_publisher publisher;
534}
535tr_torrent_tiers;
536
537static tr_torrent_tiers*
538tiersNew( void )
539{
540    tr_torrent_tiers * tiers = tr_new0( tr_torrent_tiers, 1 );
541    tiers->tiers = TR_PTR_ARRAY_INIT;
542    tiers->publisher = TR_PUBLISHER_INIT;
543    return tiers;
544}
545
546static void
547tiersFree( tr_torrent_tiers * tiers )
548{
549    tr_publisherDestruct( &tiers->publisher );
550    tr_ptrArrayDestruct( &tiers->tiers, tierFree );
551    tr_free( tiers );
552}
553
554static tr_tier*
555getTier( tr_announcer * announcer, int torrentId, int tierId )
556{
557    tr_tier * tier = NULL;
558
559    if( announcer )
560    {
561        tr_torrent * tor = tr_torrentFindFromId( announcer->session, torrentId );
562
563        if( tor && tor->tiers )
564        {
565            int i;
566            tr_ptrArray * tiers = &tor->tiers->tiers;
567            const int n = tr_ptrArraySize( tiers );
568            for( i=0; !tier && i<n; ++i )
569            {
570                tr_tier * tmp = tr_ptrArrayNth( tiers, i );
571                if( tmp->key == tierId )
572                    tier = tmp;
573            }
574        }
575    }
576
577    return tier;
578}
579
580/***
581****  PUBLISH
582***/
583
584static const tr_tracker_event emptyEvent = { 0, NULL, NULL, NULL, 0, 0 };
585
586static void
587publishMessage( tr_tier * tier, const char * msg, int type )
588{
589    if( tier && tier->tor && tier->tor->tiers )
590    {
591        tr_torrent_tiers * tiers = tier->tor->tiers;
592        tr_tracker_event event = emptyEvent;
593        event.messageType = type;
594        event.text = msg;
595        event.tracker = tier->currentTracker ? tier->currentTracker->announce : NULL;
596        tr_publisherPublish( &tiers->publisher, tier, &event );
597    }
598}
599
600static void
601publishErrorClear( tr_tier * tier )
602{
603    publishMessage( tier, NULL, TR_TRACKER_ERROR_CLEAR );
604}
605
606static void
607publishErrorMessageAndStop( tr_tier * tier, const char * msg )
608{
609    tier->isRunning = FALSE;
610
611    publishMessage( tier, msg, TR_TRACKER_ERROR );
612}
613
614static void
615publishWarning( tr_tier * tier, const char * msg )
616{
617    publishMessage( tier, msg, TR_TRACKER_WARNING );
618}
619
620static int
621getSeedProbability( int seeds, int leechers )
622{
623    if( !seeds )
624        return 0;
625
626    if( seeds>=0 && leechers>=0 )
627        return (int)((100.0*seeds)/(seeds+leechers));
628
629    return -1; /* unknown */
630}
631
632static int
633publishNewPeers( tr_tier * tier, int seeds, int leechers,
634                 const void * compact, int compactLen )
635{
636    tr_tracker_event e = emptyEvent;
637
638    e.messageType = TR_TRACKER_PEERS;
639    e.seedProbability = getSeedProbability( seeds, leechers );
640    e.compact = compact;
641    e.compactLen = compactLen;
642
643    tr_publisherPublish( &tier->tor->tiers->publisher, tier, &e );
644
645    return compactLen / 6;
646}
647
648static int
649publishNewPeersCompact( tr_tier * tier, int seeds, int leechers,
650                        const void * compact, int compactLen )
651{
652    int i;
653    const uint8_t *compactWalk;
654    uint8_t *array, *walk;
655    const int peerCount = compactLen / 6;
656    const int arrayLen = peerCount * ( sizeof( tr_address ) + 2 );
657    tr_address addr;
658    tr_port port;
659
660    addr.type = TR_AF_INET;
661    memset( &addr.addr, 0, sizeof( addr.addr ) );
662    array = tr_new( uint8_t, arrayLen );
663    for ( i=0, walk=array, compactWalk=compact ; i<peerCount ; ++i )
664    {
665        memcpy( &addr.addr.addr4, compactWalk, 4 );
666        memcpy( &port, compactWalk + 4, 2 );
667
668        memcpy( walk, &addr, sizeof( addr ) );
669        memcpy( walk + sizeof( addr ), &port, 2 );
670
671        walk += sizeof( tr_address ) + 2;
672        compactWalk += 6;
673    }
674
675    publishNewPeers( tier, seeds, leechers, array, arrayLen );
676
677    tr_free( array );
678
679    return peerCount;
680}
681
682static int
683publishNewPeersCompact6( tr_tier * tier, int seeds, int leechers,
684                         const void * compact, int compactLen )
685{
686    int i;
687    const uint8_t *compactWalk;
688    uint8_t *array, *walk;
689    const int peerCount = compactLen / 18;
690    const int arrayLen = peerCount * ( sizeof( tr_address ) + 2 );
691    tr_address addr;
692    tr_port port;
693
694    addr.type = TR_AF_INET6;
695    memset( &addr.addr, 0, sizeof( addr.addr ) );
696    array = tr_new( uint8_t, arrayLen );
697    for ( i = 0, walk = array, compactWalk = compact ; i < peerCount ; ++i )
698    {
699        memcpy( &addr.addr.addr6, compactWalk, 16 );
700        memcpy( &port, compactWalk + 16, 2 );
701        compactWalk += 18;
702
703        memcpy( walk, &addr, sizeof( addr ) );
704        memcpy( walk + sizeof( addr ), &port, 2 );
705        walk += sizeof( tr_address ) + 2;
706    }
707
708    publishNewPeers( tier, seeds, leechers, array, arrayLen );
709    tr_free( array );
710
711    return peerCount;
712}
713
714static char*
715createAnnounceURL( const tr_announcer     * announcer,
716                   const tr_torrent       * torrent,
717                   const tr_tier          * tier,
718                   const char             * eventName )
719{
720    const int isStopping = !strcmp( eventName, "stopped" );
721    const int numwant = isStopping ? 0 : NUMWANT;
722    const tr_tracker_item  * tracker = tier->currentTracker;
723    const char * ann = tracker->announce;
724    struct evbuffer * buf = evbuffer_new( );
725    char * ret;
726    const char * str;
727    const unsigned char * ipv6;
728
729    evbuffer_expand( buf, 2048 );
730
731    evbuffer_add_printf( buf, "%s"
732                              "%c"
733                              "info_hash=%s"
734                              "&peer_id=%s"
735                              "&port=%d"
736                              "&uploaded=%" PRIu64
737                              "&downloaded=%" PRIu64
738                              "&left=%" PRIu64
739                              "&numwant=%d"
740                              "&key=%s"
741                              "&compact=1"
742                              "&supportcrypto=1",
743                              ann,
744                              strchr( ann, '?' ) ? '&' : '?',
745                              torrent->info.hashEscaped,
746                              torrent->peer_id,
747                              (int)tr_sessionGetPeerPort( announcer->session ),
748                              tier->byteCounts[TR_ANN_UP],
749                              tier->byteCounts[TR_ANN_DOWN],
750                              tr_cpLeftUntilComplete( &torrent->completion ),
751                              numwant,
752                              tracker->key_param );
753
754    if( announcer->session->encryptionMode == TR_ENCRYPTION_REQUIRED )
755        evbuffer_add_printf( buf, "&requirecrypto=1" );
756
757    if( tier->byteCounts[TR_ANN_CORRUPT] )
758        evbuffer_add_printf( buf, "&corrupt=%" PRIu64, tier->byteCounts[TR_ANN_CORRUPT] );
759
760    str = eventName;
761    if( str && *str )
762        evbuffer_add_printf( buf, "&event=%s", str );
763
764    str = tracker->tracker_id;
765    if( str && *str )
766        evbuffer_add_printf( buf, "&trackerid=%s", str );
767
768    /* There are two incompatible techniques for announcing an IPv6 address.
769       BEP-7 suggests adding an "ipv6=" parameter to the announce URL,
770       while OpenTracker requires that peers announce twice, once over IPv4
771       and once over IPv6.
772
773       To be safe, we should do both: add the "ipv6=" parameter and
774       announce twice.  At any rate, we're already computing our IPv6
775       address (for the LTEP handshake), so this comes for free. */
776
777    ipv6 = tr_globalIPv6( );
778    if( ipv6 ) {
779        char ipv6_readable[INET6_ADDRSTRLEN];
780        inet_ntop( AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN );
781        evbuffer_add_printf( buf, "&ipv6=");
782        tr_http_escape( buf, ipv6_readable, strlen(ipv6_readable), TRUE );
783    }
784
785    ret = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
786    dbgmsg( tier, "announce URL is \"%s\"", ret );
787    evbuffer_free( buf );
788    return ret;
789}
790
791
792/***
793****
794***/
795
796static tr_bool
797announceURLIsSupported( const char * announce )
798{
799    return ( announce != NULL )
800        && ( ( strstr( announce, "http://" ) == announce ) ||
801             ( strstr( announce, "https://" ) == announce ) );
802}
803
804static void
805addTorrentToTier( tr_announcer * announcer, tr_torrent_tiers * tiers, tr_torrent * tor )
806{
807    int i, n;
808    const tr_tracker_info ** infos;
809    const int trackerCount = tor->info.trackerCount;
810    const tr_tracker_info  * trackers = tor->info.trackers;
811
812    /* get the trackers that we support... */
813    infos = tr_new0( const tr_tracker_info*, trackerCount );
814    for( i=n=0; i<trackerCount; ++i )
815        if( announceURLIsSupported( trackers[i].announce ) )
816            infos[n++] = &trackers[i];
817
818    /* build our private table of tiers... */
819    if( n > 0 )
820    {
821        int tierIndex = -1;
822        tr_tier * tier = NULL;
823
824        for( i=0; i<n; ++i )
825        {
826            const tr_tracker_info * info = infos[i];
827
828            if( info->tier != tierIndex )
829                tier = NULL;
830
831            tierIndex = info->tier;
832
833            if( tier == NULL ) {
834                tier = tierNew( tor );
835                dbgmsg( tier, "adding tier" );
836                tr_ptrArrayAppend( &tiers->tiers, tier );
837            }
838
839            tierAddTracker( announcer, tier, info->announce, info->scrape, info->id );
840        }
841    }
842
843    tr_free( infos );
844}
845
846tr_torrent_tiers *
847tr_announcerAddTorrent( tr_announcer * announcer, tr_torrent * tor )
848{
849    tr_torrent_tiers * tiers;
850
851    assert( announcer != NULL );
852    assert( tr_isTorrent( tor ) );
853
854    tiers = tiersNew( );
855
856    addTorrentToTier( announcer, tiers, tor );
857
858    return tiers;
859}
860
861static void
862tierAddAnnounce( tr_tier * tier, const char * announceEvent, time_t announceAt );
863
864void
865tr_announcerResetTorrent( tr_announcer * announcer, tr_torrent * tor )
866{
867    tr_ptrArray oldTiers = TR_PTR_ARRAY_INIT;
868
869    /* if we had tiers already, make a backup of them */
870    if( tor->tiers != NULL )
871    {
872        oldTiers = tor->tiers->tiers;
873        tor->tiers->tiers = TR_PTR_ARRAY_INIT;
874    }
875
876    /* create the new tier/tracker structs */
877    addTorrentToTier( announcer, tor->tiers, tor );
878
879    /* if we had tiers already, merge their state into the new structs */
880    if( !tr_ptrArrayEmpty( &oldTiers ) )
881    {
882        int i, in;
883        for( i=0, in=tr_ptrArraySize(&oldTiers); i<in; ++i )
884        {
885            int j, jn;
886            const tr_tier * o = tr_ptrArrayNth( &oldTiers, i );
887
888            if( o->currentTracker == NULL )
889                continue;
890
891            for( j=0, jn=tr_ptrArraySize(&tor->tiers->tiers); j<jn; ++j )
892            {
893                int k, kn;
894                tr_tier * t = tr_ptrArrayNth(&tor->tiers->tiers,j);
895
896                for( k=0, kn=tr_ptrArraySize(&t->trackers); k<kn; ++k )
897                {
898                    tr_tracker_item * item = tr_ptrArrayNth(&t->trackers,k);
899                    if( strcmp( o->currentTracker->announce, item->announce ) )
900                        continue;
901                    tierCopyAttributes( t, o );
902                    t->currentTracker = item;
903                    t->currentTrackerIndex = k;
904                    t->wasCopied = TRUE;
905                    trackerItemCopyAttributes( item, o->currentTracker );
906                    dbgmsg( t, "attributes copied to tier %d, tracker %d"
907                                               "from tier %d, tracker %d",
908                            i, o->currentTrackerIndex, j, k );
909
910                }
911            }
912        }
913    }
914
915    /* kickstart any tiers that didn't get started */
916    if( tor->isRunning )
917    {
918        int i, n;
919        const time_t now = tr_time( );
920        tr_tier ** tiers = (tr_tier**) tr_ptrArrayPeek( &tor->tiers->tiers, &n );
921        for( i=0; i<n; ++i ) {
922            tr_tier * tier = tiers[i];
923            if( !tier->wasCopied )
924                tierAddAnnounce( tier, STARTED, now );
925        }
926    }
927
928    /* cleanup */
929    tr_ptrArrayDestruct( &oldTiers, tierFree );
930}
931
932tr_publisher_tag
933tr_announcerSubscribe( struct tr_torrent_tiers   * tiers,
934                       tr_delivery_func            func,
935                       void                      * userData )
936{
937    return tr_publisherSubscribe( &tiers->publisher, func, userData );
938}
939
940void
941tr_announcerUnsubscribe( struct tr_torrent_tiers  * tiers,
942                         tr_publisher_tag           tag )
943{
944    if( tiers )
945        tr_publisherUnsubscribe( &tiers->publisher, tag );
946}
947
948static tr_bool
949tierCanManualAnnounce( const tr_tier * tier )
950{
951    return tier->isRunning
952        && tier->manualAnnounceAllowedAt <= tr_time( );
953}
954
955tr_bool
956tr_announcerCanManualAnnounce( const tr_torrent * tor )
957{
958    int i;
959    int n;
960    const tr_tier ** tiers;
961
962    assert( tr_isTorrent( tor ) );
963    assert( tor->tiers != NULL );
964
965    n = tr_ptrArraySize( &tor->tiers->tiers );
966    tiers = (const tr_tier**) tr_ptrArrayBase( &tor->tiers->tiers );
967    for( i=0; i<n; ++i )
968        if( tierCanManualAnnounce( tiers[i] ) )
969            return TRUE;
970
971    return FALSE;
972}
973
974time_t
975tr_announcerNextManualAnnounce( const tr_torrent * tor )
976{
977    int i;
978    int n;
979    const tr_torrent_tiers * tiers;
980    time_t ret = ~(time_t)0;
981
982    assert( tr_isTorrent( tor  ) );
983
984    tiers = tor->tiers;
985    n = tr_ptrArraySize( &tiers->tiers );
986    for( i=0; i<n; ++i ) {
987        tr_tier * tier = tr_ptrArrayNth( (tr_ptrArray*)&tiers->tiers, i );
988        if( tier->isRunning )
989            ret = MIN( ret, tier->manualAnnounceAllowedAt );
990    }
991
992    return ret;
993}
994
995static void
996tierAddAnnounce( tr_tier * tier, const char * announceEvent, time_t announceAt )
997{
998    assert( tier != NULL );
999    assert( announceEvent != NULL );
1000
1001    tr_ptrArrayAppend( &tier->announceEvents, (void*)announceEvent );
1002    tier->announceAt = announceAt;
1003
1004    dbgmsg( tier, "appended event \"%s\"; announcing in %d seconds\n", announceEvent, (int)difftime(announceAt,time(NULL)) );
1005}
1006
1007static void
1008torrentAddAnnounce( tr_torrent * tor, const char * announceEvent, time_t announceAt )
1009{
1010    int i;
1011    int n;
1012    tr_torrent_tiers * tiers;
1013
1014    assert( tr_isTorrent( tor ) );
1015
1016    tiers = tor->tiers;
1017    n = tr_ptrArraySize( &tiers->tiers );
1018    for( i=0; i<n; ++i )
1019        tierAddAnnounce( tr_ptrArrayNth( &tiers->tiers, i ), announceEvent, announceAt );
1020}
1021
1022void
1023tr_announcerTorrentStarted( tr_torrent * tor )
1024{
1025    torrentAddAnnounce( tor, STARTED, tr_time( ) );
1026}
1027void
1028tr_announcerManualAnnounce( tr_torrent * tor )
1029{
1030    torrentAddAnnounce( tor, "", tr_time( ) );
1031}
1032void
1033tr_announcerTorrentStopped( tr_torrent * tor )
1034{
1035    torrentAddAnnounce( tor, "stopped", tr_time( ) );
1036}
1037void
1038tr_announcerTorrentCompleted( tr_torrent * tor )
1039{
1040    torrentAddAnnounce( tor, "completed", tr_time( ) );
1041}
1042void
1043tr_announcerChangeMyPort( tr_torrent * tor )
1044{
1045    tr_announcerTorrentStarted( tor );
1046}
1047
1048/***
1049****
1050***/
1051
1052void
1053tr_announcerAddBytes( tr_torrent * tor, int type, uint32_t byteCount )
1054{
1055    int i, n;
1056    tr_torrent_tiers * tiers;
1057
1058    assert( tr_isTorrent( tor ) );
1059    assert( type==TR_ANN_UP || type==TR_ANN_DOWN || type==TR_ANN_CORRUPT );
1060
1061    tiers = tor->tiers;
1062    n = tr_ptrArraySize( &tiers->tiers );
1063    for( i=0; i<n; ++i )
1064    {
1065        tr_tier * tier = tr_ptrArrayNth( &tiers->tiers, i );
1066        tier->byteCounts[ type ] += byteCount;
1067    }
1068}
1069
1070/***
1071****
1072***/
1073
1074void
1075tr_announcerRemoveTorrent( tr_announcer * announcer, tr_torrent * tor )
1076{
1077    assert( tr_isTorrent( tor ) );
1078
1079    if( tor->tiers )
1080    {
1081        int i;
1082        const int n = tr_ptrArraySize( &tor->tiers->tiers );
1083        for( i=0; i<n; ++i )
1084        {
1085            tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
1086
1087            if( tier->isRunning )
1088            {
1089                struct stop_message * s = tr_new0( struct stop_message, 1 );
1090                s->up = tier->byteCounts[TR_ANN_UP];
1091                s->down = tier->byteCounts[TR_ANN_DOWN];
1092                s->url = createAnnounceURL( announcer, tor, tier, "stopped" );
1093                s->host = tier->currentTracker->host;
1094                tr_ptrArrayInsertSorted( &announcer->stops, s, compareStops );
1095            }
1096        }
1097
1098        tiersFree( tor->tiers );
1099        tor->tiers = NULL;
1100    }
1101}
1102
1103/* return true if (1) we've tried it recently AND (2) it didn't respond... */
1104static tr_bool
1105hostIsNotResponding( const tr_host * host, const time_t now )
1106{
1107    tr_bool b = ( host->lastRequestTime )
1108             && ( host->lastRequestTime >= ( now - SLOW_HOST_PENALTY_SECS ) )
1109             && ( host->lastResponseInterval > MAX_TRACKER_RESPONSE_TIME_SECS );
1110    return b;
1111}
1112
1113static tr_bool
1114tierIsNotResponding( const tr_tier * tier, const time_t now )
1115{
1116    return !tier->currentTracker
1117        || hostIsNotResponding( tier->currentTracker->host, now );
1118}
1119
1120static int
1121getRetryInterval( const tr_host * host )
1122{
1123    int interval;
1124    const int jitter = tr_cryptoWeakRandInt( 120 );
1125    const time_t timeSinceLastSuccess = tr_time() - host->lastSuccessfulRequest;
1126         if( timeSinceLastSuccess < 15*60 ) interval = 0;
1127    else if( timeSinceLastSuccess < 30*60 ) interval = 60*4;
1128    else if( timeSinceLastSuccess < 45*60 ) interval = 60*8;
1129    else if( timeSinceLastSuccess < 60*60 ) interval = 60*16;
1130    else                                    interval = 60*32;
1131    return interval + jitter;
1132}
1133
1134static int
1135compareTiers( const void * va, const void * vb )
1136{
1137    int ret = 0;
1138    tr_bool af, bf;
1139    const tr_tier * a = *(const tr_tier**)va;
1140    const tr_tier * b = *(const tr_tier**)vb;
1141
1142    /* working domains come before non-working */
1143    if( !ret ) {
1144        const time_t now = tr_time( );
1145        af = tierIsNotResponding( a, now );
1146        bf = tierIsNotResponding( b, now );
1147        if( af != bf )
1148            ret = !af ? -1 : 1;
1149    }
1150
1151    /* stops come before starts */
1152    if( !ret ) {
1153        af = a->tor->isRunning;
1154        bf = b->tor->isRunning;
1155        if( af != bf )
1156            ret = af ? 1 : -1;
1157    }
1158
1159    /* upload comes before download */
1160    if( !ret )
1161        ret = compareTransfer( a->byteCounts[TR_ANN_UP], a->byteCounts[TR_ANN_DOWN],
1162                               b->byteCounts[TR_ANN_UP], b->byteCounts[TR_ANN_DOWN] );
1163
1164    /* incomplete comes before complete */
1165    if( !ret ) {
1166        af = a->tor->completeness == TR_LEECH;
1167        bf = b->tor->completeness == TR_LEECH;
1168        if( af != bf )
1169            return af ? -1 : 1;
1170    }
1171
1172    /* private before public */
1173    if( !ret ) {
1174        af = tr_torrentIsPrivate( a->tor );
1175        bf = tr_torrentIsPrivate( b->tor );
1176        if( af != bf )
1177            ret = af ? -1 : 1;
1178    }
1179
1180    return ret;
1181}
1182
1183static uint8_t *
1184parseOldPeers( tr_benc * bePeers, size_t *  byteCount )
1185{
1186    int       i;
1187    uint8_t * array, *walk;
1188    const int peerCount = bePeers->val.l.count;
1189
1190    assert( tr_bencIsList( bePeers ) );
1191
1192    array = tr_new( uint8_t, peerCount * ( sizeof( tr_address ) + 2 ) );
1193
1194    for( i = 0, walk = array; i < peerCount; ++i )
1195    {
1196        const char * s;
1197        int64_t      itmp;
1198        tr_address   addr;
1199        tr_port      port;
1200        tr_benc    * peer = &bePeers->val.l.vals[i];
1201
1202        if( tr_bencDictFindStr( peer, "ip", &s ) )
1203            if( tr_pton( s, &addr ) == NULL )
1204                continue;
1205
1206        if( !tr_bencDictFindInt( peer, "port", &itmp )
1207                || itmp < 0
1208                || itmp > USHRT_MAX )
1209            continue;
1210
1211        memcpy( walk, &addr, sizeof( tr_address ) );
1212        port = htons( itmp );
1213        memcpy( walk + sizeof( tr_address ), &port, 2 );
1214        walk += sizeof( tr_address ) + 2;
1215    }
1216
1217    *byteCount = peerCount * sizeof( tr_address ) + 2;
1218    return array;
1219}
1220
1221static tr_bool
1222parseAnnounceResponse( tr_tier     * tier,
1223                       const char  * response,
1224                       size_t        responseLen,
1225                       tr_bool     * gotScrape )
1226{
1227    tr_benc benc;
1228    tr_bool success = FALSE;
1229    int scrapeFields = 0;
1230    const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, NULL );
1231
1232    dbgmsg( tier, "response len: %d, isBenc: %d", (int)responseLen, (int)bencLoaded );
1233    publishErrorClear( tier );
1234    if( bencLoaded && tr_bencIsDict( &benc ) )
1235    {
1236        int peerCount = 0;
1237        size_t rawlen;
1238        int64_t i;
1239        tr_benc * tmp;
1240        const char * str;
1241        const uint8_t * raw;
1242        tr_bool gotPeers = FALSE;
1243
1244        success = TRUE;
1245
1246        if( tr_bencDictFindStr( &benc, "failure reason", &str ) )
1247        {
1248            tr_strlcpy( tier->lastAnnounceStr, str,
1249                        sizeof( tier->lastAnnounceStr ) );
1250            dbgmsg( tier, "tracker gave \"%s\"", str );
1251            publishMessage( tier, str, TR_TRACKER_ERROR );
1252            success = FALSE;
1253        }
1254
1255        if( tr_bencDictFindStr( &benc, "warning message", &str ) )
1256        {
1257            tr_strlcpy( tier->lastAnnounceStr, str,
1258                        sizeof( tier->lastAnnounceStr ) );
1259            dbgmsg( tier, "tracker gave \"%s\"", str );
1260            publishWarning( tier, str );
1261        }
1262
1263        if( tr_bencDictFindInt( &benc, "interval", &i ) )
1264        {
1265            dbgmsg( tier, "setting interval to %d", (int)i );
1266            tier->announceIntervalSec = i;
1267        }
1268
1269        if( tr_bencDictFindInt( &benc, "min interval", &i ) )
1270        {
1271            dbgmsg( tier, "setting min interval to %d", (int)i );
1272            tier->announceMinIntervalSec = i;
1273        }
1274
1275        if( tr_bencDictFindStr( &benc, "tracker id", &str ) )
1276        {
1277            tier->currentTracker->tracker_id = tr_strdup( str );
1278        }
1279
1280        if( !tr_bencDictFindInt( &benc, "complete", &i ) )
1281            tier->currentTracker->seederCount = 0;
1282        else {
1283            ++scrapeFields;
1284            tier->currentTracker->seederCount = i;
1285        }
1286
1287        if( !tr_bencDictFindInt( &benc, "incomplete", &i ) )
1288            tier->currentTracker->leecherCount = 0;
1289        else {
1290            ++scrapeFields;
1291            tier->currentTracker->leecherCount = i;
1292        }
1293
1294        if( tr_bencDictFindInt( &benc, "downloaded", &i ) )
1295        {
1296            ++scrapeFields;
1297            tier->currentTracker->downloadCount = i;
1298        }
1299
1300        if( tr_bencDictFindRaw( &benc, "peers", &raw, &rawlen ) )
1301        {
1302            /* "compact" extension */
1303            const int seeders = tier->currentTracker->seederCount;
1304            const int leechers = tier->currentTracker->leecherCount;
1305            peerCount += publishNewPeersCompact( tier, seeders, leechers, raw, rawlen );
1306            gotPeers = TRUE;
1307        }
1308        else if( tr_bencDictFindList( &benc, "peers", &tmp ) )
1309        {
1310            /* original version of peers */
1311            const int seeders = tier->currentTracker->seederCount;
1312            const int leechers = tier->currentTracker->leecherCount;
1313            size_t byteCount = 0;
1314            uint8_t * array = parseOldPeers( tmp, &byteCount );
1315            peerCount += publishNewPeers( tier, seeders, leechers, array, byteCount );
1316            gotPeers = TRUE;
1317            tr_free( array );
1318        }
1319
1320        if( tr_bencDictFindRaw( &benc, "peers6", &raw, &rawlen ) )
1321        {
1322            /* "compact" extension */
1323            const int seeders = tier->currentTracker->seederCount;
1324            const int leechers = tier->currentTracker->leecherCount;
1325            peerCount += publishNewPeersCompact6( tier, seeders, leechers, raw, rawlen );
1326            gotPeers = TRUE;
1327        }
1328
1329        if( tier->lastAnnounceStr[0] == '\0' )
1330            tr_strlcpy( tier->lastAnnounceStr, _( "Success" ),
1331                        sizeof( tier->lastAnnounceStr ) );
1332
1333        if( gotPeers )
1334            tier->lastAnnouncePeerCount = peerCount;
1335    }
1336
1337    if( bencLoaded )
1338        tr_bencFree( &benc );
1339
1340    *gotScrape = scrapeFields >= 2;
1341
1342    return success;
1343}
1344
1345struct announce_data
1346{
1347    int torrentId;
1348    int tierId;
1349    time_t timeSent;
1350    const char * event;
1351
1352    /** If the request succeeds, the value for tier's "isRunning" flag */
1353    tr_bool isRunningOnSuccess;
1354};
1355
1356static void
1357onAnnounceDone( tr_session   * session,
1358                long           responseCode,
1359                const void   * response,
1360                size_t         responseLen,
1361                void         * vdata )
1362{
1363    tr_announcer * announcer = session->announcer;
1364    struct announce_data * data = vdata;
1365    tr_tier * tier = getTier( announcer, data->torrentId, data->tierId );
1366    tr_bool gotScrape = FALSE;
1367    tr_bool success = FALSE;
1368    const time_t now = time ( NULL );
1369    const char * announceEvent = data->event;
1370    const tr_bool isStopped = !strcmp( announceEvent, "stopped" );
1371
1372    if( announcer && tier )
1373    {
1374        if( tier->currentTracker->host )
1375        {
1376            tr_host * host = tier->currentTracker->host;
1377            host->lastRequestTime = data->timeSent;
1378            host->lastResponseInterval = now - data->timeSent;
1379        }
1380
1381        tier->lastAnnounceTime = now;
1382
1383        if( responseCode == HTTP_OK )
1384        {
1385            success = parseAnnounceResponse( tier, response, responseLen, &gotScrape );
1386            dbgmsg( tier, "success is %d", success );
1387
1388            if( isStopped )
1389            {
1390                /* now that we've successfully stopped the torrent,
1391                 * we can reset the up/down/corrupt count we've kept
1392                 * for this tracker */
1393                tier->byteCounts[ TR_ANN_UP ] = 0;
1394                tier->byteCounts[ TR_ANN_DOWN ] = 0;
1395                tier->byteCounts[ TR_ANN_CORRUPT ] = 0;
1396            }
1397        }
1398        else if( responseCode )
1399        {
1400            /* %1$ld - http status code, such as 404
1401             * %2$s - human-readable explanation of the http status code */
1402            char * buf = tr_strdup_printf(
1403                _( "tracker gave HTTP Response Code %1$ld (%2$s)" ),
1404                responseCode,
1405                tr_webGetResponseStr( responseCode ) );
1406
1407            tr_strlcpy( tier->lastAnnounceStr, buf,
1408                        sizeof( tier->lastAnnounceStr ) );
1409
1410            /* if the response is serious, *and* if the response may require
1411             * human intervention, then notify the user... otherwise just log it */
1412            if( responseCode >= 400 )
1413                if( tr_torrentIsPrivate( tier->tor ) || ( tier->tor->info.trackerCount < 2 ) )
1414                    publishWarning( tier, buf );
1415            tr_torinf( tier->tor, "%s", buf );
1416            dbgmsg( tier, "%s", buf );
1417
1418            tr_free( buf );
1419        }
1420        else
1421        {
1422            tr_strlcpy( tier->lastAnnounceStr,
1423                        _( "tracker did not respond" ),
1424                        sizeof( tier->lastAnnounceStr ) );
1425            dbgmsg( tier, "%s", tier->lastAnnounceStr );
1426        }
1427    }
1428
1429    if( tier )
1430    {
1431        tier->isAnnouncing = FALSE;
1432
1433        if( responseCode == 0 )
1434        {
1435            const int interval = getRetryInterval( tier->currentTracker->host );
1436            dbgmsg( tier, "No response from tracker... retrying in %d seconds.", interval );
1437            tier->manualAnnounceAllowedAt = ~(time_t)0;
1438            tierAddAnnounce( tier, announceEvent, now + interval );
1439        }
1440        else if( 200 <= responseCode && responseCode <= 299 )
1441        {
1442            const int interval = tier->announceIntervalSec;
1443            dbgmsg( tier, "request succeeded. reannouncing in %d seconds", interval );
1444
1445            if( gotScrape )
1446            {
1447                tier->lastScrapeTime = now;
1448                tier->lastScrapeSucceeded = 1;
1449                tier->scrapeAt = now + tier->scrapeIntervalSec;
1450            }
1451
1452            tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1453
1454            /* if we're running and the queue is empty, add the next update */
1455            if( !isStopped && !tr_ptrArraySize( &tier->announceEvents ) )
1456            {
1457                tierAddAnnounce( tier, "", now + interval );
1458            }
1459        }
1460        else if( 300 <= responseCode && responseCode <= 399 )
1461        {
1462            /* how did this get here?  libcurl handles this */
1463            const int interval = 5;
1464            dbgmsg( tier, "got a redirect. retrying in %d seconds", interval );
1465            tierAddAnnounce( tier, announceEvent, now + interval );
1466            tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec;
1467        }
1468        else if( ( responseCode == 404 ) || ( 500 <= responseCode && responseCode <= 599 ) )
1469        {
1470            /* 404: The requested resource could not be found but may be
1471             * available again in the future.  Subsequent requests by
1472             * the client are permissible. */
1473
1474            /* 5xx: indicate cases in which the server is aware that it
1475             * has erred or is incapable of performing the request.
1476             * So we pause a bit and try again. */
1477
1478            const int interval = getRetryInterval( tier->currentTracker->host );
1479            tier->manualAnnounceAllowedAt = ~(time_t)0;
1480            tierAddAnnounce( tier, announceEvent, now + interval );
1481        }
1482        else if( 400 <= responseCode && responseCode <= 499 )
1483        {
1484            /* The request could not be understood by the server due to
1485             * malformed syntax. The client SHOULD NOT repeat the
1486             * request without modifications. */
1487            if( tr_torrentIsPrivate( tier->tor ) || ( tier->tor->info.trackerCount < 2 ) )
1488                publishErrorMessageAndStop( tier, _( "Tracker returned a 4xx message" ) );
1489            tier->announceAt = 0;
1490            tier->manualAnnounceAllowedAt = ~(time_t)0;
1491        }
1492        else
1493        {
1494            /* WTF did we get?? */
1495            const int interval = 120;
1496            dbgmsg( tier, "Invalid response from tracker... retrying in two minutes." );
1497            tier->manualAnnounceAllowedAt = ~(time_t)0;
1498            tierAddAnnounce( tier, announceEvent, now + interval );
1499        }
1500
1501        tier->lastAnnounceSucceeded = success;
1502        tier->lastAnnounceTimedOut = responseCode == 0;
1503
1504        if( success )
1505        {
1506            tier->isRunning = data->isRunningOnSuccess;
1507
1508            if( tier->currentTracker->host )
1509                tier->currentTracker->host->lastSuccessfulRequest = now;
1510        }
1511        else if( responseCode != HTTP_OK )
1512        {
1513            tierIncrementTracker( tier );
1514
1515            tr_ptrArrayInsert( &tier->announceEvents, (void*)announceEvent, 0 );
1516        }
1517    }
1518
1519    if( announcer != NULL )
1520    {
1521        ++announcer->slotsAvailable;
1522    }
1523
1524    tr_free( data );
1525}
1526
1527static const char*
1528getNextAnnounceEvent( tr_tier * tier )
1529{
1530    int i, n;
1531    int pos = -1;
1532    tr_ptrArray tmp;
1533    const char ** events;
1534    const char * str = NULL;
1535
1536    assert( tier != NULL );
1537    assert( tr_isTorrent( tier->tor ) );
1538
1539    /* special case #1: if "stopped" is in the queue, ignore everything before it */
1540    events = (const char**) tr_ptrArrayPeek( &tier->announceEvents, &n );
1541    if( pos == -1 ) {
1542        for( i=0; i<n; ++i )
1543            if( !strcmp( events[i], "stopped" ) )
1544                break;
1545        if( i <  n )
1546            pos = i;
1547    }
1548
1549    /* special case #2: don't use empty strings if something follows them */
1550    if( pos == -1 ) {
1551        for( i = 0; i < n; ++i )
1552            if( *events[i] )
1553                break;
1554        if( i < n )
1555            pos = i;
1556    }
1557
1558    /* default: use the next in the queue */
1559    if( ( pos == -1 ) && ( n > 0 ) )
1560        pos = 0;
1561
1562    /* special case #3: if there are duplicate requests in a row, skip to the last one */
1563    if( pos >= 0 ) {
1564        for( i=pos+1; i<n; ++i )
1565            if( strcmp( events[pos], events[i] ) )
1566                break;
1567        pos = i - 1;
1568    }
1569
1570    /* special case #4: BEP 21: "In order to tell the tracker that a peer is a
1571     * partial seed, it MUST send an event=paused parameter in every
1572     * announce while it is a partial seed." */
1573    str = pos>=0 ? events[pos] : NULL;
1574    if( tr_cpGetStatus( &tier->tor->completion ) == TR_PARTIAL_SEED )
1575        if( !str || strcmp( str, "stopped" ) )
1576            str = "paused";
1577
1578#if 0
1579for( i=0; i<n; ++i ) fprintf( stderr, "(%d)\"%s\" ", i, events[i] );
1580fprintf( stderr, "\n" );
1581fprintf( stderr, "using (%d)\"%s\"\n", pos, events[pos] );
1582if( strcmp( events[pos], str ) ) fprintf( stderr, "...but really using [%s]\n", str );
1583#endif
1584
1585    /* announceEvents array upkeep */
1586    tmp = TR_PTR_ARRAY_INIT;
1587    for( i=pos+1; i<n; ++i )
1588        tr_ptrArrayAppend( &tmp, (void*)events[i] );
1589    tr_ptrArrayDestruct( &tier->announceEvents, NULL );
1590    tier->announceEvents = tmp;
1591
1592    return str;
1593}
1594
1595static void
1596tierAnnounce( tr_announcer * announcer, tr_tier * tier )
1597{
1598    const char * announceEvent = getNextAnnounceEvent( tier );
1599
1600    assert( !tier->isAnnouncing );
1601
1602    if( announceEvent != NULL )
1603    {
1604        char * url;
1605        struct announce_data * data;
1606        const tr_torrent * tor = tier->tor;
1607        const time_t now = tr_time( );
1608
1609        data = tr_new0( struct announce_data, 1 );
1610        data->torrentId = tr_torrentId( tor );
1611        data->tierId = tier->key;
1612        data->isRunningOnSuccess = tor->isRunning;
1613        data->timeSent = now;
1614        data->event = announceEvent;
1615        url = createAnnounceURL( announcer, tor, tier, data->event );
1616
1617        tier->isAnnouncing = TRUE;
1618        tier->lastAnnounceStartTime = now;
1619        --announcer->slotsAvailable;
1620        tr_webRun( announcer->session, url, NULL, onAnnounceDone, data );
1621
1622        tr_free( url );
1623    }
1624}
1625
1626static tr_bool
1627parseScrapeResponse( tr_tier     * tier,
1628                     const char  * response,
1629                     size_t        responseLen,
1630                     char        * result,
1631                     size_t        resultlen )
1632{
1633    tr_bool success = FALSE;
1634    tr_benc benc, *files;
1635    const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, NULL );
1636    if( bencLoaded && tr_bencDictFindDict( &benc, "files", &files ) )
1637    {
1638        const char * key;
1639        tr_benc * val;
1640        int i = 0;
1641        while( tr_bencDictChild( files, i++, &key, &val ))
1642        {
1643            int64_t intVal;
1644            tr_benc * flags;
1645
1646            if( memcmp( tier->tor->info.hash, key, SHA_DIGEST_LENGTH ) )
1647                continue;
1648
1649            success = TRUE;
1650            publishErrorClear( tier );
1651
1652            if( ( tr_bencDictFindInt( val, "complete", &intVal ) ) )
1653                tier->currentTracker->seederCount = intVal;
1654
1655            if( ( tr_bencDictFindInt( val, "incomplete", &intVal ) ) )
1656                tier->currentTracker->leecherCount = intVal;
1657
1658            if( ( tr_bencDictFindInt( val, "downloaded", &intVal ) ) )
1659                tier->currentTracker->downloadCount = intVal;
1660
1661            if( ( tr_bencDictFindInt( val, "downloaders", &intVal ) ) )
1662                tier->currentTracker->downloaderCount = intVal;
1663
1664            if( tr_bencDictFindDict( val, "flags", &flags ) )
1665                if( ( tr_bencDictFindInt( flags, "min_request_interval", &intVal ) ) )
1666                    tier->scrapeIntervalSec = MAX( DEFAULT_SCRAPE_INTERVAL_SEC, (int)intVal );
1667
1668            tr_tordbg( tier->tor,
1669                       "Scrape successful. Rescraping in %d seconds.",
1670                       tier->scrapeIntervalSec );
1671        }
1672    }
1673
1674    if( bencLoaded )
1675        tr_bencFree( &benc );
1676
1677    if( success )
1678        tr_strlcpy( result, _( "Success" ), resultlen );
1679    else
1680        tr_strlcpy( result, _( "Error parsing response" ), resultlen );
1681
1682    return success;
1683}
1684
1685static void
1686onScrapeDone( tr_session   * session,
1687              long           responseCode,
1688              const void   * response,
1689              size_t         responseLen,
1690              void         * vdata )
1691{
1692    tr_bool success = FALSE;
1693    tr_announcer * announcer = session->announcer;
1694    struct announce_data * data = vdata;
1695    tr_tier * tier = getTier( announcer, data->torrentId, data->tierId );
1696    const time_t now = tr_time( );
1697
1698    if( announcer )
1699        ++announcer->slotsAvailable;
1700
1701    if( announcer && tier )
1702    {
1703        tier->isScraping = FALSE;
1704        tier->lastScrapeTime = now;
1705
1706        if( tier->currentTracker->host )
1707        {
1708            tr_host * host = tier->currentTracker->host;
1709            host->lastRequestTime = data->timeSent;
1710            host->lastResponseInterval = now - data->timeSent;
1711        }
1712
1713        if( 200 <= responseCode && responseCode <= 299 )
1714        {
1715            const int interval = tier->scrapeIntervalSec;
1716            tier->scrapeAt = now + interval;
1717
1718            if( responseCode == HTTP_OK )
1719                success = parseScrapeResponse( tier, response, responseLen,
1720                                               tier->lastScrapeStr, sizeof( tier->lastScrapeStr ) );
1721            else
1722                tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1723                             _( "tracker gave HTTP Response Code %1$ld (%2$s)" ),
1724                             responseCode, tr_webGetResponseStr( responseCode ) );
1725            tr_tordbg( tier->tor, "%s", tier->lastScrapeStr );
1726        }
1727        else if( 300 <= responseCode && responseCode <= 399 )
1728        {
1729            /* this shouldn't happen; libcurl should handle this */
1730            const int interval = 5;
1731            tier->scrapeAt = now + interval;
1732            tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1733                         "Got a redirect. Retrying in %d seconds", interval );
1734            tr_tordbg( tier->tor, "%s", tier->lastScrapeStr );
1735        }
1736        else
1737        {
1738            const int interval = getRetryInterval( tier->currentTracker->host );
1739
1740            /* Don't retry on a 4xx.
1741             * Retry at growing intervals on a 5xx */
1742            if( 400 <= responseCode && responseCode <= 499 )
1743                tier->scrapeAt = 0;
1744            else
1745                tier->scrapeAt = now + interval;
1746
1747            /* %1$ld - http status code, such as 404
1748             * %2$s - human-readable explanation of the http status code */
1749            if( !responseCode )
1750                tr_strlcpy( tier->lastScrapeStr, _( "tracker did not respond" ),
1751                            sizeof( tier->lastScrapeStr ) );
1752            else
1753                tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
1754                             _( "tracker gave HTTP Response Code %1$ld (%2$s)" ),
1755                             responseCode, tr_webGetResponseStr( responseCode ) );
1756        }
1757
1758        tier->lastScrapeSucceeded = success;
1759        tier->lastScrapeTimedOut = responseCode == 0;
1760
1761        if( success && tier->currentTracker->host )
1762            tier->currentTracker->host->lastSuccessfulRequest = now;
1763    }
1764
1765    tr_free( data );
1766}
1767
1768static void
1769tierScrape( tr_announcer * announcer, tr_tier * tier )
1770{
1771    char * url;
1772    const char * scrape;
1773    struct announce_data * data;
1774    const time_t now = tr_time( );
1775
1776    assert( tier );
1777    assert( !tier->isScraping );
1778    assert( tier->currentTracker != NULL );
1779    assert( tr_isTorrent( tier->tor ) );
1780
1781    data = tr_new0( struct announce_data, 1 );
1782    data->torrentId = tr_torrentId( tier->tor );
1783    data->tierId = tier->key;
1784
1785    scrape = tier->currentTracker->scrape;
1786
1787    url = tr_strdup_printf( "%s%cinfo_hash=%s",
1788                            scrape,
1789                            strchr( scrape, '?' ) ? '&' : '?',
1790                            tier->tor->info.hashEscaped );
1791
1792    tier->isScraping = TRUE;
1793    tier->lastScrapeStartTime = now;
1794    --announcer->slotsAvailable;
1795    dbgmsg( tier, "scraping \"%s\"", url );
1796    tr_webRun( announcer->session, url, NULL, onScrapeDone, data );
1797
1798    tr_free( url );
1799}
1800
1801static void
1802flushCloseMessages( tr_announcer * announcer )
1803{
1804    int i;
1805    const int n = tr_ptrArraySize( &announcer->stops );
1806
1807    for( i=0; i<n; ++i )
1808    {
1809        struct stop_message * stop = tr_ptrArrayNth( &announcer->stops, i );
1810        tr_webRun( announcer->session, stop->url, NULL, NULL, NULL );
1811        stopFree( stop );
1812    }
1813
1814    tr_ptrArrayClear( &announcer->stops );
1815}
1816
1817static tr_bool
1818tierNeedsToAnnounce( const tr_tier * tier, const time_t now )
1819{
1820    return !tier->isAnnouncing
1821        && !tier->isScraping
1822        && ( tier->announceAt != 0 )
1823        && ( tier->announceAt <= now )
1824        && ( tr_ptrArraySize( &tier->announceEvents ) != 0 );
1825}
1826
1827static tr_bool
1828tierNeedsToScrape( const tr_tier * tier, const time_t now )
1829{
1830    return ( !tier->isScraping )
1831        && ( tier->scrapeAt != 0 )
1832        && ( tier->scrapeAt <= now )
1833        && ( tier->currentTracker != NULL )
1834        && ( tier->currentTracker->scrape != NULL );
1835}
1836
1837static void
1838announceMore( tr_announcer * announcer )
1839{
1840    tr_torrent * tor = NULL;
1841    const time_t now = tr_time( );
1842
1843    if( announcer->slotsAvailable > 0 )
1844    {
1845        int i;
1846        int n;
1847        tr_ptrArray announceMe = TR_PTR_ARRAY_INIT;
1848        tr_ptrArray scrapeMe = TR_PTR_ARRAY_INIT;
1849
1850        /* build a list of tiers that need to be announced */
1851        while(( tor = tr_torrentNext( announcer->session, tor ))) {
1852            if( tor->tiers ) {
1853                n = tr_ptrArraySize( &tor->tiers->tiers );
1854                for( i=0; i<n; ++i ) {
1855                    tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
1856                    if( tierNeedsToAnnounce( tier, now ) )
1857                        tr_ptrArrayAppend( &announceMe, tier );
1858                    else if( tierNeedsToScrape( tier, now ) )
1859                        tr_ptrArrayAppend( &scrapeMe, tier );
1860                }
1861            }
1862        }
1863
1864        /* if there are more tiers than slots available, prioritize */
1865        n = tr_ptrArraySize( &announceMe );
1866        if( n > announcer->slotsAvailable )
1867            qsort( tr_ptrArrayBase( &announceMe ), n, sizeof( tr_tier * ), compareTiers );
1868
1869        /* announce some */
1870        n = MIN( tr_ptrArraySize( &announceMe ), announcer->slotsAvailable );
1871        for( i=0; i<n; ++i ) {
1872            tr_tier * tier = tr_ptrArrayNth( &announceMe, i );
1873            dbgmsg( tier, "announcing tier %d of %d", i, n );
1874            tierAnnounce( announcer, tier );
1875        }
1876
1877        /* scrape some */
1878        n = MIN( tr_ptrArraySize( &scrapeMe ), announcer->slotsAvailable );
1879        for( i=0; i<n; ++i ) {
1880            tr_tier * tier = tr_ptrArrayNth( &scrapeMe, i );
1881            dbgmsg( tier, "scraping tier %d of %d", (i+1), n );
1882            tierScrape( announcer, tier );
1883        }
1884
1885#if 0
1886char timebuf[64];
1887tr_getLogTimeStr( timebuf, 64 );
1888fprintf( stderr, "[%s] announce.c has %d requests ready to send (announce: %d, scrape: %d)\n", timebuf, (int)(tr_ptrArraySize(&announceMe)+tr_ptrArraySize(&scrapeMe)), (int)tr_ptrArraySize(&announceMe), (int)tr_ptrArraySize(&scrapeMe) );
1889#endif
1890
1891        /* cleanup */
1892        tr_ptrArrayDestruct( &scrapeMe, NULL );
1893        tr_ptrArrayDestruct( &announceMe, NULL );
1894    }
1895
1896    tor = NULL;
1897    while(( tor = tr_torrentNext( announcer->session, tor ))) {
1898        if( tor->dhtAnnounceAt <= now ) {
1899            if( tor->isRunning && tr_torrentAllowsDHT(tor) ) {
1900                int rc;
1901                rc = tr_dhtAnnounce(tor, AF_INET, 1);
1902                if(rc == 0)
1903                    /* The DHT is not ready yet.  Try again soon. */
1904                    tor->dhtAnnounceAt = now + 5 + tr_cryptoWeakRandInt( 5 );
1905                else
1906                    /* We should announce at least once every 30 minutes. */
1907                    tor->dhtAnnounceAt =
1908                        now + 25 * 60 + tr_cryptoWeakRandInt( 3 * 60 );
1909            }
1910        }
1911
1912        if( tor->dhtAnnounce6At <= now ) {
1913            if( tor->isRunning && tr_torrentAllowsDHT(tor) ) {
1914                int rc;
1915                rc = tr_dhtAnnounce(tor, AF_INET6, 1);
1916                if(rc == 0)
1917                    tor->dhtAnnounce6At = now + 5 + tr_cryptoWeakRandInt( 5 );
1918                else
1919                    tor->dhtAnnounce6At =
1920                        now + 25 * 60 + tr_cryptoWeakRandInt( 3 * 60 );
1921            }
1922        }
1923    }
1924
1925    /* Local Peer Discovery */
1926    if( announcer->lpdHouseKeepingAt <= now )
1927    {
1928        tr_lpdAnnounceMore( now, LPD_HOUSEKEEPING_INTERVAL_SECS );
1929
1930        /* reschedule more LDS announces for ( the future + jitter ) */
1931        announcer->lpdHouseKeepingAt =
1932            calcRescheduleWithJitter( LPD_HOUSEKEEPING_INTERVAL_SECS );
1933    }
1934}
1935
1936static void
1937onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer )
1938{
1939    tr_announcer * announcer = vannouncer;
1940    tr_sessionLock( announcer->session );
1941
1942    /* maybe send out some "stopped" messages for closed torrents */
1943    flushCloseMessages( announcer );
1944
1945    /* maybe send out some announcements to trackers */
1946    announceMore( announcer );
1947
1948    /* set up the next timer */
1949    tr_timerAdd( announcer->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 );
1950
1951    tr_sessionUnlock( announcer->session );
1952}
1953
1954/***
1955****
1956***/
1957
1958tr_tracker_stat *
1959tr_announcerStats( const tr_torrent * torrent,
1960                   int              * setmeTrackerCount )
1961{
1962    int i;
1963    int n;
1964    int out = 0;
1965    int tierCount;
1966    tr_tracker_stat * ret;
1967    const time_t now = tr_time( );
1968
1969    assert( tr_isTorrent( torrent ) );
1970
1971    /* count the trackers... */
1972    for( i=n=0, tierCount=tr_ptrArraySize( &torrent->tiers->tiers ); i<tierCount; ++i ) {
1973        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1974        n += tr_ptrArraySize( &tier->trackers );
1975    }
1976
1977    /* alloc the stats */
1978    *setmeTrackerCount = n;
1979    ret = tr_new0( tr_tracker_stat, n );
1980
1981    /* populate the stats */
1982    for( i=0, tierCount=tr_ptrArraySize( &torrent->tiers->tiers ); i<tierCount; ++i )
1983    {
1984        int j;
1985        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
1986        n = tr_ptrArraySize( &tier->trackers );
1987        for( j=0; j<n; ++j )
1988        {
1989            const tr_tracker_item * tracker = tr_ptrArrayNth( (tr_ptrArray*)&tier->trackers, j );
1990            tr_tracker_stat * st = ret + out++;
1991
1992            st->id = tracker->id;
1993            tr_strlcpy( st->host, tracker->host->name, sizeof( st->host ) );
1994            tr_strlcpy( st->announce, tracker->announce, sizeof( st->announce ) );
1995            st->tier = i;
1996            st->isBackup = tracker != tier->currentTracker;
1997            st->lastScrapeStartTime = tier->lastScrapeStartTime;
1998
1999            st->seederCount = tracker->seederCount;
2000            st->leecherCount = tracker->leecherCount;
2001            st->downloadCount = tracker->downloadCount;
2002
2003            if( st->isBackup )
2004            {
2005                st->scrapeState = TR_TRACKER_INACTIVE;
2006                st->announceState = TR_TRACKER_INACTIVE;
2007                st->nextScrapeTime = 0;
2008                st->nextAnnounceTime = 0;
2009            }
2010            else
2011            {
2012                if(( st->hasScraped = tier->lastScrapeTime != 0 )) {
2013                    st->lastScrapeTime = tier->lastScrapeTime;
2014                    st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
2015                    st->lastScrapeTimedOut = tier->lastScrapeTimedOut;
2016                    tr_strlcpy( st->lastScrapeResult, tier->lastScrapeStr, sizeof( st->lastScrapeResult ) );
2017                }
2018
2019                if( tier->isScraping )
2020                    st->scrapeState = TR_TRACKER_ACTIVE;
2021                else if( !tier->scrapeAt )
2022                    st->scrapeState = TR_TRACKER_INACTIVE;
2023                else if( tier->scrapeAt > now )
2024                {
2025                    st->scrapeState = TR_TRACKER_WAITING;
2026                    st->nextScrapeTime = tier->scrapeAt;
2027                }
2028                else
2029                    st->scrapeState = TR_TRACKER_QUEUED;
2030
2031                st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
2032
2033                if(( st->hasAnnounced = tier->lastAnnounceTime != 0 )) {
2034                    st->lastAnnounceTime = tier->lastAnnounceTime;
2035                    tr_strlcpy( st->lastAnnounceResult, tier->lastAnnounceStr, sizeof( st->lastAnnounceResult ) );
2036                    st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded;
2037                    st->lastAnnounceTimedOut = tier->lastAnnounceTimedOut;
2038                    st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
2039                }
2040
2041                if( tier->isAnnouncing )
2042                    st->announceState = TR_TRACKER_ACTIVE;
2043                else if( !torrent->isRunning || !tier->announceAt )
2044                    st->announceState = TR_TRACKER_INACTIVE;
2045                else if( tier->announceAt > now )
2046                {
2047                    st->announceState = TR_TRACKER_WAITING;
2048                    st->nextAnnounceTime = tier->announceAt;
2049                }
2050                else
2051                    st->announceState = TR_TRACKER_QUEUED;
2052            }
2053        }
2054    }
2055
2056    return ret;
2057}
2058
2059void
2060tr_announcerStatsFree( tr_tracker_stat * trackers,
2061                       int trackerCount UNUSED )
2062{
2063    tr_free( trackers );
2064}
Note: See TracBrowser for help on using the repository browser.