source: trunk/libtransmission/tracker.c @ 3318

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

get rid of a confusing error message that describes a harmless situation

  • Property svn:keywords set to Date Rev Author Id
File size: 31.5 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 3318 2007-10-07 16:25:02Z 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                    continue;
588
589                publishErrorClear( tor );
590
591                if(( tmp = tr_bencDictFind( tordict, "complete" )))
592                    tor->seeders = tmp->val.i;
593
594                if(( tmp = tr_bencDictFind( tordict, "incomplete" )))
595                    tor->leechers = tmp->val.i;
596
597                if(( tmp = tr_bencDictFind( tordict, "downloaded" )))
598                    tor->timesDownloaded = tmp->val.i;
599
600                if(( flags = tr_bencDictFind( tordict, "flags" )))
601                    if(( tmp = tr_bencDictFind( flags, "min_request_interval")))
602                        t->scrapeIntervalMsec = tmp->val.i * 1000;
603
604                tr_ptrArrayRemoveSorted( t->scraping, tor, torrentCompare );
605
606                tr_timerFree( &tor->scrapeTimer );
607                tor->scrapeTimer = tr_timerNew( t->handle, onTorrentScrapeNow, tor, t->scrapeIntervalMsec );
608                tr_dbg( "Torrent '%s' scrape successful."
609                        "  Rescraping in %d seconds",
610                        tor->name, t->scrapeIntervalMsec/1000 );
611            }
612
613            if( !files->val.l.count )
614            {
615                /* got an empty files dictionary!  This probably means the
616                   torrents we're scraping have expired from the tracker,
617                   so make sure they're stopped.  It also means any previous
618                   changes to multiscrapeMax are suspect, so reset that. */
619
620                int n;
621                Torrent ** torrents = (Torrent**)
622                    tr_ptrArrayPeek( t->scraping, &n );
623                for( i=0; i<n; ++i )
624                    tr_trackerStop( torrents[i] );
625                tr_ptrArrayClear( t->scraping );
626
627                t->multiscrapeMax = INT_MAX;
628            }
629        }
630
631        if( bencLoaded )
632            tr_bencFree( &benc );
633
634        /* if the tracker gave us back fewer torrents than we
635           thought we should get, maybe our multiscrape string
636           is too big... limit it based on how many we got back */
637        if( ( 0 < numResponses ) && ( numResponses < n_scraping ) )
638            t->multiscrapeMax = numResponses;
639    }
640
641    if (( errmsg = updateAddresses( t, req ) ))
642        tr_err( errmsg );
643
644    if( !tr_ptrArrayEmpty( t->scraping ) )
645    {
646        int i, n;
647        Torrent ** torrents =
648            (Torrent**) tr_ptrArrayPeek( t->scraping, &n );
649        for( i=0; i<n; ++i ) {
650            if( errmsg != NULL )
651                publishErrorMessage( torrents[i], errmsg );
652            onTorrentScrapeNow( torrents[i] );
653        }
654        tr_ptrArrayClear( t->scraping );
655    }
656    tr_free( errmsg );
657
658    if( !tr_ptrArrayEmpty( t->scrapeQueue ) )
659        tr_trackerScrapeSoon( t );
660}
661
662static int
663onTrackerScrapeNow( void * vt )
664{
665    Tracker * t = (Tracker*) vt;
666    const tr_tracker_info * address = getCurrentAddress( t );
667
668    assert( tr_ptrArrayEmpty( t->scraping ) );
669
670    if( trackerSupportsScrape( t ) && !tr_ptrArrayEmpty( t->scrapeQueue ) )
671    {
672        int i, n, len, addr_len, ask_n;
673        char *march, *uri;
674        Torrent ** torrents =
675            (Torrent**) tr_ptrArrayPeek( t->scrapeQueue, &n );
676        struct evhttp_connection * evcon;
677        struct evhttp_request *req;
678
679        ask_n = n;
680        if( ask_n > t->multiscrapeMax )
681            ask_n = t->multiscrapeMax;
682
683        /**
684        ***  Build the scrape request
685        **/
686
687        len = addr_len = strlen( address->scrape );
688        for( i=0; i<ask_n; ++i )
689            len += strlen("&info_hash=") + strlen(torrents[i]->escaped);
690        ++len; /* for nul */
691        uri = march = tr_new( char, len );
692        memcpy( march, address->scrape, addr_len ); march += addr_len;
693        for( i=0; i<ask_n; ++i ) {
694            const int elen = strlen( torrents[i]->escaped );
695            *march++ = i?'&':'?';
696            memcpy( march, "info_hash=", 10); march += 10;
697            memcpy( march, torrents[i]->escaped, elen ); march += elen;
698        }
699        *march++ = '\0';
700        assert( march - uri == len );
701
702        /* move the first n_ask torrents from scrapeQueue to scraping */
703        for( i=0; i<ask_n; ++i )
704            tr_ptrArrayInsertSorted( t->scraping, torrents[i], torrentCompare );
705        tr_ptrArrayErase( t->scrapeQueue, 0, ask_n );
706
707        /* ping the tracker */
708        tr_inf( "Sending scrape to tracker %s:%d: %s",
709                address->address, address->port, uri );
710        evcon = getConnection( t, address->address, address->port );
711        req = evhttp_request_new( onScrapeResponse, t );
712        assert( req );
713        addCommonHeaders( t, req );
714        tr_evhttp_make_request( t->handle, evcon, req, EVHTTP_REQ_GET, uri );
715    }
716
717    t->scrapeTimer = NULL;
718    return FALSE;
719}
720
721/***
722****
723****  TRACKER REQUESTS
724****
725***/
726
727static int
728torrentIsRunning( const Torrent * tor )
729{
730    return tor && tor->isRunning;
731}
732
733static char*
734buildTrackerRequestURI( const Torrent     * tor,
735                        const tr_torrent  * torrent,
736                        const char        * eventName )
737{
738    const int stopping = !strcmp( eventName, "stopped" );
739    const int numwant = stopping ? 0 : NUMWANT;
740    char buf[4096];
741
742    snprintf( buf, sizeof(buf), "%s"
743                                "?info_hash=%s"
744                                "&peer_id=%s"
745                                "&port=%d"
746                                "&uploaded=%"PRIu64
747                                "&downloaded=%"PRIu64
748                                "&corrupt=%"PRIu64
749                                "&left=%"PRIu64
750                                "&compact=1"
751                                "&numwant=%d"
752                                "&key=%s"
753                                "%s%s"
754                                "%s%s",
755        getCurrentAddress(tor->tracker)->announce,
756        tor->escaped,
757        tor->peer_id,
758        tr_sharedGetPublicPort( torrent->handle->shared ),
759        torrent->uploadedCur,
760        torrent->downloadedCur,
761        torrent->corruptCur,
762        tr_cpLeftUntilComplete( torrent->completion ),
763        numwant,
764        tor->tracker->key_param,
765        ( ( eventName && *eventName ) ? "&event=" : "" ),
766        ( ( eventName && *eventName ) ? eventName : "" ),
767        ( ( tor->trackerID && *tor->trackerID ) ? "&trackerid=" : "" ),
768        ( ( tor->trackerID && *tor->trackerID ) ? tor->trackerID : "" ) );
769
770    return tr_strdup( buf );
771}
772
773/* Convert to compact form */
774static uint8_t *
775parseOldPeers( benc_val_t * bePeers, int * peerCount )
776{
777    int i, count;
778    uint8_t * compact;
779
780    assert( bePeers->type == TYPE_LIST );
781
782    compact = tr_new( uint8_t, 6 * bePeers->val.l.count );
783
784    for( i=count=0; i<bePeers->val.l.count; ++i )
785    {
786        struct in_addr addr;
787        tr_port_t port;
788        benc_val_t * val;
789        benc_val_t * peer = &bePeers->val.l.vals[i];
790
791        val = tr_bencDictFind( peer, "ip" );
792        if( !val || val->type!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) )
793            continue;
794
795        memcpy( &compact[6 * count], &addr, 4 );
796
797        val = tr_bencDictFind( peer, "port" );
798        if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff )
799            continue;
800
801        port = htons( val->val.i );
802        memcpy( &compact[6 * count + 4], &port, 2 );
803        ++count;
804    }
805
806    *peerCount = count;
807    return compact;
808}
809
810/* handle braindead trackers whose minimums is higher
811   than the interval. */
812static void
813setAnnounceInterval( Tracker  * t,
814                     int        minimum,
815                     int        interval )
816{
817    assert( t != NULL );
818
819    if( minimum > 0 )
820        t->minAnnounceIntervalMsec = minimum;
821
822    if( interval > 0 )
823        t->announceIntervalMsec = interval;
824
825    if( t->announceIntervalMsec < t->minAnnounceIntervalMsec )
826        t->announceIntervalMsec = t->minAnnounceIntervalMsec;
827}
828
829static int onReannounceNow( void * vtor );
830
831struct response_user_data
832{
833    tr_handle * handle;
834    uint8_t hash[SHA_DIGEST_LENGTH];
835};
836
837static struct response_user_data*
838onTrackerResponseDataNew( Torrent * tor )
839{
840    struct response_user_data * data = tr_new( struct response_user_data, 1 );
841    data->handle = tor->tracker->handle;
842    memcpy( data->hash, tor->hash, SHA_DIGEST_LENGTH );
843    return data;
844}
845
846static void
847onStoppedResponse( struct evhttp_request * req UNUSED, void * handle UNUSED )
848{
849}
850
851static void
852onTrackerResponse( struct evhttp_request * req, void * vdata )
853{
854    char * errmsg;
855    Torrent * tor;
856    int isStopped;
857    int reannounceInterval;
858    struct response_user_data * data;
859    tr_torrent * t;
860
861    data = vdata;
862    t = tr_torrentFindFromHash( data->handle, data->hash );
863    tr_free( data );
864    if( t == NULL ) /* torrent has been closed */
865        return;
866
867    tor = t->tracker;
868    isStopped = !torrentIsRunning( tor );
869
870    tr_inf( "Torrent \"%s\" tracker response: %s",
871            tor->name,
872            ( req ? req->response_code_line : "(null)") );
873
874    if( req && ( req->response_code == HTTP_OK ) )
875    {
876        benc_val_t benc;
877        const int bencLoaded = !parseBencResponse( req, &benc );
878
879        publishErrorClear( tor );
880
881        if( bencLoaded && benc.type==TYPE_DICT )
882        {
883            benc_val_t * tmp;
884
885            if(( tmp = tr_bencDictFind( &benc, "failure reason" )))
886                publishErrorMessage( tor, tmp->val.s.s );
887
888            if(( tmp = tr_bencDictFind( &benc, "warning message" )))
889                publishWarningMessage( tor, tmp->val.s.s );
890
891            if(( tmp = tr_bencDictFind( &benc, "interval" )))
892                setAnnounceInterval( tor->tracker, -1, tmp->val.i * 1000 );
893
894            if(( tmp = tr_bencDictFind( &benc, "min interval" )))
895                setAnnounceInterval( tor->tracker, tmp->val.i * 1000, -1 );
896
897            if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
898                tor->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
899
900            if(( tmp = tr_bencDictFind( &benc, "complete" )))
901                tor->seeders = tmp->val.i;
902
903            if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
904                tor->leechers = tmp->val.i;
905
906            if(( tmp = tr_bencDictFind( &benc, "peers" )))
907            {
908                int peerCount = 0;
909                uint8_t * peerCompact = NULL;
910
911                if( tmp->type == TYPE_LIST ) /* original protocol */
912                {
913                    if( tmp->val.l.count > 0 )
914                        peerCompact = parseOldPeers( tmp, &peerCount );
915                }
916                else if( tmp->type == TYPE_STR ) /* "compact" extension */
917                {
918                    if( tmp->val.s.i >= 6 )
919                    {
920                        peerCount = tmp->val.s.i / 6;
921                        peerCompact = tr_new( uint8_t, tmp->val.s.i );
922                        memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
923                    }
924                }
925
926                publishNewPeers( tor, peerCount, peerCompact );
927                tr_free( peerCompact );
928            }
929        }
930
931        reannounceInterval = isStopped
932            ? -1
933            : tor->tracker->announceIntervalMsec;
934
935        if( bencLoaded )
936            tr_bencFree( &benc );
937    }
938    else
939    {
940        tr_inf( "Bad response from tracker '%s' on request '%s' "
941                "for torrent '%s'... trying again in 30 seconds",
942                tor->tracker->primaryAddress,
943                tor->lastRequest,
944                tor->name );
945
946        reannounceInterval = 30 * 1000;
947    }
948
949    if (( errmsg = updateAddresses( tor->tracker, req ) )) {
950        publishErrorMessage( tor, errmsg );
951        tr_err( errmsg );
952        tr_free( errmsg );
953    }
954
955    if( !isStopped && reannounceInterval>0 ) {
956        tr_dbg( "torrent '%s' reannouncing in %d seconds",
957                tor->name, (reannounceInterval/1000) );
958        tr_timerFree( &tor->reannounceTimer );
959        tor->reannounceTimer = tr_timerNew( tor->tracker->handle, onReannounceNow, tor, reannounceInterval );
960        tor->manualAnnounceAllowedAt
961                           = tr_date() + MANUAL_ANNOUNCE_INTERVAL_MSEC;
962    }
963}
964
965static int
966sendTrackerRequest( void * vt, const char * eventName )
967{
968    Torrent * t = (Torrent *) vt;
969    const tr_tracker_info * address = getCurrentAddress( t->tracker );
970    char * uri;
971    struct evhttp_connection * evcon;
972    const tr_torrent * tor;
973
974    tor = tr_torrentFindFromHash( t->tracker->handle, t->hash );
975    if( tor == NULL )
976        return FALSE;   
977
978    uri = buildTrackerRequestURI( t, tor, eventName );
979
980    tr_inf( "Torrent \"%s\" sending '%s' to tracker %s:%d: %s",
981            t->name,
982            (eventName ? eventName : "periodic announce"),
983            address->address, address->port,
984            uri );
985
986    /* kill any pending requests */
987    tr_timerFree( &t->reannounceTimer );
988
989    evcon = getConnection( t->tracker, address->address, address->port );
990    if ( !evcon ) {
991        tr_err( "Can't make a connection to %s:%d", address->address, address->port );
992        tr_free( uri );
993    } else {
994        struct evhttp_request * httpReq;
995        tr_free( t->lastRequest );
996        t->lastRequest = tr_strdup( eventName );
997        if( eventName && !strcmp( eventName, "stopped" ) )
998            httpReq = evhttp_request_new( onStoppedResponse, t->tracker->handle );
999        else
1000            httpReq = evhttp_request_new( onTrackerResponse, onTrackerResponseDataNew(t) );
1001        addCommonHeaders( t->tracker, httpReq );
1002        tr_evhttp_make_request( t->tracker->handle, evcon,
1003                                httpReq, EVHTTP_REQ_GET, uri );
1004    }
1005
1006    return FALSE;
1007}
1008
1009static int
1010onReannounceNow( void * vtor )
1011{
1012    Torrent * tor = (Torrent *) vtor;
1013    sendTrackerRequest( tor, "" );
1014    tor->reannounceTimer = NULL;
1015    return FALSE;
1016}
1017
1018/***
1019****  PUBLIC
1020***/
1021
1022tr_publisher_tag
1023tr_trackerSubscribe( Torrent             * tor,
1024                     tr_delivery_func      func,
1025                     void                * user_data )
1026{
1027    return tr_publisherSubscribe( tor->publisher, func, user_data );
1028}
1029
1030void
1031tr_trackerUnsubscribe( Torrent           * tor,
1032                       tr_publisher_tag    tag )
1033{
1034    tr_publisherUnsubscribe( tor->publisher, tag );
1035}
1036
1037const tr_tracker_info *
1038tr_trackerGetAddress( const Torrent * tor )
1039{
1040    return getCurrentAddress( tor->tracker );
1041}
1042
1043int
1044tr_trackerCanManualAnnounce ( const Torrent * tor )
1045{
1046    /* return true if this torrent's currently running
1047       and it's been long enough since the last announce */
1048    return ( torrentIsRunning( tor ) )
1049        && ( tr_date() >= tor->manualAnnounceAllowedAt );
1050}
1051
1052void
1053tr_trackerGetCounts( const Torrent       * tor,
1054                     int                 * setme_completedCount,
1055                     int                 * setme_leecherCount,
1056                     int                 * setme_seederCount )
1057{
1058    if( setme_completedCount )
1059       *setme_completedCount = tor->timesDownloaded;
1060
1061    if( setme_leecherCount )
1062       *setme_leecherCount = tor->leechers;
1063
1064    if( setme_seederCount )
1065       *setme_seederCount = tor->seeders;
1066}
1067
1068void
1069tr_trackerStart( Torrent * tor )
1070{
1071    tr_peerIdNew( tor->peer_id, sizeof(tor->peer_id) );
1072
1073    if( !tor->reannounceTimer && !tor->isRunning )
1074    {
1075        tor->isRunning = 1;
1076        sendTrackerRequest( tor, "started" );
1077    }
1078}
1079
1080void
1081tr_trackerReannounce( Torrent * tor )
1082{
1083    sendTrackerRequest( tor, "started" );
1084}
1085
1086void
1087tr_trackerCompleted( Torrent * tor )
1088{
1089    sendTrackerRequest( tor, "completed" );
1090}
1091
1092void
1093tr_trackerStop( Torrent * tor )
1094{
1095    if( tor->isRunning )
1096    {
1097        tor->isRunning = 0;
1098        sendTrackerRequest( tor, "stopped" );
1099    }
1100}
1101
1102void
1103tr_trackerChangeMyPort( Torrent * tor )
1104{
1105    if( torrentIsRunning( tor ) )
1106        tr_trackerReannounce( tor );
1107}
Note: See TracBrowser for help on using the repository browser.