source: trunk/libtransmission/tracker.c @ 2806

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

fix crash-on-startup bug reported by Lacrocivious

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