source: trunk/libtransmission/tracker.c @ 3455

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

added debugging messages to the tracker cleanup. better error handling for esoteric http errors.

  • Property svn:keywords set to Date Rev Author Id
File size: 27.9 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 3455 2007-10-18 03:55:55Z 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 );
302fprintf( stderr, "sizeof(tr_tracker) is %d\n", (int)sizeof(tr_tracker) );
303    return t;
304}
305
306static void
307onTrackerFreeNow( void * vt )
308{
309    int i;
310    tr_tracker * t = vt;
311
312    tr_timerFree( &t->scrapeTimer );
313    tr_timerFree( &t->reannounceTimer );
314    tr_publisherFree( &t->publisher );
315    tr_free( t->name );
316    tr_free( t->trackerID );
317    tr_free( t->lastRequest );
318
319    for( i=0; i<t->addressCount; ++i )
320        tr_trackerInfoClear( &t->addresses[i] );
321
322    if( t->redirect ) {
323        tr_trackerInfoClear( t->redirect );
324        tr_free( t->redirect );
325    }
326
327    tr_free( t->addresses );
328    tr_free( t->tierFronts );
329    tr_free( t );
330}
331
332void
333tr_trackerFree( tr_tracker * t )
334{
335    tr_runInEventThread( t->handle, onTrackerFreeNow, t );
336}
337
338/***
339****  UTIL
340***/
341
342static int
343parseBencResponse( struct evhttp_request * req, benc_val_t * setme )
344{
345    const unsigned char * body = EVBUFFER_DATA( req->input_buffer );
346    const int bodylen = EVBUFFER_LENGTH( req->input_buffer );
347    int ret = 1;
348    int i;
349
350    for( i=0; ret && i<bodylen; ++i )
351        if( !tr_bencLoad( body+i, bodylen-1, setme, NULL ) )
352            ret = 0;
353
354    return ret;
355}
356
357static const char*
358updateAddresses( tr_tracker * t, const struct evhttp_request * req )
359{
360    const char * ret = NULL;
361    int moveToNextAddress = FALSE;
362
363    if( !req )
364    {
365        ret = "No response from tracker -- will keep trying.";
366        tr_inf( ret );
367
368        moveToNextAddress = TRUE;
369    }
370    else if( req->response_code == HTTP_OK )
371    {
372        if( t->redirect != NULL )
373        {
374            /* multitracker spec: "if a connection with a tracker is
375               successful, it will be moved to the front of the tier." */
376            const int i = t->addressIndex;
377            const int j = t->tierFronts[i];
378            const tr_tracker_info swap = t->addresses[i];
379            t->addresses[i] = t->addresses[j];
380            t->addresses[j] = swap;
381        }
382    }
383    else if(    ( req->response_code == HTTP_MOVEPERM )
384             || ( req->response_code == HTTP_MOVETEMP ) )
385    {
386        const char * loc = evhttp_find_header( req->input_headers, "Location" );
387        tr_tracker_info tmp;
388        if( tr_trackerInfoInit( &tmp, loc, -1 ) ) /* a bad redirect? */
389        {
390            moveToNextAddress = TRUE;
391        }
392        else if( req->response_code == HTTP_MOVEPERM )
393        {
394            tr_tracker_info * cur = &t->addresses[t->addressIndex];
395            tr_trackerInfoClear( cur );
396            *cur = tmp;
397        }
398        else if( req->response_code == HTTP_MOVETEMP )
399        {
400            if( t->redirect == NULL )
401                t->redirect = tr_new0( tr_tracker_info, 1 );
402            else
403                tr_trackerInfoClear( t->redirect );
404            *t->redirect = tmp;
405        }
406    }
407    else 
408    {
409        ret = "No response from tracker -- will keep trying.";
410        moveToNextAddress = TRUE;
411    }
412
413    if( moveToNextAddress )
414        if ( ++t->addressIndex >= t->addressCount )
415            t->addressIndex = 0;
416
417    return ret;
418}
419
420static tr_tracker_info *
421getCurrentAddress( const tr_tracker * t )
422{
423    assert( t->addresses != NULL );
424    assert( t->addressIndex >= 0 );
425    assert( t->addressIndex < t->addressCount );
426
427    return &t->addresses[t->addressIndex];
428}
429static int
430trackerSupportsScrape( const tr_tracker * t )
431{
432    const tr_tracker_info * info = getCurrentAddress( t );
433
434    return ( info != NULL )
435        && ( info->scrape != NULL )
436        && ( info->scrape[0] != '\0' );
437}
438
439
440static void
441addCommonHeaders( const tr_tracker * t,
442                  struct evhttp_request * req )
443{
444    char buf[1024];
445    const tr_tracker_info * address = getCurrentAddress( t );
446    snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
447    evhttp_add_header( req->output_headers, "Host", buf );
448    evhttp_add_header( req->output_headers, "Connection", "close" );
449    evhttp_add_header( req->output_headers, "Content-Length", "0" );
450    evhttp_add_header( req->output_headers, "User-Agent",
451                                         TR_NAME "/" LONG_VERSION_STRING );
452}
453
454/**
455***
456**/
457
458struct torrent_hash
459{
460    tr_handle * handle;
461    uint8_t hash[SHA_DIGEST_LENGTH];
462};
463
464static struct torrent_hash*
465torrentHashNew( tr_tracker * t )
466{
467    struct torrent_hash * data = tr_new( struct torrent_hash, 1 );
468    data->handle = t->handle;
469    memcpy( data->hash, t->hash, SHA_DIGEST_LENGTH );
470    return data;
471}
472
473tr_tracker *
474findTrackerFromHash( struct torrent_hash * data )
475{
476    tr_torrent * torrent = tr_torrentFindFromHash( data->handle, data->hash );
477    return torrent ? torrent->tracker : NULL;
478}
479
480/***
481****
482****  SCRAPE
483****
484***/
485
486static int
487onScrapeNow( void * vt );
488
489static void
490onScrapeResponse( struct evhttp_request * req, void * vhash )
491{
492    const char * warning;
493    time_t nextScrapeSec = 60;
494    tr_tracker * t;
495
496    t = findTrackerFromHash( vhash );
497    tr_free( vhash );
498    if( t == NULL ) /* tracker's been closed... */
499        return;
500
501    tr_inf( "Got scrape response for  '%s': %s",
502            t->name,
503            ( ( req && req->response_code_line ) ? req->response_code_line
504                                                 : "(null)") );
505
506    if( req && ( req->response_code == HTTP_OK ) )
507    {
508        benc_val_t benc, *files;
509        const int bencLoaded = !parseBencResponse( req, &benc );
510
511        if( bencLoaded
512            && (( files = tr_bencDictFind( &benc, "files" ) ))
513            && ( files->type == TYPE_DICT ) )
514        {
515            int i;
516            for( i=0; i<files->val.l.count; i+=2 )
517            {
518                const uint8_t* hash =
519                        (const uint8_t*) files->val.l.vals[i].val.s.s;
520                benc_val_t *tmp, *flags;
521                benc_val_t *tordict = &files->val.l.vals[i+1];
522                if( memcmp( t->hash, hash, SHA_DIGEST_LENGTH ) )
523                    continue;
524
525                publishErrorClear( t );
526
527                if(( tmp = tr_bencDictFind( tordict, "complete" )))
528                    t->seederCount = tmp->val.i;
529
530                if(( tmp = tr_bencDictFind( tordict, "incomplete" )))
531                    t->leecherCount = tmp->val.i;
532
533                if(( tmp = tr_bencDictFind( tordict, "downloaded" )))
534                    t->timesDownloaded = tmp->val.i;
535
536                if(( flags = tr_bencDictFind( tordict, "flags" )))
537                    if(( tmp = tr_bencDictFind( flags, "min_request_interval")))
538                        t->scrapeIntervalSec = tmp->val.i;
539
540                tr_dbg( "Torrent '%s' scrape successful."
541                        "  Rescraping in %d seconds",
542                        t->name, t->scrapeIntervalSec );
543
544                nextScrapeSec = t->scrapeIntervalSec;
545            }
546        }
547
548        if( bencLoaded )
549            tr_bencFree( &benc );
550    }
551
552    if (( warning = updateAddresses( t, req ) )) {
553        tr_err( warning );
554        publishWarning( t, warning );
555    }
556
557    tr_timerFree( &t->scrapeTimer );
558
559    t->scrapeTimer = tr_timerNew( t->handle,
560                                  onScrapeNow, t,
561                                  nextScrapeSec*1000 );
562}
563
564static int
565onScrapeNow( void * vt )
566{
567    tr_tracker * t = vt;
568    const tr_tracker_info * address = getCurrentAddress( t );
569
570    if( trackerSupportsScrape( t ) )
571    {
572        char * uri;
573        struct evhttp_connection * evcon;
574        struct evhttp_request *req;
575        struct evbuffer * buf = evbuffer_new( );
576
577        evbuffer_add_printf( buf, "%s?info_hash=%s", address->scrape, t->escaped );
578        uri = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
579        evbuffer_free( buf );
580
581        tr_inf( "Sending scrape to tracker %s:%d: %s",
582                address->address, address->port, uri );
583
584        evcon = getConnection( t, address->address, address->port );
585        evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
586        req = evhttp_request_new( onScrapeResponse, torrentHashNew( t ) );
587        addCommonHeaders( t, req );
588        tr_evhttp_make_request( t->handle, evcon, req, EVHTTP_REQ_GET, uri );
589    }
590
591    t->scrapeTimer = NULL;
592    return FALSE;
593}
594
595static void
596scrapeNow( tr_tracker * t )
597{
598    onScrapeNow( t );
599}
600
601/***
602****
603****  TRACKER REQUESTS
604****
605***/
606
607static char*
608buildTrackerRequestURI( const tr_tracker  * t,
609                        const tr_torrent  * torrent,
610                        const char        * eventName )
611{
612    const int isStopping = !strcmp( eventName, "stopped" );
613    const int numwant = isStopping ? 0 : NUMWANT;
614    struct evbuffer * buf = evbuffer_new( );
615    char * ret;
616
617    evbuffer_add_printf( buf, "%s"
618                              "?info_hash=%s"
619                              "&peer_id=%s"
620                              "&port=%d"
621                              "&uploaded=%"PRIu64
622                              "&downloaded=%"PRIu64
623                              "&corrupt=%"PRIu64
624                              "&left=%"PRIu64
625                              "&compact=1"
626                              "&numwant=%d"
627                              "&key=%s"
628                              "%s%s"
629                              "%s%s",
630        getCurrentAddress(t)->announce,
631        t->escaped,
632        t->peer_id,
633        tr_sharedGetPublicPort( t->handle->shared ),
634        torrent->uploadedCur,
635        torrent->downloadedCur,
636        torrent->corruptCur,
637        tr_cpLeftUntilComplete( torrent->completion ),
638        numwant,
639        t->key_param,
640        ( ( eventName && *eventName ) ? "&event=" : "" ),
641        ( ( eventName && *eventName ) ? eventName : "" ),
642        ( ( t->trackerID && *t->trackerID ) ? "&trackerid=" : "" ),
643        ( ( t->trackerID && *t->trackerID ) ? t->trackerID : "" ) );
644
645    ret = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
646    evbuffer_free( buf );
647    return ret;
648}
649
650/* Convert to compact form */
651static uint8_t *
652parseOldPeers( benc_val_t * bePeers, int * setmePeerCount )
653{
654    int i;
655    uint8_t *compact, *walk;
656    const int peerCount = bePeers->val.l.count;
657
658    assert( bePeers->type == TYPE_LIST );
659
660    compact = tr_new( uint8_t, peerCount*6 );
661
662    for( i=0, walk=compact; i<peerCount; ++i )
663    {
664        struct in_addr addr;
665        tr_port_t port;
666        benc_val_t * val;
667        benc_val_t * peer = &bePeers->val.l.vals[i];
668
669        val = tr_bencDictFind( peer, "ip" );
670        if( !val || val->type!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) )
671            continue;
672
673        memcpy( walk, &addr, 4 );
674        walk += 4;
675
676        val = tr_bencDictFind( peer, "port" );
677        if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff )
678            continue;
679
680        port = htons( val->val.i );
681        memcpy( walk, &port, 2 );
682        walk += 2;
683    }
684
685    *setmePeerCount = peerCount;
686    return compact;
687}
688
689static int onRetry( void * vt );
690static int onReannounce( void * vt );
691
692static void
693onStoppedResponse( struct evhttp_request * req UNUSED, void * handle UNUSED )
694{
695    dbgmsg( NULL, "got a response to some `stop' message" );
696}
697
698static void
699onTrackerResponse( struct evhttp_request * req, void * torrent_hash )
700{
701    const char * warning;
702    tr_tracker * t;
703    int err = 0;
704    int responseCode;
705
706    t = findTrackerFromHash( torrent_hash );
707    if( t == NULL ) /* tracker has been closed */
708        return;
709
710    dbgmsg( t, "got response from tracker: \"%s\"",
711            ( req && req->response_code_line ) ?  req->response_code_line
712                                               : "(null)" );
713
714    tr_inf( "Torrent \"%s\" tracker response: %s",
715            t->name,
716            ( req ? req->response_code_line : "(null)") );
717
718    if( req && ( req->response_code == HTTP_OK ) )
719    {
720        benc_val_t benc;
721        const int bencLoaded = !parseBencResponse( req, &benc );
722
723        publishErrorClear( t );
724
725        if( bencLoaded && benc.type==TYPE_DICT )
726        {
727            benc_val_t * tmp;
728
729            if(( tmp = tr_bencDictFind( &benc, "failure reason" ))) {
730                dbgmsg( t, "got failure message [%s]", tmp->val.s.s );
731                publishErrorMessage( t, tmp->val.s.s );
732            }
733
734            if(( tmp = tr_bencDictFind( &benc, "warning message" ))) {
735                dbgmsg( t, "got warning message [%s]", tmp->val.s.s );
736                publishWarning( t, tmp->val.s.s );
737            }
738
739            if(( tmp = tr_bencDictFind( &benc, "interval" ))) {
740                dbgmsg( t, "setting interval to %d", tmp->val.i );
741                t->announceIntervalSec = tmp->val.i;
742            }
743
744            if(( tmp = tr_bencDictFind( &benc, "min interval" ))) {
745                dbgmsg( t, "setting min interval to %d", tmp->val.i );
746                t->announceMinIntervalSec = tmp->val.i;
747            }
748
749            if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
750                t->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
751
752            if(( tmp = tr_bencDictFind( &benc, "complete" )))
753                t->seederCount = tmp->val.i;
754
755            if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
756                t->leecherCount = tmp->val.i;
757
758            if(( tmp = tr_bencDictFind( &benc, "peers" )))
759            {
760                int peerCount = 0;
761                uint8_t * peerCompact = NULL;
762
763                if( tmp->type == TYPE_LIST ) /* original protocol */
764                {
765                    if( tmp->val.l.count > 0 )
766                        peerCompact = parseOldPeers( tmp, &peerCount );
767                }
768                else if( tmp->type == TYPE_STR ) /* "compact" extension */
769                {
770                    if( tmp->val.s.i >= 6 )
771                    {
772                        peerCount = tmp->val.s.i / 6;
773                        peerCompact = tr_new( uint8_t, tmp->val.s.i );
774                        memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
775                    }
776                }
777
778                publishNewPeers( t, peerCount, peerCompact );
779                tr_free( peerCompact );
780            }
781        }
782
783        if( bencLoaded )
784            tr_bencFree( &benc );
785    }
786    else
787    {
788        tr_inf( "Bad response for torrent '%s' on request '%s' "
789                "... trying again in 30 seconds",
790                t->name, t->lastRequest );
791
792        err = 1;
793    }
794
795    if (( warning = updateAddresses( t, req ) )) {
796        publishWarning( t, warning );
797        tr_err( warning );
798    }
799
800    /**
801    ***
802    **/
803
804    responseCode = req ? req->response_code : 503;
805
806    if( 200<=responseCode && responseCode<=299 )
807    {
808        dbgmsg( t, "request succeeded. reannouncing in %d seconds", t->announceIntervalSec );
809
810        t->manualAnnounceAllowedAt = time(NULL)
811                                   + t->announceMinIntervalSec;
812        t->reannounceTimer = tr_timerNew( t->handle,
813                                          onReannounce, t,
814                                          t->announceIntervalSec * 1000 );
815    }
816    else if( 300<=responseCode && responseCode<=399 )
817    {
818        dbgmsg( t, "got a redirect; retrying immediately" );
819
820        /* it's a redirect... updateAddresses() has already
821         * parsed the redirect, all that's left is to retry */
822        onRetry( t );
823    }
824    else if( 400<=responseCode && responseCode<=499 )
825    {
826        dbgmsg( t, "got a 4xx error." );
827
828        /* The request could not be understood by the server due to
829         * malformed syntax. The client SHOULD NOT repeat the
830         * request without modifications. */
831        publishErrorMessage( t, req->response_code_line );
832        t->manualAnnounceAllowedAt = ~(time_t)0;
833        t->reannounceTimer = NULL;
834    }
835    else if( 500<=responseCode && responseCode<=599 )
836    {
837        dbgmsg( t, "got a 5xx error... retrying in 15 seconds." );
838
839        /* Response status codes beginning with the digit "5" indicate
840         * cases in which the server is aware that it has erred or is
841         * incapable of performing the request.  So we pause a bit and
842         * try again. */
843        publishWarning( t, req->response_code_line );
844        t->manualAnnounceAllowedAt = ~(time_t)0;
845        t->reannounceTimer = tr_timerNew( t->handle, onRetry, t, 15 * 1000 );
846    }
847    else
848    {
849        dbgmsg( t, "unhandled condition... retrying in 120 seconds." );
850
851        /* WTF did we get?? */
852        publishErrorMessage( t, req->response_code_line );
853        t->manualAnnounceAllowedAt = ~(time_t)0;
854        t->reannounceTimer = tr_timerNew( t->handle, onRetry, t, 120 * 1000 );
855    }
856}
857
858static int
859sendTrackerRequest( void * vt, const char * eventName )
860{
861    tr_tracker * t = vt;
862    const int isStopping = eventName && !strcmp( eventName, "stopped" );
863    const tr_tracker_info * address = getCurrentAddress( t );
864    char * uri;
865    struct evhttp_connection * evcon;
866    const tr_torrent * tor;
867
868    tor = tr_torrentFindFromHash( t->handle, t->hash );
869    if( tor == NULL )
870        return FALSE;   
871
872    uri = buildTrackerRequestURI( t, tor, eventName );
873
874    tr_inf( "Torrent \"%s\" sending '%s' to tracker %s:%d: %s",
875            t->name,
876            (eventName ? eventName : "periodic announce"),
877            address->address, address->port,
878            uri );
879
880    /* kill any pending requests */
881    tr_timerFree( &t->reannounceTimer );
882
883    evcon = getConnection( t, address->address, address->port );
884    if ( !evcon ) {
885        tr_err( "Can't make a connection to %s:%d", address->address, address->port );
886        tr_free( uri );
887    } else {
888        struct evhttp_request * req;
889        tr_free( t->lastRequest );
890        t->lastRequest = tr_strdup( eventName );
891        if( isStopping ) {
892            evhttp_connection_set_timeout( evcon, STOP_TIMEOUT_INTERVAL_SEC );
893            req = evhttp_request_new( onStoppedResponse, t->handle );
894        } else {
895            evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
896            req = evhttp_request_new( onTrackerResponse, torrentHashNew(t) );
897        }
898        dbgmsg( t, "sending \"%s\" request to tracker", eventName ? eventName : "reannounce" );
899
900        addCommonHeaders( t, req );
901        tr_evhttp_make_request( t->handle, evcon,
902                                req, EVHTTP_REQ_GET, uri );
903    }
904
905    return FALSE;
906}
907
908static int
909onReannounce( void * vt )
910{
911    tr_tracker * t = vt;
912    sendTrackerRequest( t, "" );
913    t->reannounceTimer = NULL;
914    return FALSE;
915}
916
917static int
918onRetry( void * vt )
919{
920    tr_tracker * t = vt;
921    sendTrackerRequest( t, t->lastRequest );
922    t->reannounceTimer = NULL;
923    return FALSE;
924}
925
926/***
927****  PUBLIC
928***/
929
930tr_publisher_tag
931tr_trackerSubscribe( tr_tracker          * t,
932                     tr_delivery_func      func,
933                     void                * user_data )
934{
935    return tr_publisherSubscribe( t->publisher, func, user_data );
936}
937
938void
939tr_trackerUnsubscribe( tr_tracker        * t,
940                       tr_publisher_tag    tag )
941{
942    tr_publisherUnsubscribe( t->publisher, tag );
943}
944
945const tr_tracker_info *
946tr_trackerGetAddress( const tr_tracker   * t )
947{
948    return getCurrentAddress( t );
949}
950
951int
952tr_trackerCanManualAnnounce ( const tr_tracker * t)
953{
954    return t->isRunning
955            && ( time(NULL) >= t->manualAnnounceAllowedAt );
956}
957
958void
959tr_trackerGetCounts( const tr_tracker  * t,
960                     int               * setme_completedCount,
961                     int               * setme_leecherCount,
962                     int               * setme_seederCount )
963{
964    if( setme_completedCount )
965       *setme_completedCount = t->timesDownloaded;
966
967    if( setme_leecherCount )
968       *setme_leecherCount = t->leecherCount;
969
970    if( setme_seederCount )
971       *setme_seederCount = t->seederCount;
972}
973
974void
975tr_trackerStart( tr_tracker * t )
976{
977    tr_peerIdNew( t->peer_id, sizeof(t->peer_id) );
978
979    if( !t->reannounceTimer && !t->isRunning )
980    {
981        t->isRunning = 1;
982        sendTrackerRequest( t, "started" );
983    }
984}
985
986void
987tr_trackerReannounce( tr_tracker * t )
988{
989    sendTrackerRequest( t, "started" );
990}
991
992void
993tr_trackerCompleted( tr_tracker * t )
994{
995    sendTrackerRequest( t, "completed" );
996}
997
998void
999tr_trackerStop( tr_tracker * t )
1000{
1001    if( t->isRunning )
1002    {
1003        t->isRunning = 0;
1004        sendTrackerRequest( t, "stopped" );
1005    }
1006}
1007
1008void
1009tr_trackerChangeMyPort( tr_tracker * t )
1010{
1011    if( t->isRunning )
1012        tr_trackerReannounce( t );
1013}
Note: See TracBrowser for help on using the repository browser.