source: trunk/libtransmission/tracker.c @ 3337

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

fix recent tracker error reported by John_Clay that made it slower to pick up peers

  • Property svn:keywords set to Date Rev Author Id
File size: 32.0 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 3337 2007-10-09 04:50:10Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isalnum */
15#include <limits.h> /* INT_MAX */
16#include <stdio.h> /* snprintf */
17#include <stdlib.h>
18#include <string.h> /* strcmp, strchr */
19#include <sys/queue.h> /* for evhttp */
20#include <sys/types.h> /* for evhttp */
21
22#include <event.h>
23#include <evhttp.h>
24
25#include "transmission.h"
26#include "bencode.h"
27#include "completion.h"
28#include "net.h"
29#include "ptrarray.h"
30#include "publish.h"
31#include "shared.h"
32#include "tracker.h"
33#include "trevent.h"
34#include "utils.h"
35
36#define MINUTES_TO_MSEC(N) ((N) * 60 * 1000)
37
38/* manual announces via "update tracker" are allowed this frequently */
39#define MANUAL_ANNOUNCE_INTERVAL_MSEC (MINUTES_TO_MSEC(10))
40
41/* unless the tracker tells us otherwise, rescrape this frequently */
42#define DEFAULT_SCRAPE_INTERVAL_MSEC (MINUTES_TO_MSEC(15))
43
44/* unless the tracker tells us otherwise, reannounce this frequently */
45#define DEFAULT_ANNOUNCE_INTERVAL_MSEC (MINUTES_TO_MSEC(20))
46
47/* this is how long we'll leave a request hanging before timeout */
48#define TIMEOUT_INTERVAL_SEC 40
49#define STOPPING_TIMEOUT_INTERVAL_SEC 8
50
51/* the value of the 'numwant' argument passed in tracker requests.
52 * this should be big, but if it's *too* big trackers will ignore it */
53#define NUMWANT 50
54
55/* the length of the 'key' argument passed in tracker requests */
56#define TR_KEY_LEN 10
57
58
59/**
60***
61**/
62
63typedef struct
64{
65    tr_handle * handle;
66
67    tr_ptrArray * torrents;
68    tr_ptrArray * scraping;
69    tr_ptrArray * scrapeQueue;
70
71    /* these are set from the latest scrape or tracker response */
72    int announceIntervalMsec;
73    int minAnnounceIntervalMsec;
74    int scrapeIntervalMsec;
75
76    /* calculated when we get fewer scrapes
77       back than we asked for */
78    int multiscrapeMax;
79
80    tr_tracker_info * redirect;
81    tr_tracker_info * addresses;
82    int addressIndex;
83    int addressCount;
84    int * tierFronts;
85
86    char * primaryAddress;
87
88    /* sent as the "key" argument in tracker requests
89       to verify us if our IP address changes.
90       This is immutable for the life of the tracker object. */
91    char key_param[TR_KEY_LEN+1];
92
93    tr_timer * scrapeTimer;
94}
95Tracker;
96
97/* this is the Torrent struct, but since it's the pointer
98   passed around in the public API of this tracker module,
99   its *public* name is tr_tracker... wheee */
100typedef struct tr_tracker
101{
102    tr_publisher_t * publisher;
103
104    /* torrent hash string */
105    uint8_t hash[SHA_DIGEST_LENGTH];
106    char escaped[SHA_DIGEST_LENGTH * 3 + 1];
107    char * name;
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 * scrapeTimer;
133    tr_timer * reannounceTimer;
134
135    unsigned int isRunning : 1;
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
159static int
160freeConnection( void * evcon )
161{
162    evhttp_connection_free( evcon );
163    return FALSE;
164}
165
166static void
167connectionClosedCB( struct evhttp_connection * evcon, void * handle )
168{
169    tr_timerNew( handle, freeConnection, evcon, 100 );
170}
171
172static struct evhttp_connection*
173getConnection( Tracker * tracker, const char * address, int port )
174{
175    struct evhttp_connection * c = evhttp_connection_new( address, port );
176    evhttp_connection_set_closecb( c, connectionClosedCB, tracker->handle );
177    return c;
178}
179
180/***
181****  PUBLISH
182***/
183
184static const tr_tracker_event_t emptyEvent = { 0, NULL, NULL, NULL, 0 };
185
186static void
187publishMessage( Torrent * tor, const char * msg, int type )
188{
189    tr_tracker_event_t event = emptyEvent;
190    event.hash = tor->hash;
191    event.messageType = type;
192    event.text = msg;
193    tr_publisherPublish( tor->publisher, tor, &event );
194}
195
196static void
197publishErrorClear( Torrent * tor )
198{
199    publishMessage( tor, NULL, TR_TRACKER_ERROR_CLEAR );
200}
201
202static void
203publishErrorMessage( Torrent * tor, const char * msg )
204{
205    publishMessage( tor, msg, TR_TRACKER_ERROR );
206}
207
208static void
209publishWarningMessage( Torrent * tor, const char * msg )
210{
211    publishMessage( tor, msg, TR_TRACKER_WARNING );
212}
213
214static void
215publishNewPeers( Torrent * tor, int count, uint8_t * peers )
216{
217    tr_tracker_event_t event = emptyEvent;
218    event.hash = tor->hash;
219    event.messageType = TR_TRACKER_PEERS;
220    event.peerCount = count;
221    event.peerCompact = peers;
222    tr_inf( "Torrent \"%s\" got %d new peers", tor->name, count );
223    tr_publisherPublish( tor->publisher, tor, &event );
224}
225
226/***
227****  LIFE CYCLE
228***/
229
230static tr_ptrArray *
231getTrackerLookupTable( void )
232{
233    static tr_ptrArray * myTrackers = NULL;
234    if( !myTrackers )
235         myTrackers = tr_ptrArrayNew( );
236    return myTrackers;
237}
238
239static void
240generateKeyParam( char * msg, int len )
241{
242    int i;
243    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
244    for( i=0; i<len; ++i )
245        *msg++ = pool[tr_rand(36)];
246    *msg = '\0';
247}
248
249static int onTrackerScrapeNow( void* );
250
251static void
252tr_trackerScrapeSoon( Tracker * t )
253{
254    /* don't start more than one scrape at once for the same tracker... */
255    if( !tr_ptrArrayEmpty( t->scraping ) )
256        return;
257
258    if( !t->scrapeTimer )
259         t->scrapeTimer = tr_timerNew( t->handle, onTrackerScrapeNow, t, 5000 );
260}
261
262static Tracker*
263getExistingTracker( const char * primaryAddress )
264{
265    tr_ptrArray * trackers = getTrackerLookupTable( );
266    Tracker tmp;
267    assert( primaryAddress && *primaryAddress );
268    tmp.primaryAddress = (char*) primaryAddress;
269    return tr_ptrArrayFindSorted( trackers, &tmp, trackerCompare );
270}
271
272static Tracker*
273tr_trackerGet( const tr_torrent * tor )
274{
275    const tr_info * info = &tor->info;
276    Tracker * t = getExistingTracker( info->primaryAddress );
277
278    if( t == NULL ) /* no such tracker.... create one */
279    {
280        int i, j, sum, *iwalk;
281        tr_tracker_info * nwalk;
282        tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress );
283
284        t = tr_new0( Tracker, 1 );
285        t->handle = tor->handle;
286        t->primaryAddress = tr_strdup( info->primaryAddress );
287        t->scrapeIntervalMsec      = DEFAULT_SCRAPE_INTERVAL_MSEC;
288        t->announceIntervalMsec    = DEFAULT_ANNOUNCE_INTERVAL_MSEC;
289        t->minAnnounceIntervalMsec = DEFAULT_ANNOUNCE_INTERVAL_MSEC;
290        t->multiscrapeMax = INT_MAX;
291        t->torrents    = tr_ptrArrayNew( );
292        t->scraping    = tr_ptrArrayNew( );
293        t->scrapeQueue = tr_ptrArrayNew( );
294        generateKeyParam( t->key_param, TR_KEY_LEN );
295
296        for( sum=i=0; i<info->trackerTiers; ++i )
297             sum += info->trackerList[i].count;
298        t->addresses = nwalk = tr_new0( tr_tracker_info, sum );
299        t->addressIndex = 0;
300        t->addressCount = sum;
301        t->tierFronts = iwalk = tr_new0( int, sum );
302
303        for( i=0; i<info->trackerTiers; ++i )
304        {
305            const int tierFront = nwalk - t->addresses;
306
307            for( j=0; j<info->trackerList[i].count; ++j )
308            {
309                const tr_tracker_info * src = &info->trackerList[i].list[j];
310                nwalk->address = tr_strdup( src->address );
311                nwalk->port = src->port;
312                nwalk->announce = tr_strdup( src->announce );
313                nwalk->scrape = tr_strdup( src->scrape );
314                ++nwalk;
315
316                *iwalk++ = tierFront;
317            }
318        }
319
320        assert( nwalk - t->addresses == sum );
321        assert( iwalk - t->tierFronts == sum );
322
323        tr_ptrArrayInsertSorted( getTrackerLookupTable( ), t, trackerCompare );
324    }
325
326    return t;
327}
328
329static Torrent *
330getExistingTorrent( Tracker * t, const uint8_t hash[SHA_DIGEST_LENGTH] )
331{
332    Torrent tmp;
333    memcpy( tmp.hash, hash, SHA_DIGEST_LENGTH );
334    return tr_ptrArrayFindSorted( t->torrents, &tmp, torrentCompare );
335}
336
337static void
338escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
339{
340    const uint8_t *end = in + in_len;
341    while( in != end )
342        if( isalnum(*in) )
343            *out++ = (char) *in++;
344        else 
345            out += snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
346    *out = '\0';
347}
348
349static void
350onTorrentFreeNow( void * vtor )
351{
352    Torrent * tor = (Torrent *) vtor;
353    Tracker * t = tor->tracker;
354
355    tr_ptrArrayRemoveSorted( t->torrents, tor, torrentCompare );
356    tr_ptrArrayRemoveSorted( t->scrapeQueue, tor, torrentCompare );
357    tr_ptrArrayRemoveSorted( t->scraping, tor, torrentCompare );
358
359    tr_timerFree( &tor->scrapeTimer );
360    tr_timerFree( &tor->reannounceTimer );
361    tr_publisherFree( &tor->publisher );
362    tr_free( tor->name );
363    tr_free( tor->trackerID );
364    tr_free( tor->lastRequest );
365    tr_free( tor );
366
367    if( tr_ptrArrayEmpty( t->torrents ) ) /* last one.. free the tracker too */
368    {
369        int i;
370        tr_ptrArrayRemoveSorted( getTrackerLookupTable( ), t, trackerCompare );
371
372        tr_ptrArrayFree( t->torrents, NULL );
373        tr_ptrArrayFree( t->scrapeQueue, NULL );
374        tr_ptrArrayFree( t->scraping, NULL );
375
376        for( i=0; i<t->addressCount; ++i )
377            tr_trackerInfoClear( &t->addresses[i] );
378
379        if( t->redirect ) {
380            tr_trackerInfoClear( t->redirect );
381            tr_free( t->redirect );
382        }
383
384        tr_timerFree( &t->scrapeTimer );
385
386        tr_free( t->primaryAddress );
387        tr_free( t->addresses );
388        tr_free( t->tierFronts );
389        tr_free( t );
390    }
391}
392
393void
394tr_trackerFree( Torrent * tor )
395{
396    tr_runInEventThread( tor->tracker->handle, onTorrentFreeNow, tor );
397}
398
399Torrent*
400tr_trackerNew( tr_torrent * torrent )
401{
402    Torrent * tor;
403    Tracker * t = tr_trackerGet( torrent );
404    assert( getExistingTorrent( t, torrent->info.hash ) == NULL );
405
406    /* create a new Torrent and queue it for scraping */
407    tor = tr_new0( Torrent, 1 );
408    tor->publisher = tr_publisherNew( );
409    tor->tracker = t;
410    /*tor->torrent = torrent;*/
411    tor->timesDownloaded = -1;
412    tor->seeders = -1;
413    tor->leechers = -1;
414    tor->manualAnnounceAllowedAt = ~0;
415    tor->name = tr_strdup( torrent->info.name );
416    memcpy( tor->hash, torrent->info.hash, SHA_DIGEST_LENGTH );
417    escape( tor->escaped, torrent->info.hash, SHA_DIGEST_LENGTH );
418    tr_ptrArrayInsertSorted( t->torrents, tor, torrentCompare );
419    tr_ptrArrayInsertSorted( t->scrapeQueue, tor, torrentCompare );
420    tr_trackerScrapeSoon( t );
421    return tor;
422}
423
424/***
425****  UTIL
426***/
427
428static int
429parseBencResponse( struct evhttp_request * req, benc_val_t * setme )
430{
431    const unsigned char * body = EVBUFFER_DATA( req->input_buffer );
432    const int bodylen = EVBUFFER_LENGTH( req->input_buffer );
433    int ret = 1;
434    int i;
435
436    for( i=0; ret && i<bodylen; ++i )
437        if( !tr_bencLoad( body+i, bodylen-1, setme, NULL ) )
438            ret = 0;
439    return ret;
440}
441
442static char*
443updateAddresses( Tracker * t, const struct evhttp_request * req )
444{
445    char * ret = NULL;
446    int moveToNextAddress = FALSE;
447
448    if( !req )
449    {
450        ret = tr_strdup( "No response from tracker -- will keep trying." );
451        tr_inf( ret );
452
453        moveToNextAddress = TRUE;
454    }
455    else if( req->response_code == HTTP_OK )
456    {
457        if( t->redirect != NULL )
458        {
459            /* multitracker spec: "if a connection with a tracker is
460               successful, it will be moved to the front of the tier." */
461            const int i = t->addressIndex;
462            const int j = t->tierFronts[i];
463            const tr_tracker_info swap = t->addresses[i];
464            t->addresses[i] = t->addresses[j];
465            t->addresses[j] = swap;
466        }
467    }
468    else if(    ( req->response_code == HTTP_MOVEPERM )
469             || ( req->response_code == HTTP_MOVETEMP ) )
470    {
471        const char * loc = evhttp_find_header( req->input_headers, "Location" );
472        tr_tracker_info tmp;
473        if( tr_trackerInfoInit( &tmp, loc, -1 ) ) /* a bad redirect? */
474        {
475            moveToNextAddress = TRUE;
476        }
477        else if( req->response_code == HTTP_MOVEPERM )
478        {
479            tr_tracker_info * cur = &t->addresses[t->addressIndex];
480            tr_trackerInfoClear( cur );
481            *cur = tmp;
482        }
483        else if( req->response_code == HTTP_MOVETEMP )
484        {
485            if( t->redirect == NULL )
486                t->redirect = tr_new0( tr_tracker_info, 1 );
487            else
488                tr_trackerInfoClear( t->redirect );
489            *t->redirect = tmp;
490        }
491    }
492    else 
493    {
494        ret = tr_strdup( "No response from tracker -- will keep trying." );
495        moveToNextAddress = TRUE;
496    }
497
498    if( moveToNextAddress )
499        if ( ++t->addressIndex >= t->addressCount )
500            t->addressIndex = 0;
501
502    return ret;
503}
504
505static tr_tracker_info *
506getCurrentAddress( const Tracker * t )
507{
508    assert( t->addresses != NULL );
509    assert( t->addressIndex >= 0 );
510    assert( t->addressIndex < t->addressCount );
511
512    return &t->addresses[t->addressIndex];
513}
514static int
515trackerSupportsScrape( const Tracker * t )
516{
517    const tr_tracker_info * info = getCurrentAddress( t );
518
519    return ( info != NULL )
520        && ( info->scrape != NULL )
521        && ( info->scrape[0] != '\0' );
522}
523
524
525static void
526addCommonHeaders( const Tracker * t,
527                  struct evhttp_request * req )
528{
529    char buf[1024];
530    tr_tracker_info * address = getCurrentAddress( t );
531    snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
532    evhttp_add_header( req->output_headers, "Host", buf );
533    evhttp_add_header( req->output_headers, "Connection", "close" );
534    evhttp_add_header( req->output_headers, "Content-Length", "0" );
535    evhttp_add_header( req->output_headers, "User-Agent",
536                                         TR_NAME "/" LONG_VERSION_STRING );
537}
538
539/***
540****
541****  SCRAPE
542****
543***/
544
545static int
546onTorrentScrapeNow( void * vtor )
547{
548    Torrent * tor = (Torrent *) vtor;
549    if( trackerSupportsScrape( tor->tracker ) )
550    {
551        if( tr_ptrArrayFindSorted( tor->tracker->scrapeQueue, tor, torrentCompare) == NULL )
552            tr_ptrArrayInsertSorted( tor->tracker->scrapeQueue, tor, torrentCompare );
553        tr_trackerScrapeSoon( tor->tracker );
554    }
555    tor->scrapeTimer = NULL;
556    return FALSE;
557}
558
559static void
560onScrapeResponse( struct evhttp_request * req, void * primaryAddress )
561{
562    char * errmsg;
563    Tracker * t;
564
565    tr_inf( "Got scrape response from  '%s': %s",
566            primaryAddress,
567            ( ( req && req->response_code_line ) ? req->response_code_line : "(null)") );
568
569    t = getExistingTracker( primaryAddress );
570    tr_free( primaryAddress );
571    if( t == NULL ) /* tracker has been closed... */
572        return;
573
574    if( req && ( req->response_code == HTTP_OK ) )
575    {
576        int numResponses = 0;
577        benc_val_t benc, *files;
578        const int n_scraping = tr_ptrArraySize( t->scraping );
579        const int bencLoaded = !parseBencResponse( req, &benc );
580
581        if( bencLoaded
582            && (( files = tr_bencDictFind( &benc, "files" ) ))
583            && ( files->type == TYPE_DICT ) )
584        {
585            int i;
586            for( i=0; i<files->val.l.count; i+=2 )
587            {
588                const uint8_t* hash =
589                    (const uint8_t*) files->val.l.vals[i].val.s.s;
590                benc_val_t *tmp, *flags;
591                benc_val_t *tordict = &files->val.l.vals[i+1];
592                Torrent * tor = getExistingTorrent( t, hash );
593                ++numResponses;
594                   
595                if( !tor )
596                    continue;
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;
686        struct evhttp_request *req;
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, TIMEOUT_INTERVAL_SEC );
721        req = evhttp_request_new( onScrapeResponse, tr_strdup(t->primaryAddress) );
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 isStopping = !strcmp( eventName, "stopped" );
749    const int numwant = isStopping ? 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
857onStoppedResponse( struct evhttp_request * req UNUSED, void * handle UNUSED )
858{
859}
860
861static void
862onTrackerResponse( struct evhttp_request * req, void * vdata )
863{
864    char * errmsg;
865    Torrent * tor;
866    int isStopped;
867    int reannounceInterval;
868    struct response_user_data * data;
869    tr_torrent * t;
870
871    data = vdata;
872    t = tr_torrentFindFromHash( data->handle, data->hash );
873    tr_free( data );
874    if( t == NULL ) /* torrent has been closed */
875        return;
876
877    tor = t->tracker;
878    isStopped = !torrentIsRunning( tor );
879
880    tr_inf( "Torrent \"%s\" tracker response: %s",
881            tor->name,
882            ( req ? req->response_code_line : "(null)") );
883
884    if( req && ( req->response_code == HTTP_OK ) )
885    {
886        benc_val_t benc;
887        const int bencLoaded = !parseBencResponse( req, &benc );
888
889        publishErrorClear( tor );
890
891        if( bencLoaded && benc.type==TYPE_DICT )
892        {
893            benc_val_t * tmp;
894
895            if(( tmp = tr_bencDictFind( &benc, "failure reason" )))
896                publishErrorMessage( tor, tmp->val.s.s );
897
898            if(( tmp = tr_bencDictFind( &benc, "warning message" )))
899                publishWarningMessage( tor, tmp->val.s.s );
900
901            if(( tmp = tr_bencDictFind( &benc, "interval" )))
902                setAnnounceInterval( tor->tracker, -1, tmp->val.i * 1000 );
903
904            if(( tmp = tr_bencDictFind( &benc, "min interval" )))
905                setAnnounceInterval( tor->tracker, tmp->val.i * 1000, -1 );
906
907            if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
908                tor->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
909
910            if(( tmp = tr_bencDictFind( &benc, "complete" )))
911                tor->seeders = tmp->val.i;
912
913            if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
914                tor->leechers = tmp->val.i;
915
916            if(( tmp = tr_bencDictFind( &benc, "peers" )))
917            {
918                int peerCount = 0;
919                uint8_t * peerCompact = NULL;
920
921                if( tmp->type == TYPE_LIST ) /* original protocol */
922                {
923                    if( tmp->val.l.count > 0 )
924                        peerCompact = parseOldPeers( tmp, &peerCount );
925                }
926                else if( tmp->type == TYPE_STR ) /* "compact" extension */
927                {
928                    if( tmp->val.s.i >= 6 )
929                    {
930                        peerCount = tmp->val.s.i / 6;
931                        peerCompact = tr_new( uint8_t, tmp->val.s.i );
932                        memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
933                    }
934                }
935
936                publishNewPeers( tor, peerCount, peerCompact );
937                tr_free( peerCompact );
938            }
939        }
940
941        reannounceInterval = isStopped
942            ? -1
943            : tor->tracker->announceIntervalMsec;
944
945        if( bencLoaded )
946            tr_bencFree( &benc );
947    }
948    else
949    {
950        tr_inf( "Bad response from tracker '%s' on request '%s' "
951                "for torrent '%s'... trying again in 30 seconds",
952                tor->tracker->primaryAddress,
953                tor->lastRequest,
954                tor->name );
955
956        reannounceInterval = 30 * 1000;
957    }
958
959    if (( errmsg = updateAddresses( tor->tracker, req ) )) {
960        publishErrorMessage( tor, errmsg );
961        tr_err( errmsg );
962        tr_free( errmsg );
963    }
964
965    if( !isStopped && reannounceInterval>0 ) {
966        tr_dbg( "torrent '%s' reannouncing in %d seconds",
967                tor->name, (reannounceInterval/1000) );
968        tr_timerFree( &tor->reannounceTimer );
969        tor->reannounceTimer = tr_timerNew( tor->tracker->handle, onReannounceNow, tor, reannounceInterval );
970        tor->manualAnnounceAllowedAt
971                           = tr_date() + MANUAL_ANNOUNCE_INTERVAL_MSEC;
972    }
973}
974
975static int
976sendTrackerRequest( void * vt, const char * eventName )
977{
978    Torrent * t = (Torrent *) vt;
979    const int isStopping = eventName && !strcmp( eventName, "stopped" );
980    const tr_tracker_info * address = getCurrentAddress( t->tracker );
981    char * uri;
982    struct evhttp_connection * evcon;
983    const tr_torrent * tor;
984
985    tor = tr_torrentFindFromHash( t->tracker->handle, t->hash );
986    if( tor == NULL )
987        return FALSE;   
988
989    uri = buildTrackerRequestURI( t, tor, eventName );
990
991    tr_inf( "Torrent \"%s\" sending '%s' to tracker %s:%d: %s",
992            t->name,
993            (eventName ? eventName : "periodic announce"),
994            address->address, address->port,
995            uri );
996
997    /* kill any pending requests */
998    tr_timerFree( &t->reannounceTimer );
999
1000    evcon = getConnection( t->tracker, address->address, address->port );
1001    if ( !evcon ) {
1002        tr_err( "Can't make a connection to %s:%d", address->address, address->port );
1003        tr_free( uri );
1004    } else {
1005        struct evhttp_request * httpReq;
1006        tr_free( t->lastRequest );
1007        t->lastRequest = tr_strdup( eventName );
1008        if( isStopping ) {
1009            evhttp_connection_set_timeout( evcon, STOPPING_TIMEOUT_INTERVAL_SEC );
1010            httpReq = evhttp_request_new( onStoppedResponse, t->tracker->handle );
1011        } else {
1012            evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
1013            httpReq = evhttp_request_new( onTrackerResponse, onTrackerResponseDataNew(t) );
1014        }
1015        addCommonHeaders( t->tracker, httpReq );
1016        tr_evhttp_make_request( t->tracker->handle, evcon,
1017                                httpReq, EVHTTP_REQ_GET, uri );
1018    }
1019
1020    return FALSE;
1021}
1022
1023static int
1024onReannounceNow( void * vtor )
1025{
1026    Torrent * tor = (Torrent *) vtor;
1027    sendTrackerRequest( tor, "" );
1028    tor->reannounceTimer = NULL;
1029    return FALSE;
1030}
1031
1032/***
1033****  PUBLIC
1034***/
1035
1036tr_publisher_tag
1037tr_trackerSubscribe( Torrent             * tor,
1038                     tr_delivery_func      func,
1039                     void                * user_data )
1040{
1041    return tr_publisherSubscribe( tor->publisher, func, user_data );
1042}
1043
1044void
1045tr_trackerUnsubscribe( Torrent           * tor,
1046                       tr_publisher_tag    tag )
1047{
1048    tr_publisherUnsubscribe( tor->publisher, tag );
1049}
1050
1051const tr_tracker_info *
1052tr_trackerGetAddress( const Torrent * tor )
1053{
1054    return getCurrentAddress( tor->tracker );
1055}
1056
1057int
1058tr_trackerCanManualAnnounce ( const Torrent * tor )
1059{
1060    /* return true if this torrent's currently running
1061       and it's been long enough since the last announce */
1062    return ( torrentIsRunning( tor ) )
1063        && ( tr_date() >= tor->manualAnnounceAllowedAt );
1064}
1065
1066void
1067tr_trackerGetCounts( const Torrent       * tor,
1068                     int                 * setme_completedCount,
1069                     int                 * setme_leecherCount,
1070                     int                 * setme_seederCount )
1071{
1072    if( setme_completedCount )
1073       *setme_completedCount = tor->timesDownloaded;
1074
1075    if( setme_leecherCount )
1076       *setme_leecherCount = tor->leechers;
1077
1078    if( setme_seederCount )
1079       *setme_seederCount = tor->seeders;
1080}
1081
1082void
1083tr_trackerStart( Torrent * tor )
1084{
1085    tr_peerIdNew( tor->peer_id, sizeof(tor->peer_id) );
1086
1087    if( !tor->reannounceTimer && !tor->isRunning )
1088    {
1089        tor->isRunning = 1;
1090        sendTrackerRequest( tor, "started" );
1091    }
1092}
1093
1094void
1095tr_trackerReannounce( Torrent * tor )
1096{
1097    sendTrackerRequest( tor, "started" );
1098}
1099
1100void
1101tr_trackerCompleted( Torrent * tor )
1102{
1103    sendTrackerRequest( tor, "completed" );
1104}
1105
1106void
1107tr_trackerStop( Torrent * tor )
1108{
1109    if( tor->isRunning )
1110    {
1111        tor->isRunning = 0;
1112        sendTrackerRequest( tor, "stopped" );
1113    }
1114}
1115
1116void
1117tr_trackerChangeMyPort( Torrent * tor )
1118{
1119    if( torrentIsRunning( tor ) )
1120        tr_trackerReannounce( tor );
1121}
Note: See TracBrowser for help on using the repository browser.