source: trunk/libtransmission/announcer.c @ 10141

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

(trunk) per-tier up/down/corrupt byte counts for announcing to trackers

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