source: trunk/libtransmission/announcer.c @ 10150

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

(trunk libT) fix 1.82 memory corruption error reported by john doe

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