source: trunk/libtransmission/tracker.c @ 3332

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

fix crash reported by Waldorf in http://pastebin.ca/729980

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