source: trunk/libtransmission/tracker.c @ 3117

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

clamp down on the flood of debug messages.

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