source: trunk/libtransmission/tracker.c @ 2853

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

added mirek's slovakian translation. added mirek, Niels Provos, and TooMuchTime? to the `thanks' sectio of AUTHORS

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