source: trunk/libtransmission/announcer.c @ 10165

Last change on this file since 10165 was 10165, checked in by livings124, 12 years ago

chop off a bit of text in an announce error message

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