source: trunk/libtransmission/tracker.c @ 3439

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

Follow uTorrent 1.8's behavior of allowing a manual reannounce every max(60 seconds, min_interval) where min_interval is from the tracker.

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