source: trunk/libtransmission/announcer.c @ 10155

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

(trunk libT) try to fix the mac build. This commit still has the announce logging code.

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