source: trunk/libtransmission/tracker.c @ 3317

Last change on this file since 3317 was 3317, checked in by charles, 15 years ago
  • don't reuse http connections to trackers. I think this is what caused most of the `no response from tracker X' messages.
  • extra safety checks to ensure the last `stopped' message to the tracker is sent on shutdown.
  • Property svn:keywords set to Date Rev Author Id
File size: 31.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 3317 2007-10-07 16:07:19Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isalnum */
15#include <limits.h> /* INT_MAX */
16#include <stdio.h> /* snprintf */
17#include <stdlib.h>
18#include <string.h> /* strcmp, strchr */
19#include <sys/queue.h> /* for evhttp */
20#include <sys/types.h> /* for evhttp */
21
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 "ptrarray.h"
30#include "publish.h"
31#include "shared.h"
32#include "tracker.h"
33#include "trevent.h"
34#include "utils.h"
35
36#define MINUTES_TO_MSEC(N) ((N) * 60 * 1000)
37
38/* manual announces via "update tracker" are allowed this frequently */
39#define MANUAL_ANNOUNCE_INTERVAL_MSEC (MINUTES_TO_MSEC(10))
40
41/* unless the tracker tells us otherwise, rescrape this frequently */
42#define DEFAULT_SCRAPE_INTERVAL_MSEC (MINUTES_TO_MSEC(15))
43
44/* unless the tracker tells us otherwise, reannounce this frequently */
45#define DEFAULT_ANNOUNCE_INTERVAL_MSEC (MINUTES_TO_MSEC(20))
46
47/* this is how long we'll leave a request hanging before timeout */
48#define TIMEOUT_INTERVAL_SEC 5
49
50/* the value of the 'numwant' argument passed in tracker requests */
51#define NUMWANT 128
52
53/* the length of the 'key' argument passed in tracker requests */
54#define TR_KEY_LEN 10
55
56
57/**
58***
59**/
60
61typedef struct
62{
63    tr_handle * handle;
64
65    tr_ptrArray * torrents;
66    tr_ptrArray * scraping;
67    tr_ptrArray * scrapeQueue;
68
69    /* these are set from the latest scrape or tracker response */
70    int announceIntervalMsec;
71    int minAnnounceIntervalMsec;
72    int scrapeIntervalMsec;
73
74    /* calculated when we get fewer scrapes
75       back than we asked for */
76    int multiscrapeMax;
77
78    tr_tracker_info * redirect;
79    tr_tracker_info * addresses;
80    int addressIndex;
81    int addressCount;
82    int * tierFronts;
83
84    char * primaryAddress;
85
86    /* sent as the "key" argument in tracker requests
87       to verify us if our IP address changes.
88       This is immutable for the life of the tracker object. */
89    char key_param[TR_KEY_LEN+1];
90
91    tr_timer * scrapeTimer;
92}
93Tracker;
94
95/* this is the Torrent struct, but since it's the pointer
96   passed around in the public API of this tracker module,
97   its *public* name is tr_tracker... wheee */
98typedef struct tr_tracker
99{
100    tr_publisher_t * publisher;
101
102    /* torrent hash string */
103    uint8_t hash[SHA_DIGEST_LENGTH];
104    char escaped[SHA_DIGEST_LENGTH * 3 + 1];
105    char * name;
106
107    /* corresponds to the peer_id sent as a tracker request parameter.
108       OiNK's op TooMuchTime says: "When the same torrent is opened and
109       closed and opened again without quitting Transmission ...
110       change the peerid. It would help sometimes if a stopped event
111       was missed to ensure that we didn't think someone was cheating. */
112    char peer_id[TR_ID_LEN + 1];
113
114    /* these are set from the latest scrape or tracker response...
115       -1 means 'unknown' */
116    int timesDownloaded;
117    int seeders;
118    int leechers;
119    char * trackerID;
120
121    /* the last tracker request we sent. (started, stopped, etc.)
122       automatic announces are an empty string;
123       NULL means no message has ever been sent */
124    char * lastRequest;
125
126    uint64_t manualAnnounceAllowedAt;
127
128    Tracker * tracker;
129
130    tr_timer * scrapeTimer;
131    tr_timer * reannounceTimer;
132
133    unsigned int isRunning : 1;
134}
135Torrent;
136
137static int
138trackerCompare( const void * va, const void * vb )
139{
140    const Tracker * a = ( const Tracker * ) va;
141    const Tracker * b = ( const Tracker * ) vb;
142    return strcmp( a->primaryAddress, b->primaryAddress );
143}
144
145static int
146torrentCompare( const void * va, const void * vb )
147{
148    const Torrent * a = (const Torrent*) va;
149    const Torrent * b = (const Torrent*) vb;
150    return memcmp( a->hash, b->hash, SHA_DIGEST_LENGTH );
151}
152
153/***
154****
155***/
156
157static int
158freeConnection( void * evcon )
159{
160    evhttp_connection_free( evcon );
161    return FALSE;
162}
163
164static void
165connectionClosedCB( struct evhttp_connection * evcon, void * handle )
166{
167    tr_timerNew( handle, freeConnection, evcon, 100 );
168}
169
170static struct evhttp_connection*
171getConnection( Tracker * tracker, const char * address, int port )
172{
173    struct evhttp_connection * c = evhttp_connection_new( address, port );
174    evhttp_connection_set_timeout( c, TIMEOUT_INTERVAL_SEC );
175    evhttp_connection_set_closecb( c, connectionClosedCB, tracker->handle );
176    return c;
177}
178
179/***
180****  PUBLISH
181***/
182
183static const tr_tracker_event_t emptyEvent = { 0, NULL, NULL, NULL, 0 };
184
185static void
186publishMessage( Torrent * tor, const char * msg, int type )
187{
188    tr_tracker_event_t event = emptyEvent;
189    event.hash = tor->hash;
190    event.messageType = type;
191    event.text = msg;
192    tr_publisherPublish( tor->publisher, tor, &event );
193}
194
195static void
196publishErrorClear( Torrent * tor )
197{
198    publishMessage( tor, NULL, TR_TRACKER_ERROR_CLEAR );
199}
200
201static void
202publishErrorMessage( Torrent * tor, const char * msg )
203{
204    publishMessage( tor, msg, TR_TRACKER_ERROR );
205}
206
207static void
208publishWarningMessage( Torrent * tor, const char * msg )
209{
210    publishMessage( tor, msg, TR_TRACKER_WARNING );
211}
212
213static void
214publishNewPeers( Torrent * tor, int count, uint8_t * peers )
215{
216    tr_tracker_event_t event = emptyEvent;
217    event.hash = tor->hash;
218    event.messageType = TR_TRACKER_PEERS;
219    event.peerCount = count;
220    event.peerCompact = peers;
221    tr_inf( "Torrent \"%s\" got %d new peers", tor->name, count );
222    tr_publisherPublish( tor->publisher, tor, &event );
223}
224
225/***
226****  LIFE CYCLE
227***/
228
229static tr_ptrArray *
230getTrackerLookupTable( void )
231{
232    static tr_ptrArray * myTrackers = NULL;
233    if( !myTrackers )
234         myTrackers = tr_ptrArrayNew( );
235    return myTrackers;
236}
237
238static void
239generateKeyParam( char * msg, int len )
240{
241    int i;
242    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
243    for( i=0; i<len; ++i )
244        *msg++ = pool[tr_rand(36)];
245    *msg = '\0';
246}
247
248static int onTrackerScrapeNow( void* );
249
250static void
251tr_trackerScrapeSoon( Tracker * t )
252{
253    /* don't start more than one scrape at once for the same tracker... */
254    if( !tr_ptrArrayEmpty( t->scraping ) )
255        return;
256
257    if( !t->scrapeTimer )
258         t->scrapeTimer = tr_timerNew( t->handle, onTrackerScrapeNow, t, 5000 );
259}
260
261static Tracker*
262tr_trackerGet( const tr_torrent * tor )
263{
264    const tr_info * info = &tor->info;
265    tr_ptrArray * trackers = getTrackerLookupTable( );
266    Tracker *t, tmp;
267    assert( info != NULL );
268    assert( info->primaryAddress && *info->primaryAddress );
269    tmp.primaryAddress = info->primaryAddress;
270    t = tr_ptrArrayFindSorted( trackers, &tmp, trackerCompare );
271
272    assert( t==NULL || !strcmp(t->primaryAddress,info->primaryAddress) );
273
274    if( t == NULL ) /* no such tracker.... create one */
275    {
276        int i, j, sum, *iwalk;
277        tr_tracker_info * nwalk;
278        tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress );
279
280        t = tr_new0( Tracker, 1 );
281        t->handle = tor->handle;
282        t->primaryAddress = tr_strdup( info->primaryAddress );
283        t->scrapeIntervalMsec      = DEFAULT_SCRAPE_INTERVAL_MSEC;
284        t->announceIntervalMsec    = DEFAULT_ANNOUNCE_INTERVAL_MSEC;
285        t->minAnnounceIntervalMsec = DEFAULT_ANNOUNCE_INTERVAL_MSEC;
286        t->multiscrapeMax = INT_MAX;
287        t->torrents    = tr_ptrArrayNew( );
288        t->scraping    = tr_ptrArrayNew( );
289        t->scrapeQueue = tr_ptrArrayNew( );
290        generateKeyParam( t->key_param, TR_KEY_LEN );
291
292        for( sum=i=0; i<info->trackerTiers; ++i )
293             sum += info->trackerList[i].count;
294        t->addresses = nwalk = tr_new0( tr_tracker_info, sum );
295        t->addressIndex = 0;
296        t->addressCount = sum;
297        t->tierFronts = iwalk = tr_new0( int, sum );
298
299        for( i=0; i<info->trackerTiers; ++i )
300        {
301            const int tierFront = nwalk - t->addresses;
302
303            for( j=0; j<info->trackerList[i].count; ++j )
304            {
305                const tr_tracker_info * src = &info->trackerList[i].list[j];
306                nwalk->address = tr_strdup( src->address );
307                nwalk->port = src->port;
308                nwalk->announce = tr_strdup( src->announce );
309                nwalk->scrape = tr_strdup( src->scrape );
310                ++nwalk;
311
312                *iwalk++ = tierFront;
313            }
314        }
315
316        assert( nwalk - t->addresses == sum );
317        assert( iwalk - t->tierFronts == sum );
318
319        tr_ptrArrayInsertSorted( trackers, t, trackerCompare );
320    }
321
322    return t;
323}
324
325static Torrent *
326getExistingTorrent( Tracker * t, const uint8_t hash[SHA_DIGEST_LENGTH] )
327{
328    Torrent tmp;
329    memcpy( tmp.hash, hash, SHA_DIGEST_LENGTH );
330    return tr_ptrArrayFindSorted( t->torrents, &tmp, torrentCompare );
331}
332
333static void
334escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
335{
336    const uint8_t *end = in + in_len;
337    while( in != end )
338        if( isalnum(*in) )
339            *out++ = (char) *in++;
340        else 
341            out += snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
342    *out = '\0';
343}
344
345static void
346onTorrentFreeNow( void * vtor )
347{
348    Torrent * tor = (Torrent *) vtor;
349    Tracker * t = tor->tracker;
350
351    tr_ptrArrayRemoveSorted( t->torrents, tor, torrentCompare );
352    tr_ptrArrayRemoveSorted( t->scrapeQueue, tor, torrentCompare );
353    tr_ptrArrayRemoveSorted( t->scraping, tor, torrentCompare );
354
355    tr_timerFree( &tor->scrapeTimer );
356    tr_timerFree( &tor->reannounceTimer );
357    tr_publisherFree( &tor->publisher );
358    tr_free( tor->name );
359    tr_free( tor->trackerID );
360    tr_free( tor->lastRequest );
361    tr_free( tor );
362
363    if( tr_ptrArrayEmpty( t->torrents ) ) /* last one.. free the tracker too */
364    {
365        int i;
366        tr_ptrArrayRemoveSorted( getTrackerLookupTable( ), t, trackerCompare );
367
368        tr_ptrArrayFree( t->torrents, NULL );
369        tr_ptrArrayFree( t->scrapeQueue, NULL );
370        tr_ptrArrayFree( t->scraping, NULL );
371
372        for( i=0; i<t->addressCount; ++i )
373            tr_trackerInfoClear( &t->addresses[i] );
374
375        if( t->redirect ) {
376            tr_trackerInfoClear( t->redirect );
377            tr_free( t->redirect );
378        }
379
380        tr_timerFree( &t->scrapeTimer );
381
382        tr_free( t->primaryAddress );
383        tr_free( t->addresses );
384        tr_free( t->tierFronts );
385        tr_free( t );
386    }
387}
388
389void
390tr_trackerFree( Torrent * tor )
391{
392    tr_runInEventThread( tor->tracker->handle, onTorrentFreeNow, tor );
393}
394
395Torrent*
396tr_trackerNew( tr_torrent * torrent )
397{
398    Torrent * tor;
399    Tracker * t = tr_trackerGet( torrent );
400    assert( getExistingTorrent( t, torrent->info.hash ) == NULL );
401
402    /* create a new Torrent and queue it for scraping */
403    tor = tr_new0( Torrent, 1 );
404    tor->publisher = tr_publisherNew( );
405    tor->tracker = t;
406    /*tor->torrent = torrent;*/
407    tor->timesDownloaded = -1;
408    tor->seeders = -1;
409    tor->leechers = -1;
410    tor->manualAnnounceAllowedAt = ~0;
411    tor->name = tr_strdup( torrent->info.name );
412    memcpy( tor->hash, torrent->info.hash, SHA_DIGEST_LENGTH );
413    escape( tor->escaped, torrent->info.hash, SHA_DIGEST_LENGTH );
414    tr_ptrArrayInsertSorted( t->torrents, tor, torrentCompare );
415    tr_ptrArrayInsertSorted( t->scrapeQueue, tor, torrentCompare );
416    tr_trackerScrapeSoon( t );
417    return tor;
418}
419
420/***
421****  UTIL
422***/
423
424static int
425parseBencResponse( struct evhttp_request * req, benc_val_t * setme )
426{
427    const unsigned char * body = EVBUFFER_DATA( req->input_buffer );
428    const int bodylen = EVBUFFER_LENGTH( req->input_buffer );
429    int ret = 1;
430    int i;
431
432    for( i=0; ret && i<bodylen; ++i )
433        if( !tr_bencLoad( body+i, bodylen-1, setme, NULL ) )
434            ret = 0;
435    return ret;
436}
437
438static char*
439updateAddresses( Tracker * t, const struct evhttp_request * req )
440{
441    char * ret = NULL;
442    int moveToNextAddress = FALSE;
443
444    if( !req )
445    {
446        ret = tr_strdup( "No response from tracker -- will keep trying." );
447        tr_inf( ret );
448
449        moveToNextAddress = TRUE;
450    }
451    else if( req->response_code == HTTP_OK )
452    {
453        if( t->redirect != NULL )
454        {
455            /* multitracker spec: "if a connection with a tracker is
456               successful, it will be moved to the front of the tier." */
457            const int i = t->addressIndex;
458            const int j = t->tierFronts[i];
459            const tr_tracker_info swap = t->addresses[i];
460            t->addresses[i] = t->addresses[j];
461            t->addresses[j] = swap;
462        }
463    }
464    else if(    ( req->response_code == HTTP_MOVEPERM )
465             || ( req->response_code == HTTP_MOVETEMP ) )
466    {
467        const char * loc = evhttp_find_header( req->input_headers, "Location" );
468        tr_tracker_info tmp;
469        if( tr_trackerInfoInit( &tmp, loc, -1 ) ) /* a bad redirect? */
470        {
471            moveToNextAddress = TRUE;
472        }
473        else if( req->response_code == HTTP_MOVEPERM )
474        {
475            tr_tracker_info * cur = &t->addresses[t->addressIndex];
476            tr_trackerInfoClear( cur );
477            *cur = tmp;
478        }
479        else if( req->response_code == HTTP_MOVETEMP )
480        {
481            if( t->redirect == NULL )
482                t->redirect = tr_new0( tr_tracker_info, 1 );
483            else
484                tr_trackerInfoClear( t->redirect );
485            *t->redirect = tmp;
486        }
487    }
488    else 
489    {
490        ret = tr_strdup( "No response from tracker -- will keep trying." );
491        moveToNextAddress = TRUE;
492    }
493
494    if( moveToNextAddress )
495        if ( ++t->addressIndex >= t->addressCount )
496            t->addressIndex = 0;
497
498    return ret;
499}
500
501static tr_tracker_info *
502getCurrentAddress( const Tracker * t )
503{
504    assert( t->addresses != NULL );
505    assert( t->addressIndex >= 0 );
506    assert( t->addressIndex < t->addressCount );
507
508    return &t->addresses[t->addressIndex];
509}
510static int
511trackerSupportsScrape( const Tracker * t )
512{
513    const tr_tracker_info * info = getCurrentAddress( t );
514
515    return ( info != NULL )
516        && ( info->scrape != NULL )
517        && ( info->scrape[0] != '\0' );
518}
519
520
521static void
522addCommonHeaders( const Tracker * t,
523                  struct evhttp_request * req )
524{
525    char buf[1024];
526    tr_tracker_info * address = getCurrentAddress( t );
527    snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
528    evhttp_add_header( req->output_headers, "Host", buf );
529    evhttp_add_header( req->output_headers, "Connection", "close" );
530    evhttp_add_header( req->output_headers, "Content-Length", "0" );
531    evhttp_add_header( req->output_headers, "User-Agent",
532                                         TR_NAME "/" LONG_VERSION_STRING );
533}
534
535/***
536****
537****  SCRAPE
538****
539***/
540
541static int
542onTorrentScrapeNow( void * vtor )
543{
544    Torrent * tor = (Torrent *) vtor;
545    if( trackerSupportsScrape( tor->tracker ) )
546    {
547        if( tr_ptrArrayFindSorted( tor->tracker->scrapeQueue, tor, torrentCompare) == NULL )
548            tr_ptrArrayInsertSorted( tor->tracker->scrapeQueue, tor, torrentCompare );
549        tr_trackerScrapeSoon( tor->tracker );
550    }
551    tor->scrapeTimer = NULL;
552    return FALSE;
553}
554
555static void
556onScrapeResponse( struct evhttp_request * req, void * vt )
557{
558    char * errmsg;
559    Tracker * t = (Tracker*) vt;
560
561    tr_inf( "Got scrape response from  '%s': %s",
562            t->primaryAddress,
563            (req ? req->response_code_line : "(null)") );
564
565    if( req && ( req->response_code == HTTP_OK ) )
566    {
567        int numResponses = 0;
568        benc_val_t benc, *files;
569        const int n_scraping = tr_ptrArraySize( t->scraping );
570        const int bencLoaded = !parseBencResponse( req, &benc );
571
572        if( bencLoaded
573            && (( files = tr_bencDictFind( &benc, "files" ) ))
574            && ( files->type == TYPE_DICT ) )
575        {
576            int i;
577            for( i=0; i<files->val.l.count; i+=2 )
578            {
579                const uint8_t* hash =
580                    (const uint8_t*) files->val.l.vals[i].val.s.s;
581                benc_val_t *tmp, *flags;
582                benc_val_t *tordict = &files->val.l.vals[i+1];
583                Torrent * tor = getExistingTorrent( t, hash );
584                ++numResponses;
585                   
586                if( !tor ) {
587                    tr_err( "Got an unrequested scrape response!" );
588                    continue;
589                }
590
591                publishErrorClear( tor );
592
593                if(( tmp = tr_bencDictFind( tordict, "complete" )))
594                    tor->seeders = tmp->val.i;
595
596                if(( tmp = tr_bencDictFind( tordict, "incomplete" )))
597                    tor->leechers = tmp->val.i;
598
599                if(( tmp = tr_bencDictFind( tordict, "downloaded" )))
600                    tor->timesDownloaded = tmp->val.i;
601
602                if(( flags = tr_bencDictFind( tordict, "flags" )))
603                    if(( tmp = tr_bencDictFind( flags, "min_request_interval")))
604                        t->scrapeIntervalMsec = tmp->val.i * 1000;
605
606                tr_ptrArrayRemoveSorted( t->scraping, tor, torrentCompare );
607
608                tr_timerFree( &tor->scrapeTimer );
609                tor->scrapeTimer = tr_timerNew( t->handle, onTorrentScrapeNow, tor, t->scrapeIntervalMsec );
610                tr_dbg( "Torrent '%s' scrape successful."
611                        "  Rescraping in %d seconds",
612                        tor->name, t->scrapeIntervalMsec/1000 );
613            }
614
615            if( !files->val.l.count )
616            {
617                /* got an empty files dictionary!  This probably means the
618                   torrents we're scraping have expired from the tracker,
619                   so make sure they're stopped.  It also means any previous
620                   changes to multiscrapeMax are suspect, so reset that. */
621
622                int n;
623                Torrent ** torrents = (Torrent**)
624                    tr_ptrArrayPeek( t->scraping, &n );
625                for( i=0; i<n; ++i )
626                    tr_trackerStop( torrents[i] );
627                tr_ptrArrayClear( t->scraping );
628
629                t->multiscrapeMax = INT_MAX;
630            }
631        }
632
633        if( bencLoaded )
634            tr_bencFree( &benc );
635
636        /* if the tracker gave us back fewer torrents than we
637           thought we should get, maybe our multiscrape string
638           is too big... limit it based on how many we got back */
639        if( ( 0 < numResponses ) && ( numResponses < n_scraping ) )
640            t->multiscrapeMax = numResponses;
641    }
642
643    if (( errmsg = updateAddresses( t, req ) ))
644        tr_err( errmsg );
645
646    if( !tr_ptrArrayEmpty( t->scraping ) )
647    {
648        int i, n;
649        Torrent ** torrents =
650            (Torrent**) tr_ptrArrayPeek( t->scraping, &n );
651        for( i=0; i<n; ++i ) {
652            if( errmsg != NULL )
653                publishErrorMessage( torrents[i], errmsg );
654            onTorrentScrapeNow( torrents[i] );
655        }
656        tr_ptrArrayClear( t->scraping );
657    }
658    tr_free( errmsg );
659
660    if( !tr_ptrArrayEmpty( t->scrapeQueue ) )
661        tr_trackerScrapeSoon( t );
662}
663
664static int
665onTrackerScrapeNow( void * vt )
666{
667    Tracker * t = (Tracker*) vt;
668    const tr_tracker_info * address = getCurrentAddress( t );
669
670    assert( tr_ptrArrayEmpty( t->scraping ) );
671
672    if( trackerSupportsScrape( t ) && !tr_ptrArrayEmpty( t->scrapeQueue ) )
673    {
674        int i, n, len, addr_len, ask_n;
675        char *march, *uri;
676        Torrent ** torrents =
677            (Torrent**) tr_ptrArrayPeek( t->scrapeQueue, &n );
678        struct evhttp_connection * evcon;
679        struct evhttp_request *req;
680
681        ask_n = n;
682        if( ask_n > t->multiscrapeMax )
683            ask_n = t->multiscrapeMax;
684
685        /**
686        ***  Build the scrape request
687        **/
688
689        len = addr_len = strlen( address->scrape );
690        for( i=0; i<ask_n; ++i )
691            len += strlen("&info_hash=") + strlen(torrents[i]->escaped);
692        ++len; /* for nul */
693        uri = march = tr_new( char, len );
694        memcpy( march, address->scrape, addr_len ); march += addr_len;
695        for( i=0; i<ask_n; ++i ) {
696            const int elen = strlen( torrents[i]->escaped );
697            *march++ = i?'&':'?';
698            memcpy( march, "info_hash=", 10); march += 10;
699            memcpy( march, torrents[i]->escaped, elen ); march += elen;
700        }
701        *march++ = '\0';
702        assert( march - uri == len );
703
704        /* move the first n_ask torrents from scrapeQueue to scraping */
705        for( i=0; i<ask_n; ++i )
706            tr_ptrArrayInsertSorted( t->scraping, torrents[i], torrentCompare );
707        tr_ptrArrayErase( t->scrapeQueue, 0, ask_n );
708
709        /* ping the tracker */
710        tr_inf( "Sending scrape to tracker %s:%d: %s",
711                address->address, address->port, uri );
712        evcon = getConnection( t, address->address, address->port );
713        req = evhttp_request_new( onScrapeResponse, t );
714        assert( req );
715        addCommonHeaders( t, req );
716        tr_evhttp_make_request( t->handle, evcon, req, EVHTTP_REQ_GET, uri );
717    }
718
719    t->scrapeTimer = NULL;
720    return FALSE;
721}
722
723/***
724****
725****  TRACKER REQUESTS
726****
727***/
728
729static int
730torrentIsRunning( const Torrent * tor )
731{
732    return tor && tor->isRunning;
733}
734
735static char*
736buildTrackerRequestURI( const Torrent     * tor,
737                        const tr_torrent  * torrent,
738                        const char        * eventName )
739{
740    const int stopping = !strcmp( eventName, "stopped" );
741    const int numwant = stopping ? 0 : NUMWANT;
742    char buf[4096];
743
744    snprintf( buf, sizeof(buf), "%s"
745                                "?info_hash=%s"
746                                "&peer_id=%s"
747                                "&port=%d"
748                                "&uploaded=%"PRIu64
749                                "&downloaded=%"PRIu64
750                                "&corrupt=%"PRIu64
751                                "&left=%"PRIu64
752                                "&compact=1"
753                                "&numwant=%d"
754                                "&key=%s"
755                                "%s%s"
756                                "%s%s",
757        getCurrentAddress(tor->tracker)->announce,
758        tor->escaped,
759        tor->peer_id,
760        tr_sharedGetPublicPort( torrent->handle->shared ),
761        torrent->uploadedCur,
762        torrent->downloadedCur,
763        torrent->corruptCur,
764        tr_cpLeftUntilComplete( torrent->completion ),
765        numwant,
766        tor->tracker->key_param,
767        ( ( eventName && *eventName ) ? "&event=" : "" ),
768        ( ( eventName && *eventName ) ? eventName : "" ),
769        ( ( tor->trackerID && *tor->trackerID ) ? "&trackerid=" : "" ),
770        ( ( tor->trackerID && *tor->trackerID ) ? tor->trackerID : "" ) );
771
772    return tr_strdup( buf );
773}
774
775/* Convert to compact form */
776static uint8_t *
777parseOldPeers( benc_val_t * bePeers, int * peerCount )
778{
779    int i, count;
780    uint8_t * compact;
781
782    assert( bePeers->type == TYPE_LIST );
783
784    compact = tr_new( uint8_t, 6 * bePeers->val.l.count );
785
786    for( i=count=0; i<bePeers->val.l.count; ++i )
787    {
788        struct in_addr addr;
789        tr_port_t port;
790        benc_val_t * val;
791        benc_val_t * peer = &bePeers->val.l.vals[i];
792
793        val = tr_bencDictFind( peer, "ip" );
794        if( !val || val->type!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) )
795            continue;
796
797        memcpy( &compact[6 * count], &addr, 4 );
798
799        val = tr_bencDictFind( peer, "port" );
800        if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff )
801            continue;
802
803        port = htons( val->val.i );
804        memcpy( &compact[6 * count + 4], &port, 2 );
805        ++count;
806    }
807
808    *peerCount = count;
809    return compact;
810}
811
812/* handle braindead trackers whose minimums is higher
813   than the interval. */
814static void
815setAnnounceInterval( Tracker  * t,
816                     int        minimum,
817                     int        interval )
818{
819    assert( t != NULL );
820
821    if( minimum > 0 )
822        t->minAnnounceIntervalMsec = minimum;
823
824    if( interval > 0 )
825        t->announceIntervalMsec = interval;
826
827    if( t->announceIntervalMsec < t->minAnnounceIntervalMsec )
828        t->announceIntervalMsec = t->minAnnounceIntervalMsec;
829}
830
831static int onReannounceNow( void * vtor );
832
833struct response_user_data
834{
835    tr_handle * handle;
836    uint8_t hash[SHA_DIGEST_LENGTH];
837};
838
839static struct response_user_data*
840onTrackerResponseDataNew( Torrent * tor )
841{
842    struct response_user_data * data = tr_new( struct response_user_data, 1 );
843    data->handle = tor->tracker->handle;
844    memcpy( data->hash, tor->hash, SHA_DIGEST_LENGTH );
845    return data;
846}
847
848static void
849onStoppedResponse( struct evhttp_request * req UNUSED, void * handle UNUSED )
850{
851}
852
853static void
854onTrackerResponse( struct evhttp_request * req, void * vdata )
855{
856    char * errmsg;
857    Torrent * tor;
858    int isStopped;
859    int reannounceInterval;
860    struct response_user_data * data;
861    tr_torrent * t;
862
863    data = vdata;
864    t = tr_torrentFindFromHash( data->handle, data->hash );
865    tr_free( data );
866    if( t == NULL ) /* torrent has been closed */
867        return;
868
869    tor = t->tracker;
870    isStopped = !torrentIsRunning( tor );
871
872    tr_inf( "Torrent \"%s\" tracker response: %s",
873            tor->name,
874            ( req ? req->response_code_line : "(null)") );
875
876    if( req && ( req->response_code == HTTP_OK ) )
877    {
878        benc_val_t benc;
879        const int bencLoaded = !parseBencResponse( req, &benc );
880
881        publishErrorClear( tor );
882
883        if( bencLoaded && benc.type==TYPE_DICT )
884        {
885            benc_val_t * tmp;
886
887            if(( tmp = tr_bencDictFind( &benc, "failure reason" )))
888                publishErrorMessage( tor, tmp->val.s.s );
889
890            if(( tmp = tr_bencDictFind( &benc, "warning message" )))
891                publishWarningMessage( tor, tmp->val.s.s );
892
893            if(( tmp = tr_bencDictFind( &benc, "interval" )))
894                setAnnounceInterval( tor->tracker, -1, tmp->val.i * 1000 );
895
896            if(( tmp = tr_bencDictFind( &benc, "min interval" )))
897                setAnnounceInterval( tor->tracker, tmp->val.i * 1000, -1 );
898
899            if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
900                tor->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
901
902            if(( tmp = tr_bencDictFind( &benc, "complete" )))
903                tor->seeders = tmp->val.i;
904
905            if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
906                tor->leechers = tmp->val.i;
907
908            if(( tmp = tr_bencDictFind( &benc, "peers" )))
909            {
910                int peerCount = 0;
911                uint8_t * peerCompact = NULL;
912
913                if( tmp->type == TYPE_LIST ) /* original protocol */
914                {
915                    if( tmp->val.l.count > 0 )
916                        peerCompact = parseOldPeers( tmp, &peerCount );
917                }
918                else if( tmp->type == TYPE_STR ) /* "compact" extension */
919                {
920                    if( tmp->val.s.i >= 6 )
921                    {
922                        peerCount = tmp->val.s.i / 6;
923                        peerCompact = tr_new( uint8_t, tmp->val.s.i );
924                        memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
925                    }
926                }
927
928                publishNewPeers( tor, peerCount, peerCompact );
929                tr_free( peerCompact );
930            }
931        }
932
933        reannounceInterval = isStopped
934            ? -1
935            : tor->tracker->announceIntervalMsec;
936
937        if( bencLoaded )
938            tr_bencFree( &benc );
939    }
940    else
941    {
942        tr_inf( "Bad response from tracker '%s' on request '%s' "
943                "for torrent '%s'... trying again in 30 seconds",
944                tor->tracker->primaryAddress,
945                tor->lastRequest,
946                tor->name );
947
948        reannounceInterval = 30 * 1000;
949    }
950
951    if (( errmsg = updateAddresses( tor->tracker, req ) )) {
952        publishErrorMessage( tor, errmsg );
953        tr_err( errmsg );
954        tr_free( errmsg );
955    }
956
957    if( !isStopped && reannounceInterval>0 ) {
958        tr_dbg( "torrent '%s' reannouncing in %d seconds",
959                tor->name, (reannounceInterval/1000) );
960        tr_timerFree( &tor->reannounceTimer );
961        tor->reannounceTimer = tr_timerNew( tor->tracker->handle, onReannounceNow, tor, reannounceInterval );
962        tor->manualAnnounceAllowedAt
963                           = tr_date() + MANUAL_ANNOUNCE_INTERVAL_MSEC;
964    }
965}
966
967static int
968sendTrackerRequest( void * vt, const char * eventName )
969{
970    Torrent * t = (Torrent *) vt;
971    const tr_tracker_info * address = getCurrentAddress( t->tracker );
972    char * uri;
973    struct evhttp_connection * evcon;
974    const tr_torrent * tor;
975
976    tor = tr_torrentFindFromHash( t->tracker->handle, t->hash );
977    if( tor == NULL )
978        return FALSE;   
979
980    uri = buildTrackerRequestURI( t, tor, eventName );
981
982    tr_inf( "Torrent \"%s\" sending '%s' to tracker %s:%d: %s",
983            t->name,
984            (eventName ? eventName : "periodic announce"),
985            address->address, address->port,
986            uri );
987
988    /* kill any pending requests */
989    tr_timerFree( &t->reannounceTimer );
990
991    evcon = getConnection( t->tracker, address->address, address->port );
992    if ( !evcon ) {
993        tr_err( "Can't make a connection to %s:%d", address->address, address->port );
994        tr_free( uri );
995    } else {
996        struct evhttp_request * httpReq;
997        tr_free( t->lastRequest );
998        t->lastRequest = tr_strdup( eventName );
999        if( eventName && !strcmp( eventName, "stopped" ) )
1000            httpReq = evhttp_request_new( onStoppedResponse, t->tracker->handle );
1001        else
1002            httpReq = evhttp_request_new( onTrackerResponse, onTrackerResponseDataNew(t) );
1003        addCommonHeaders( t->tracker, httpReq );
1004        tr_evhttp_make_request( t->tracker->handle, evcon,
1005                                httpReq, EVHTTP_REQ_GET, uri );
1006    }
1007
1008    return FALSE;
1009}
1010
1011static int
1012onReannounceNow( void * vtor )
1013{
1014    Torrent * tor = (Torrent *) vtor;
1015    sendTrackerRequest( tor, "" );
1016    tor->reannounceTimer = NULL;
1017    return FALSE;
1018}
1019
1020/***
1021****  PUBLIC
1022***/
1023
1024tr_publisher_tag
1025tr_trackerSubscribe( Torrent             * tor,
1026                     tr_delivery_func      func,
1027                     void                * user_data )
1028{
1029    return tr_publisherSubscribe( tor->publisher, func, user_data );
1030}
1031
1032void
1033tr_trackerUnsubscribe( Torrent           * tor,
1034                       tr_publisher_tag    tag )
1035{
1036    tr_publisherUnsubscribe( tor->publisher, tag );
1037}
1038
1039const tr_tracker_info *
1040tr_trackerGetAddress( const Torrent * tor )
1041{
1042    return getCurrentAddress( tor->tracker );
1043}
1044
1045int
1046tr_trackerCanManualAnnounce ( const Torrent * tor )
1047{
1048    /* return true if this torrent's currently running
1049       and it's been long enough since the last announce */
1050    return ( torrentIsRunning( tor ) )
1051        && ( tr_date() >= tor->manualAnnounceAllowedAt );
1052}
1053
1054void
1055tr_trackerGetCounts( const Torrent       * tor,
1056                     int                 * setme_completedCount,
1057                     int                 * setme_leecherCount,
1058                     int                 * setme_seederCount )
1059{
1060    if( setme_completedCount )
1061       *setme_completedCount = tor->timesDownloaded;
1062
1063    if( setme_leecherCount )
1064       *setme_leecherCount = tor->leechers;
1065
1066    if( setme_seederCount )
1067       *setme_seederCount = tor->seeders;
1068}
1069
1070void
1071tr_trackerStart( Torrent * tor )
1072{
1073    tr_peerIdNew( tor->peer_id, sizeof(tor->peer_id) );
1074
1075    if( !tor->reannounceTimer && !tor->isRunning )
1076    {
1077        tor->isRunning = 1;
1078        sendTrackerRequest( tor, "started" );
1079    }
1080}
1081
1082void
1083tr_trackerReannounce( Torrent * tor )
1084{
1085    sendTrackerRequest( tor, "started" );
1086}
1087
1088void
1089tr_trackerCompleted( Torrent * tor )
1090{
1091    sendTrackerRequest( tor, "completed" );
1092}
1093
1094void
1095tr_trackerStop( Torrent * tor )
1096{
1097    if( tor->isRunning )
1098    {
1099        tor->isRunning = 0;
1100        sendTrackerRequest( tor, "stopped" );
1101    }
1102}
1103
1104void
1105tr_trackerChangeMyPort( Torrent * tor )
1106{
1107    if( torrentIsRunning( tor ) )
1108        tr_trackerReannounce( tor );
1109}
Note: See TracBrowser for help on using the repository browser.