source: trunk/libtransmission/tracker.c @ 3458

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

possibly fix r3457 crash reported by chrissturm

  • Property svn:keywords set to Date Rev Author Id
File size: 28.2 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 3458 2007-10-18 19:33:29Z 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",
810                   t->announceIntervalSec );
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        if( req && req->response_code_line )
833            publishErrorMessage( t, req->response_code_line );
834        t->manualAnnounceAllowedAt = ~(time_t)0;
835        t->reannounceTimer = NULL;
836    }
837    else if( 500<=responseCode && responseCode<=599 )
838    {
839        dbgmsg( t, "got a 5xx error... retrying in 15 seconds." );
840
841        /* Response status codes beginning with the digit "5" indicate
842         * cases in which the server is aware that it has erred or is
843         * incapable of performing the request.  So we pause a bit and
844         * try again. */
845        if( req && req->response_code_line )
846            publishWarning( t, req->response_code_line );
847        t->manualAnnounceAllowedAt = ~(time_t)0;
848        t->reannounceTimer = tr_timerNew( t->handle, onRetry, t, 15 * 1000 );
849    }
850    else
851    {
852        dbgmsg( t, "unhandled condition... retrying in 120 seconds." );
853
854        /* WTF did we get?? */
855        if( req && req->response_code_line )
856            publishErrorMessage( t, req->response_code_line );
857        t->manualAnnounceAllowedAt = ~(time_t)0;
858        t->reannounceTimer = tr_timerNew( t->handle, onRetry, t, 120 * 1000 );
859    }
860}
861
862static int
863sendTrackerRequest( void * vt, const char * eventName )
864{
865    tr_tracker * t = vt;
866    const int isStopping = eventName && !strcmp( eventName, "stopped" );
867    const tr_tracker_info * address = getCurrentAddress( t );
868    char * uri;
869    struct evhttp_connection * evcon;
870    const tr_torrent * tor;
871
872    tor = tr_torrentFindFromHash( t->handle, t->hash );
873    if( tor == NULL )
874        return FALSE;   
875
876    uri = buildTrackerRequestURI( t, tor, eventName );
877
878    tr_inf( "Torrent \"%s\" sending '%s' to tracker %s:%d: %s",
879            t->name,
880            (eventName ? eventName : "periodic announce"),
881            address->address, address->port,
882            uri );
883
884    /* kill any pending requests */
885    dbgmsg( t, "clearing announce timer" );
886    tr_timerFree( &t->reannounceTimer );
887
888    evcon = getConnection( t, address->address, address->port );
889    if ( !evcon ) {
890        tr_err( "Can't make a connection to %s:%d", address->address, address->port );
891        tr_free( uri );
892    } else {
893        struct evhttp_request * req;
894        tr_free( t->lastRequest );
895        t->lastRequest = tr_strdup( eventName );
896        if( isStopping ) {
897            evhttp_connection_set_timeout( evcon, STOP_TIMEOUT_INTERVAL_SEC );
898            req = evhttp_request_new( onStoppedResponse, t->handle );
899        } else {
900            evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
901            req = evhttp_request_new( onTrackerResponse, torrentHashNew(t) );
902        }
903        dbgmsg( t, "sending \"%s\" request to tracker", eventName ? eventName : "reannounce" );
904
905        addCommonHeaders( t, req );
906        tr_evhttp_make_request( t->handle, evcon,
907                                req, EVHTTP_REQ_GET, uri );
908    }
909
910    return FALSE;
911}
912
913static int
914onReannounce( void * vt )
915{
916    tr_tracker * t = vt;
917    dbgmsg( t, "onReannounce" );
918    sendTrackerRequest( t, "" );
919    dbgmsg( t, "onReannounce setting announceTimer to NULL" );
920    t->reannounceTimer = NULL;
921    return FALSE;
922}
923
924static int
925onRetry( void * vt )
926{
927    tr_tracker * t = vt;
928    dbgmsg( t, "onRetry" );
929    sendTrackerRequest( t, t->lastRequest );
930    dbgmsg( t, "onRetry setting announceTimer to NULL" );
931    t->reannounceTimer = NULL;
932    return FALSE;
933}
934
935/***
936****  PUBLIC
937***/
938
939tr_publisher_tag
940tr_trackerSubscribe( tr_tracker          * t,
941                     tr_delivery_func      func,
942                     void                * user_data )
943{
944    return tr_publisherSubscribe( t->publisher, func, user_data );
945}
946
947void
948tr_trackerUnsubscribe( tr_tracker        * t,
949                       tr_publisher_tag    tag )
950{
951    tr_publisherUnsubscribe( t->publisher, tag );
952}
953
954const tr_tracker_info *
955tr_trackerGetAddress( const tr_tracker   * t )
956{
957    return getCurrentAddress( t );
958}
959
960int
961tr_trackerCanManualAnnounce ( const tr_tracker * t)
962{
963    return t->isRunning
964            && ( time(NULL) >= t->manualAnnounceAllowedAt );
965}
966
967void
968tr_trackerGetCounts( const tr_tracker  * t,
969                     int               * setme_completedCount,
970                     int               * setme_leecherCount,
971                     int               * setme_seederCount )
972{
973    if( setme_completedCount )
974       *setme_completedCount = t->timesDownloaded;
975
976    if( setme_leecherCount )
977       *setme_leecherCount = t->leecherCount;
978
979    if( setme_seederCount )
980       *setme_seederCount = t->seederCount;
981}
982
983void
984tr_trackerStart( tr_tracker * t )
985{
986    tr_peerIdNew( t->peer_id, sizeof(t->peer_id) );
987
988    if( !t->reannounceTimer && !t->isRunning )
989    {
990        t->isRunning = 1;
991        sendTrackerRequest( t, "started" );
992    }
993}
994
995void
996tr_trackerReannounce( tr_tracker * t )
997{
998    sendTrackerRequest( t, "started" );
999}
1000
1001void
1002tr_trackerCompleted( tr_tracker * t )
1003{
1004    sendTrackerRequest( t, "completed" );
1005}
1006
1007void
1008tr_trackerStop( tr_tracker * t )
1009{
1010    if( t->isRunning )
1011    {
1012        t->isRunning = 0;
1013        sendTrackerRequest( t, "stopped" );
1014    }
1015}
1016
1017void
1018tr_trackerChangeMyPort( tr_tracker * t )
1019{
1020    if( t->isRunning )
1021        tr_trackerReannounce( t );
1022}
Note: See TracBrowser for help on using the repository browser.