source: trunk/libtransmission/tracker.c @ 3457

Last change on this file since 3457 was 3457, checked in by charles, 15 years ago

fix overflow error that caused timers with intervals larger than 35 minutes to be lost... which is nastier than it sounds, since that's shorter than many tracker announce intervals.

  • Property svn:keywords set to Date Rev Author Id
File size: 28.1 KB
Line 
1/*
2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
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: tracker.c 3457 2007-10-18 18:32:58Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isalnum */
15#include <stdio.h> /* snprintf */
16#include <stdlib.h>
17#include <string.h> /* strcmp, strchr */
18
19#include <sys/queue.h> /* libevent needs this */
20#include <sys/types.h> /* libevent needs this */
21#include <event.h>
22#include <evhttp.h>
23
24#include "transmission.h"
25#include "bencode.h"
26#include "completion.h"
27#include "net.h"
28#include "publish.h"
29#include "shared.h"
30#include "tracker.h"
31#include "trevent.h"
32#include "utils.h"
33
34enum
35{
36    /* unless the tracker says otherwise, rescrape this frequently */
37    DEFAULT_SCRAPE_INTERVAL_SEC = (60 * 15),
38
39    /* unless the tracker says otherwise, this is the announce interval */
40    DEFAULT_ANNOUNCE_INTERVAL_SEC = (60 * 4),
41
42    /* unless the tracker says otherwise, this is the announce min_interval */
43    DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = (60 * 2),
44
45    /* this is how long we'll leave a request hanging before timeout */
46    TIMEOUT_INTERVAL_SEC = 45,
47
48    /* this is how long we'll leave a 'stop' request hanging before timeout.
49       we wait less time for this so it doesn't slow down shutdowns */
50    STOP_TIMEOUT_INTERVAL_SEC = 5,
51
52    /* the value of the 'numwant' argument passed in tracker requests. */
53    NUMWANT = 200,
54
55    /* the length of the 'key' argument passed in tracker requests */
56    KEYLEN = 10
57};
58
59/**
60***
61**/
62
63struct tr_tracker
64{
65    tr_handle * handle;
66
67    /* these are set from the latest scrape or tracker response */
68    int announceIntervalSec;
69    int announceMinIntervalSec;
70    int scrapeIntervalSec;
71
72    tr_tracker_info * redirect;
73    tr_tracker_info * addresses;
74    int addressIndex;
75    int addressCount;
76    int * tierFronts;
77
78    /* sent as the "key" argument in tracker requests
79       to verify us if our IP address changes.
80       This is immutable for the life of the tracker object. */
81    char key_param[KEYLEN+1];
82
83    tr_publisher_t * publisher;
84
85    /* torrent hash string */
86    uint8_t hash[SHA_DIGEST_LENGTH];
87    char escaped[SHA_DIGEST_LENGTH * 3 + 1];
88    char * name;
89
90    /* corresponds to the peer_id sent as a tracker request parameter.
91       OiNK's op TooMuchTime says: "When the same torrent is opened and
92       closed and opened again without quitting Transmission ...
93       change the peerid. It would help sometimes if a stopped event
94       was missed to ensure that we didn't think someone was cheating. */
95    char peer_id[TR_ID_LEN + 1];
96
97    /* these are set from the latest tracker response... -1 is 'unknown' */
98    int timesDownloaded;
99    int seederCount;
100    int leecherCount;
101    char * trackerID;
102
103    /* the last tracker request we sent. (started, stopped, etc.)
104       automatic announces are an empty string;
105       NULL means no message has ever been sent */
106    char * lastRequest;
107
108    time_t manualAnnounceAllowedAt;
109
110    tr_timer * scrapeTimer;
111    tr_timer * reannounceTimer;
112
113    unsigned int isRunning : 1;
114};
115
116/**
117***
118**/
119
120static void
121myDebug( const char * file, int line, const tr_tracker * t, const char * fmt, ... )
122{   
123    FILE * fp = tr_getLog( );
124    if( fp != NULL )
125    {
126        va_list args;
127        struct evbuffer * buf = evbuffer_new( );
128        char timestr[64];
129        evbuffer_add_printf( buf, "[%s] ", tr_getLogTimeStr( timestr, sizeof(timestr) ) );
130        if( t != NULL )
131            evbuffer_add_printf( buf, "%s ", t->name );
132        va_start( args, fmt );
133        evbuffer_add_vprintf( buf, fmt, args );
134        va_end( args );
135        evbuffer_add_printf( buf, " (%s:%d)\n", file, line );
136
137        fwrite( EVBUFFER_DATA(buf), 1, EVBUFFER_LENGTH(buf), fp );
138        evbuffer_free( buf );
139    }
140}
141
142#define dbgmsg(t, fmt...) myDebug(__FILE__, __LINE__, t, ##fmt )
143
144
145/***
146****  Connections that know how to clean up after themselves
147***/
148
149static int
150freeConnection( void * evcon )
151{
152    evhttp_connection_free( evcon );
153    return FALSE;
154}
155
156static void
157connectionClosedCB( struct evhttp_connection * evcon, void * handle )
158{
159    /* libevent references evcon right after calling this function,
160       so we can't free it yet... defer it to after this call chain
161       has played out */
162    tr_timerNew( handle, freeConnection, evcon, 100 );
163}
164
165static struct evhttp_connection*
166getConnection( tr_tracker * t, const char * address, int port )
167{
168    struct evhttp_connection * c = evhttp_connection_new( address, port );
169    evhttp_connection_set_closecb( c, connectionClosedCB, t->handle );
170    return c;
171}
172
173/***
174****  PUBLISH
175***/
176
177static const tr_tracker_event emptyEvent = { 0, NULL, NULL, NULL, 0 };
178
179static void
180publishMessage( tr_tracker * t, const char * msg, int type )
181{
182    tr_tracker_event event = emptyEvent;
183    event.hash = t->hash;
184    event.messageType = type;
185    event.text = msg;
186    tr_publisherPublish( t->publisher, t, &event );
187}
188
189static void
190publishErrorClear( tr_tracker * t )
191{
192    publishMessage( t, NULL, TR_TRACKER_ERROR_CLEAR );
193}
194
195static void
196publishErrorMessage( tr_tracker * t, const char * msg )
197{
198    publishMessage( t, msg, TR_TRACKER_ERROR );
199}
200
201static void
202publishWarning( tr_tracker * t, const char * msg )
203{
204    publishMessage( t, msg, TR_TRACKER_WARNING );
205}
206
207static void
208publishNewPeers( tr_tracker * t, int count, uint8_t * peers )
209{
210    tr_tracker_event event = emptyEvent;
211    event.hash = t->hash;
212    event.messageType = TR_TRACKER_PEERS;
213    event.peerCount = count;
214    event.peerCompact = peers;
215    tr_inf( "Torrent \"%s\" got %d new peers", t->name, count );
216    tr_publisherPublish( t->publisher, t, &event );
217}
218
219/***
220****  LIFE CYCLE
221***/
222
223static void
224generateKeyParam( char * msg, int len )
225{
226    int i;
227    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
228    const int poolSize = strlen( pool );
229    for( i=0; i<len; ++i )
230        *msg++ = pool[tr_rand(poolSize)];
231    *msg = '\0';
232}
233
234static void
235escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
236{
237    const uint8_t *end = in + in_len;
238    while( in != end )
239        if( isalnum(*in) )
240            *out++ = (char) *in++;
241        else 
242            out += snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
243    *out = '\0';
244}
245
246static void scrapeNow( tr_tracker * );
247
248tr_tracker *
249tr_trackerNew( const tr_torrent * torrent )
250{
251    const tr_info * info = &torrent->info;
252    int i, j, sum, *iwalk;
253    tr_tracker_info * nwalk;
254    tr_tracker * t;
255
256    tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress );
257
258    t = tr_new0( tr_tracker, 1 );
259    t->handle = torrent->handle;
260    t->scrapeIntervalSec       = DEFAULT_SCRAPE_INTERVAL_SEC;
261    t->announceIntervalSec     = DEFAULT_ANNOUNCE_INTERVAL_SEC;
262    t->announceMinIntervalSec  = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
263    generateKeyParam( t->key_param, KEYLEN );
264
265    t->publisher = tr_publisherNew( );
266    t->timesDownloaded = -1;
267    t->seederCount = -1;
268    t->leecherCount = -1;
269    t->manualAnnounceAllowedAt = ~(time_t)0;
270    t->name = tr_strdup( info->name );
271    memcpy( t->hash, info->hash, SHA_DIGEST_LENGTH );
272    escape( t->escaped, info->hash, SHA_DIGEST_LENGTH );
273
274    for( sum=i=0; i<info->trackerTiers; ++i )
275         sum += info->trackerList[i].count;
276    t->addresses = nwalk = tr_new0( tr_tracker_info, sum );
277    t->addressIndex = 0;
278    t->addressCount = sum;
279    t->tierFronts = iwalk = tr_new0( int, sum );
280
281    for( i=0; i<info->trackerTiers; ++i )
282    {
283        const int tierFront = nwalk - t->addresses;
284
285        for( j=0; j<info->trackerList[i].count; ++j )
286        {
287            const tr_tracker_info * src = &info->trackerList[i].list[j];
288            nwalk->address = tr_strdup( src->address );
289            nwalk->port = src->port;
290            nwalk->announce = tr_strdup( src->announce );
291            nwalk->scrape = tr_strdup( src->scrape );
292            ++nwalk;
293
294            *iwalk++ = tierFront;
295        }
296    }
297
298    assert( nwalk - t->addresses == sum );
299    assert( iwalk - t->tierFronts == sum );
300
301    scrapeNow( t );
302    return t;
303}
304
305static void
306onTrackerFreeNow( void * vt )
307{
308    int i;
309    tr_tracker * t = vt;
310
311    tr_timerFree( &t->scrapeTimer );
312    tr_timerFree( &t->reannounceTimer );
313    tr_publisherFree( &t->publisher );
314    tr_free( t->name );
315    tr_free( t->trackerID );
316    tr_free( t->lastRequest );
317
318    /* addresses... */
319    for( i=0; i<t->addressCount; ++i )
320        tr_trackerInfoClear( &t->addresses[i] );
321    tr_free( t->addresses );
322    tr_free( t->tierFronts );
323
324    /* redirect... */
325    if( t->redirect ) {
326        tr_trackerInfoClear( t->redirect );
327        tr_free( t->redirect );
328    }
329
330    tr_free( t );
331}
332
333void
334tr_trackerFree( tr_tracker * t )
335{
336    tr_runInEventThread( t->handle, onTrackerFreeNow, t );
337}
338
339/***
340****  UTIL
341***/
342
343static int
344parseBencResponse( struct evhttp_request * req, benc_val_t * setme )
345{
346    const unsigned char * body = EVBUFFER_DATA( req->input_buffer );
347    const int bodylen = EVBUFFER_LENGTH( req->input_buffer );
348    int ret = 1;
349    int i;
350
351    for( i=0; ret && i<bodylen; ++i )
352        if( !tr_bencLoad( body+i, bodylen-1, setme, NULL ) )
353            ret = 0;
354
355    return ret;
356}
357
358static const char*
359updateAddresses( tr_tracker * t, const struct evhttp_request * req )
360{
361    const char * ret = NULL;
362    int moveToNextAddress = FALSE;
363
364    if( !req )
365    {
366        ret = "No response from tracker -- will keep trying.";
367        tr_inf( ret );
368
369        moveToNextAddress = TRUE;
370    }
371    else if( req->response_code == HTTP_OK )
372    {
373        if( t->redirect != NULL )
374        {
375            /* multitracker spec: "if a connection with a tracker is
376               successful, it will be moved to the front of the tier." */
377            const int i = t->addressIndex;
378            const int j = t->tierFronts[i];
379            const tr_tracker_info swap = t->addresses[i];
380            t->addresses[i] = t->addresses[j];
381            t->addresses[j] = swap;
382        }
383    }
384    else if(    ( req->response_code == HTTP_MOVEPERM )
385             || ( req->response_code == HTTP_MOVETEMP ) )
386    {
387        const char * loc = evhttp_find_header( req->input_headers, "Location" );
388        tr_tracker_info tmp;
389        if( tr_trackerInfoInit( &tmp, loc, -1 ) ) /* a bad redirect? */
390        {
391            moveToNextAddress = TRUE;
392        }
393        else if( req->response_code == HTTP_MOVEPERM )
394        {
395            tr_tracker_info * cur = &t->addresses[t->addressIndex];
396            tr_trackerInfoClear( cur );
397            *cur = tmp;
398        }
399        else if( req->response_code == HTTP_MOVETEMP )
400        {
401            if( t->redirect == NULL )
402                t->redirect = tr_new0( tr_tracker_info, 1 );
403            else
404                tr_trackerInfoClear( t->redirect );
405            *t->redirect = tmp;
406        }
407    }
408    else 
409    {
410        ret = "No response from tracker -- will keep trying.";
411        moveToNextAddress = TRUE;
412    }
413
414    if( moveToNextAddress )
415        if ( ++t->addressIndex >= t->addressCount )
416            t->addressIndex = 0;
417
418    return ret;
419}
420
421static tr_tracker_info *
422getCurrentAddress( const tr_tracker * t )
423{
424    assert( t->addresses != NULL );
425    assert( t->addressIndex >= 0 );
426    assert( t->addressIndex < t->addressCount );
427
428    return &t->addresses[t->addressIndex];
429}
430static int
431trackerSupportsScrape( const tr_tracker * t )
432{
433    const tr_tracker_info * info = getCurrentAddress( t );
434
435    return ( info != NULL )
436        && ( info->scrape != NULL )
437        && ( info->scrape[0] != '\0' );
438}
439
440
441static void
442addCommonHeaders( const tr_tracker * t,
443                  struct evhttp_request * req )
444{
445    char buf[1024];
446    const tr_tracker_info * address = getCurrentAddress( t );
447    snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
448    evhttp_add_header( req->output_headers, "Host", buf );
449    evhttp_add_header( req->output_headers, "Connection", "close" );
450    evhttp_add_header( req->output_headers, "Content-Length", "0" );
451    evhttp_add_header( req->output_headers, "User-Agent",
452                                         TR_NAME "/" LONG_VERSION_STRING );
453}
454
455/**
456***
457**/
458
459struct torrent_hash
460{
461    tr_handle * handle;
462    uint8_t hash[SHA_DIGEST_LENGTH];
463};
464
465static struct torrent_hash*
466torrentHashNew( tr_tracker * t )
467{
468    struct torrent_hash * data = tr_new( struct torrent_hash, 1 );
469    data->handle = t->handle;
470    memcpy( data->hash, t->hash, SHA_DIGEST_LENGTH );
471    return data;
472}
473
474tr_tracker *
475findTrackerFromHash( struct torrent_hash * data )
476{
477    tr_torrent * torrent = tr_torrentFindFromHash( data->handle, data->hash );
478    return torrent ? torrent->tracker : NULL;
479}
480
481/***
482****
483****  SCRAPE
484****
485***/
486
487static int
488onScrapeNow( void * vt );
489
490static void
491onScrapeResponse( struct evhttp_request * req, void * vhash )
492{
493    const char * warning;
494    time_t nextScrapeSec = 60;
495    tr_tracker * t;
496
497    t = findTrackerFromHash( vhash );
498    tr_free( vhash );
499    if( t == NULL ) /* tracker's been closed... */
500        return;
501
502    tr_inf( "Got scrape response for  '%s': %s",
503            t->name,
504            ( ( req && req->response_code_line ) ? req->response_code_line
505                                                 : "(null)") );
506
507    if( req && ( req->response_code == HTTP_OK ) )
508    {
509        benc_val_t benc, *files;
510        const int bencLoaded = !parseBencResponse( req, &benc );
511
512        if( bencLoaded
513            && (( files = tr_bencDictFind( &benc, "files" ) ))
514            && ( files->type == TYPE_DICT ) )
515        {
516            int i;
517            for( i=0; i<files->val.l.count; i+=2 )
518            {
519                const uint8_t* hash =
520                        (const uint8_t*) files->val.l.vals[i].val.s.s;
521                benc_val_t *tmp, *flags;
522                benc_val_t *tordict = &files->val.l.vals[i+1];
523                if( memcmp( t->hash, hash, SHA_DIGEST_LENGTH ) )
524                    continue;
525
526                publishErrorClear( t );
527
528                if(( tmp = tr_bencDictFind( tordict, "complete" )))
529                    t->seederCount = tmp->val.i;
530
531                if(( tmp = tr_bencDictFind( tordict, "incomplete" )))
532                    t->leecherCount = tmp->val.i;
533
534                if(( tmp = tr_bencDictFind( tordict, "downloaded" )))
535                    t->timesDownloaded = tmp->val.i;
536
537                if(( flags = tr_bencDictFind( tordict, "flags" )))
538                    if(( tmp = tr_bencDictFind( flags, "min_request_interval")))
539                        t->scrapeIntervalSec = tmp->val.i;
540
541                tr_dbg( "Torrent '%s' scrape successful."
542                        "  Rescraping in %d seconds",
543                        t->name, t->scrapeIntervalSec );
544
545                nextScrapeSec = t->scrapeIntervalSec;
546            }
547        }
548
549        if( bencLoaded )
550            tr_bencFree( &benc );
551    }
552
553    if (( warning = updateAddresses( t, req ) )) {
554        tr_err( warning );
555        publishWarning( t, warning );
556    }
557
558    tr_timerFree( &t->scrapeTimer );
559
560    t->scrapeTimer = tr_timerNew( t->handle,
561                                  onScrapeNow, t,
562                                  nextScrapeSec*1000 );
563}
564
565static int
566onScrapeNow( void * vt )
567{
568    tr_tracker * t = vt;
569    const tr_tracker_info * address = getCurrentAddress( t );
570
571    if( trackerSupportsScrape( t ) )
572    {
573        char * uri;
574        struct evhttp_connection * evcon;
575        struct evhttp_request *req;
576        struct evbuffer * buf = evbuffer_new( );
577
578        evbuffer_add_printf( buf, "%s?info_hash=%s", address->scrape, t->escaped );
579        uri = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
580        evbuffer_free( buf );
581
582        tr_inf( "Sending scrape to tracker %s:%d: %s",
583                address->address, address->port, uri );
584
585        evcon = getConnection( t, address->address, address->port );
586        evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
587        req = evhttp_request_new( onScrapeResponse, torrentHashNew( t ) );
588        addCommonHeaders( t, req );
589        tr_evhttp_make_request( t->handle, evcon, req, EVHTTP_REQ_GET, uri );
590    }
591
592    t->scrapeTimer = NULL;
593    return FALSE;
594}
595
596static void
597scrapeNow( tr_tracker * t )
598{
599    onScrapeNow( t );
600}
601
602/***
603****
604****  TRACKER REQUESTS
605****
606***/
607
608static char*
609buildTrackerRequestURI( const tr_tracker  * t,
610                        const tr_torrent  * torrent,
611                        const char        * eventName )
612{
613    const int isStopping = !strcmp( eventName, "stopped" );
614    const int numwant = isStopping ? 0 : NUMWANT;
615    struct evbuffer * buf = evbuffer_new( );
616    char * ret;
617
618    evbuffer_add_printf( buf, "%s"
619                              "?info_hash=%s"
620                              "&peer_id=%s"
621                              "&port=%d"
622                              "&uploaded=%"PRIu64
623                              "&downloaded=%"PRIu64
624                              "&corrupt=%"PRIu64
625                              "&left=%"PRIu64
626                              "&compact=1"
627                              "&numwant=%d"
628                              "&key=%s"
629                              "%s%s"
630                              "%s%s",
631        getCurrentAddress(t)->announce,
632        t->escaped,
633        t->peer_id,
634        tr_sharedGetPublicPort( t->handle->shared ),
635        torrent->uploadedCur,
636        torrent->downloadedCur,
637        torrent->corruptCur,
638        tr_cpLeftUntilComplete( torrent->completion ),
639        numwant,
640        t->key_param,
641        ( ( eventName && *eventName ) ? "&event=" : "" ),
642        ( ( eventName && *eventName ) ? eventName : "" ),
643        ( ( t->trackerID && *t->trackerID ) ? "&trackerid=" : "" ),
644        ( ( t->trackerID && *t->trackerID ) ? t->trackerID : "" ) );
645
646    ret = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
647    evbuffer_free( buf );
648    return ret;
649}
650
651/* Convert to compact form */
652static uint8_t *
653parseOldPeers( benc_val_t * bePeers, int * setmePeerCount )
654{
655    int i;
656    uint8_t *compact, *walk;
657    const int peerCount = bePeers->val.l.count;
658
659    assert( bePeers->type == TYPE_LIST );
660
661    compact = tr_new( uint8_t, peerCount*6 );
662
663    for( i=0, walk=compact; i<peerCount; ++i )
664    {
665        struct in_addr addr;
666        tr_port_t port;
667        benc_val_t * val;
668        benc_val_t * peer = &bePeers->val.l.vals[i];
669
670        val = tr_bencDictFind( peer, "ip" );
671        if( !val || val->type!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) )
672            continue;
673
674        memcpy( walk, &addr, 4 );
675        walk += 4;
676
677        val = tr_bencDictFind( peer, "port" );
678        if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff )
679            continue;
680
681        port = htons( val->val.i );
682        memcpy( walk, &port, 2 );
683        walk += 2;
684    }
685
686    *setmePeerCount = peerCount;
687    return compact;
688}
689
690static int onRetry( void * vt );
691static int onReannounce( void * vt );
692
693static void
694onStoppedResponse( struct evhttp_request * req UNUSED, void * handle UNUSED )
695{
696    dbgmsg( NULL, "got a response to some `stop' message" );
697}
698
699static void
700onTrackerResponse( struct evhttp_request * req, void * torrent_hash )
701{
702    const char * warning;
703    tr_tracker * t;
704    int err = 0;
705    int responseCode;
706
707    t = findTrackerFromHash( torrent_hash );
708    if( t == NULL ) /* tracker has been closed */
709        return;
710
711    dbgmsg( t, "got response from tracker: \"%s\"",
712            ( req && req->response_code_line ) ?  req->response_code_line
713                                               : "(null)" );
714
715    tr_inf( "Torrent \"%s\" tracker response: %s",
716            t->name,
717            ( req ? req->response_code_line : "(null)") );
718
719    if( req && ( req->response_code == HTTP_OK ) )
720    {
721        benc_val_t benc;
722        const int bencLoaded = !parseBencResponse( req, &benc );
723
724        publishErrorClear( t );
725
726        if( bencLoaded && benc.type==TYPE_DICT )
727        {
728            benc_val_t * tmp;
729
730            if(( tmp = tr_bencDictFind( &benc, "failure reason" ))) {
731                dbgmsg( t, "got failure message [%s]", tmp->val.s.s );
732                publishErrorMessage( t, tmp->val.s.s );
733            }
734
735            if(( tmp = tr_bencDictFind( &benc, "warning message" ))) {
736                dbgmsg( t, "got warning message [%s]", tmp->val.s.s );
737                publishWarning( t, tmp->val.s.s );
738            }
739
740            if(( tmp = tr_bencDictFind( &benc, "interval" ))) {
741                dbgmsg( t, "setting interval to %d", tmp->val.i );
742                t->announceIntervalSec = tmp->val.i;
743            }
744
745            if(( tmp = tr_bencDictFind( &benc, "min interval" ))) {
746                dbgmsg( t, "setting min interval to %d", tmp->val.i );
747                t->announceMinIntervalSec = tmp->val.i;
748            }
749
750            if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
751                t->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
752
753            if(( tmp = tr_bencDictFind( &benc, "complete" )))
754                t->seederCount = tmp->val.i;
755
756            if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
757                t->leecherCount = tmp->val.i;
758
759            if(( tmp = tr_bencDictFind( &benc, "peers" )))
760            {
761                int peerCount = 0;
762                uint8_t * peerCompact = NULL;
763
764                if( tmp->type == TYPE_LIST ) /* original protocol */
765                {
766                    if( tmp->val.l.count > 0 )
767                        peerCompact = parseOldPeers( tmp, &peerCount );
768                }
769                else if( tmp->type == TYPE_STR ) /* "compact" extension */
770                {
771                    if( tmp->val.s.i >= 6 )
772                    {
773                        peerCount = tmp->val.s.i / 6;
774                        peerCompact = tr_new( uint8_t, tmp->val.s.i );
775                        memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
776                    }
777                }
778
779                publishNewPeers( t, peerCount, peerCompact );
780                tr_free( peerCompact );
781            }
782        }
783
784        if( bencLoaded )
785            tr_bencFree( &benc );
786    }
787    else
788    {
789        tr_inf( "Bad response for torrent '%s' on request '%s' "
790                "... trying again in 30 seconds",
791                t->name, t->lastRequest );
792
793        err = 1;
794    }
795
796    if (( warning = updateAddresses( t, req ) )) {
797        publishWarning( t, warning );
798        tr_err( warning );
799    }
800
801    /**
802    ***
803    **/
804
805    responseCode = req ? req->response_code : 503;
806
807    if( 200<=responseCode && responseCode<=299 )
808    {
809        dbgmsg( t, "request succeeded. reannouncing in %d seconds", t->announceIntervalSec );
810
811        t->manualAnnounceAllowedAt = time(NULL)
812                                   + t->announceMinIntervalSec;
813        t->reannounceTimer = tr_timerNew( t->handle,
814                                          onReannounce, t,
815                                          t->announceIntervalSec * 1000 );
816    }
817    else if( 300<=responseCode && responseCode<=399 )
818    {
819        dbgmsg( t, "got a redirect; retrying immediately" );
820
821        /* it's a redirect... updateAddresses() has already
822         * parsed the redirect, all that's left is to retry */
823        onRetry( t );
824    }
825    else if( 400<=responseCode && responseCode<=499 )
826    {
827        dbgmsg( t, "got a 4xx error." );
828
829        /* The request could not be understood by the server due to
830         * malformed syntax. The client SHOULD NOT repeat the
831         * request without modifications. */
832        publishErrorMessage( t, req->response_code_line );
833        t->manualAnnounceAllowedAt = ~(time_t)0;
834        t->reannounceTimer = NULL;
835    }
836    else if( 500<=responseCode && responseCode<=599 )
837    {
838        dbgmsg( t, "got a 5xx error... retrying in 15 seconds." );
839
840        /* Response status codes beginning with the digit "5" indicate
841         * cases in which the server is aware that it has erred or is
842         * incapable of performing the request.  So we pause a bit and
843         * try again. */
844        publishWarning( t, req->response_code_line );
845        t->manualAnnounceAllowedAt = ~(time_t)0;
846        t->reannounceTimer = tr_timerNew( t->handle, onRetry, t, 15 * 1000 );
847    }
848    else
849    {
850        dbgmsg( t, "unhandled condition... retrying in 120 seconds." );
851
852        /* WTF did we get?? */
853        publishErrorMessage( t, req->response_code_line );
854        t->manualAnnounceAllowedAt = ~(time_t)0;
855        t->reannounceTimer = tr_timerNew( t->handle, onRetry, t, 120 * 1000 );
856    }
857}
858
859static int
860sendTrackerRequest( void * vt, const char * eventName )
861{
862    tr_tracker * t = vt;
863    const int isStopping = eventName && !strcmp( eventName, "stopped" );
864    const tr_tracker_info * address = getCurrentAddress( t );
865    char * uri;
866    struct evhttp_connection * evcon;
867    const tr_torrent * tor;
868
869    tor = tr_torrentFindFromHash( t->handle, t->hash );
870    if( tor == NULL )
871        return FALSE;   
872
873    uri = buildTrackerRequestURI( t, tor, eventName );
874
875    tr_inf( "Torrent \"%s\" sending '%s' to tracker %s:%d: %s",
876            t->name,
877            (eventName ? eventName : "periodic announce"),
878            address->address, address->port,
879            uri );
880
881    /* kill any pending requests */
882    dbgmsg( t, "clearing announce timer" );
883    tr_timerFree( &t->reannounceTimer );
884
885    evcon = getConnection( t, address->address, address->port );
886    if ( !evcon ) {
887        tr_err( "Can't make a connection to %s:%d", address->address, address->port );
888        tr_free( uri );
889    } else {
890        struct evhttp_request * req;
891        tr_free( t->lastRequest );
892        t->lastRequest = tr_strdup( eventName );
893        if( isStopping ) {
894            evhttp_connection_set_timeout( evcon, STOP_TIMEOUT_INTERVAL_SEC );
895            req = evhttp_request_new( onStoppedResponse, t->handle );
896        } else {
897            evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
898            req = evhttp_request_new( onTrackerResponse, torrentHashNew(t) );
899        }
900        dbgmsg( t, "sending \"%s\" request to tracker", eventName ? eventName : "reannounce" );
901
902        addCommonHeaders( t, req );
903        tr_evhttp_make_request( t->handle, evcon,
904                                req, EVHTTP_REQ_GET, uri );
905    }
906
907    return FALSE;
908}
909
910static int
911onReannounce( void * vt )
912{
913    tr_tracker * t = vt;
914    dbgmsg( t, "onReannounce" );
915    sendTrackerRequest( t, "" );
916    dbgmsg( t, "onReannounce setting announceTimer to NULL" );
917    t->reannounceTimer = NULL;
918    return FALSE;
919}
920
921static int
922onRetry( void * vt )
923{
924    tr_tracker * t = vt;
925    dbgmsg( t, "onRetry" );
926    sendTrackerRequest( t, t->lastRequest );
927    dbgmsg( t, "onRetry setting announceTimer to NULL" );
928    t->reannounceTimer = NULL;
929    return FALSE;
930}
931
932/***
933****  PUBLIC
934***/
935
936tr_publisher_tag
937tr_trackerSubscribe( tr_tracker          * t,
938                     tr_delivery_func      func,
939                     void                * user_data )
940{
941    return tr_publisherSubscribe( t->publisher, func, user_data );
942}
943
944void
945tr_trackerUnsubscribe( tr_tracker        * t,
946                       tr_publisher_tag    tag )
947{
948    tr_publisherUnsubscribe( t->publisher, tag );
949}
950
951const tr_tracker_info *
952tr_trackerGetAddress( const tr_tracker   * t )
953{
954    return getCurrentAddress( t );
955}
956
957int
958tr_trackerCanManualAnnounce ( const tr_tracker * t)
959{
960    return t->isRunning
961            && ( time(NULL) >= t->manualAnnounceAllowedAt );
962}
963
964void
965tr_trackerGetCounts( const tr_tracker  * t,
966                     int               * setme_completedCount,
967                     int               * setme_leecherCount,
968                     int               * setme_seederCount )
969{
970    if( setme_completedCount )
971       *setme_completedCount = t->timesDownloaded;
972
973    if( setme_leecherCount )
974       *setme_leecherCount = t->leecherCount;
975
976    if( setme_seederCount )
977       *setme_seederCount = t->seederCount;
978}
979
980void
981tr_trackerStart( tr_tracker * t )
982{
983    tr_peerIdNew( t->peer_id, sizeof(t->peer_id) );
984
985    if( !t->reannounceTimer && !t->isRunning )
986    {
987        t->isRunning = 1;
988        sendTrackerRequest( t, "started" );
989    }
990}
991
992void
993tr_trackerReannounce( tr_tracker * t )
994{
995    sendTrackerRequest( t, "started" );
996}
997
998void
999tr_trackerCompleted( tr_tracker * t )
1000{
1001    sendTrackerRequest( t, "completed" );
1002}
1003
1004void
1005tr_trackerStop( tr_tracker * t )
1006{
1007    if( t->isRunning )
1008    {
1009        t->isRunning = 0;
1010        sendTrackerRequest( t, "stopped" );
1011    }
1012}
1013
1014void
1015tr_trackerChangeMyPort( tr_tracker * t )
1016{
1017    if( t->isRunning )
1018        tr_trackerReannounce( t );
1019}
Note: See TracBrowser for help on using the repository browser.