source: trunk/libtransmission/tracker.c @ 3295

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