source: trunk/libtransmission/tracker.c @ 3556

Last change on this file since 3556 was 3556, checked in by livings124, 15 years ago

trunk: Ensure only a single "?" is used for appending arguments to the announce and scrape URL (fixes #411)

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