source: trunk/libtransmission/tracker.c @ 2814

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

fix shutdown error in the new tracker code, reported by joshe

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