source: trunk/libtransmission/tracker.c @ 3167

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

better handling of tracker error messages.

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