source: trunk/libtransmission/tracker.c @ 2838

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

add the content-length header. I'm not getting any tracker errors anymore; please let me know if your experience differs

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