source: trunk/libtransmission/tracker.c @ 3753

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

update our #includes now that libevent has cleaned up event.h

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