source: trunk/libtransmission/tracker.c @ 3119

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

slightly less broken handling of cases where we can't bind to our listening port.

  • 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 3119 2007-09-21 05:31:29Z 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}
201static void
202publishErrorMessage( Torrent * tor, const char * msg )
203{
204    publishMessage( tor, msg, TR_TRACKER_ERROR );
205}
206static void
207publishWarningMessage( Torrent * tor, const char * msg )
208{
209    publishMessage( tor, msg, TR_TRACKER_WARNING );
210}
211
212static void
213publishNewPeers( Torrent * tor, int count, uint8_t * peers )
214{
215    tr_tracker_event_t event = emptyEvent;
216    event.hash = tor->hash;
217    event.messageType = TR_TRACKER_PEERS;
218    event.peerCount = count;
219    event.peerCompact = peers;
220    tr_inf( "Torrent \"%s\" got %d new peers", tor->torrent->info.name, count );
221    tr_publisherPublish( tor->publisher, tor, &event );
222}
223
224static void
225publishStopped( Torrent * tor )
226{
227    tr_tracker_event_t event = emptyEvent;
228    event.hash = tor->hash;
229    event.messageType = TR_TRACKER_STOPPED;
230    tr_publisherPublish( tor->publisher, tor, &event );
231}
232
233/***
234****  LIFE CYCLE
235***/
236
237static tr_ptrArray *
238getTrackerLookupTable( void )
239{
240    static tr_ptrArray * myTrackers = NULL;
241    if( !myTrackers )
242         myTrackers = tr_ptrArrayNew( );
243    return myTrackers;
244}
245
246static void
247generateKeyParam( char * msg, int len )
248{
249    int i;
250    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
251    for( i=0; i<len; ++i )
252        *msg++ = pool[tr_rand(36)];
253    *msg = '\0';
254}
255
256static int onTrackerScrapeNow( void* );
257
258static void
259tr_trackerScrapeSoon( Tracker * t )
260{
261    /* don't start more than one scrape at once for the same tracker... */
262    if( !tr_ptrArrayEmpty( t->scraping ) )
263        return;
264
265    if( !t->scrapeTimer )
266         t->scrapeTimer = tr_timerNew( t->handle, onTrackerScrapeNow, t, 1000 );
267}
268
269static Tracker*
270tr_trackerGet( const tr_torrent * tor )
271{
272    const tr_info * info = &tor->info;
273    tr_ptrArray * trackers = getTrackerLookupTable( );
274    Tracker *t, tmp;
275    assert( info != NULL );
276    assert( info->primaryAddress && *info->primaryAddress );
277    tmp.primaryAddress = info->primaryAddress;
278    t = tr_ptrArrayFindSorted( trackers, &tmp, trackerCompare );
279
280    assert( t==NULL || !strcmp(t->primaryAddress,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->scrapeIntervalMsec      = DEFAULT_SCRAPE_INTERVAL_MSEC;
292        t->announceIntervalMsec    = DEFAULT_ANNOUNCE_INTERVAL_MSEC;
293        t->minAnnounceIntervalMsec = DEFAULT_ANNOUNCE_INTERVAL_MSEC;
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( trackers, 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->trackerID );
367    tr_free( tor->lastRequest );
368    if( tor->httpReq != NULL )
369        evhttp_request_free( tor->httpReq );
370    tr_free( tor );
371
372    if( tr_ptrArrayEmpty( t->torrents ) ) /* last one.. free the tracker too */
373    {
374        int i;
375        tr_ptrArrayRemoveSorted( getTrackerLookupTable( ), t, trackerCompare );
376
377        if( t->connection != NULL )
378            evhttp_connection_free( t->connection );
379
380        tr_ptrArrayFree( t->torrents );
381        tr_ptrArrayFree( t->scrapeQueue );
382        tr_ptrArrayFree( t->scraping );
383
384        for( i=0; i<t->addressCount; ++i )
385            tr_trackerInfoClear( &t->addresses[i] );
386
387        if( t->redirect ) {
388            tr_trackerInfoClear( t->redirect );
389            tr_free( t->redirect );
390        }
391
392        tr_timerFree( &t->scrapeTimer );
393
394        tr_free( t->primaryAddress );
395        tr_free( t->addresses );
396        tr_free( t->tierFronts );
397        tr_free( t );
398    }
399}
400
401void
402tr_trackerFree( Torrent * tor )
403{
404    tr_runInEventThread( tor->tracker->handle, onTorrentFreeNow, tor );
405}
406
407Torrent*
408tr_trackerNew( tr_torrent * torrent )
409{
410    Torrent * tor;
411    Tracker * t = tr_trackerGet( torrent );
412    assert( getExistingTorrent( t, torrent->info.hash ) == NULL );
413
414    /* create a new Torrent and queue it for scraping */
415    tor = tr_new0( Torrent, 1 );
416    tor->publisher = tr_publisherNew( );
417    tor->tracker = t;
418    tor->torrent = torrent;
419    tor->timesDownloaded = -1;
420    tor->seeders = -1;
421    tor->leechers = -1;
422    tor->manualAnnounceAllowedAt = ~0;
423    memcpy( tor->hash, torrent->info.hash, SHA_DIGEST_LENGTH );
424    escape( tor->escaped, torrent->info.hash, SHA_DIGEST_LENGTH );
425    tr_ptrArrayInsertSorted( t->torrents, tor, torrentCompare );
426    tr_ptrArrayInsertSorted( t->scrapeQueue, tor, torrentCompare );
427    tr_trackerScrapeSoon( t );
428    return tor;
429}
430
431/***
432****  UTIL
433***/
434
435static int
436parseBencResponse( struct evhttp_request * req, benc_val_t * setme )
437{
438    const unsigned char * body = EVBUFFER_DATA( req->input_buffer );
439    const int bodylen = EVBUFFER_LENGTH( req->input_buffer );
440    int ret = 1;
441    int i;
442
443    for( i=0; ret && i<bodylen; ++i )
444        if( !tr_bencLoad( body+i, bodylen-1, setme, NULL ) )
445            ret = 0;
446    return ret;
447}
448
449static char*
450updateAddresses( Tracker * t, const struct evhttp_request * req )
451{
452    char * ret = NULL;
453    int used = 0, max = 0;
454    int moveToNextAddress = FALSE;
455
456    if( !req )
457    {
458        tr_sprintf( &ret, &used, &max,
459                    "Connecting to %s got a NULL response",
460                    t->addresses[t->addressIndex].announce );
461        tr_inf( ret );
462
463        moveToNextAddress = TRUE;
464    }
465    else if( req->response_code == HTTP_OK )
466    {
467        if( t->redirect != NULL )
468        {
469            /* multitracker spec: "if a connection with a tracker is
470               successful, it will be moved to the front of the tier." */
471            const int i = t->addressIndex;
472            const int j = t->tierFronts[i];
473            const tr_tracker_info swap = t->addresses[i];
474            t->addresses[i] = t->addresses[j];
475            t->addresses[j] = swap;
476        }
477    }
478    else if(    ( req->response_code == HTTP_MOVEPERM )
479             || ( req->response_code == HTTP_MOVETEMP ) )
480    {
481        const char * loc = evhttp_find_header( req->input_headers, "Location" );
482        tr_tracker_info tmp;
483        if( tr_trackerInfoInit( &tmp, loc, -1 ) ) /* a bad redirect? */
484        {
485            moveToNextAddress = TRUE;
486        }
487        else if( req->response_code == HTTP_MOVEPERM )
488        {
489            tr_tracker_info * cur = &t->addresses[t->addressIndex];
490            tr_trackerInfoClear( cur );
491            *cur = tmp;
492        }
493        else if( req->response_code == HTTP_MOVETEMP )
494        {
495            if( t->redirect == NULL )
496                t->redirect = tr_new0( tr_tracker_info, 1 );
497            else
498                tr_trackerInfoClear( t->redirect );
499            *t->redirect = tmp;
500        }
501    }
502    else 
503    {
504        tr_sprintf( &ret, &used, &max,
505                    "Error connecting: %s",
506                    t->addresses[t->addressIndex].announce, 
507                    req->response_code_line );
508
509        moveToNextAddress = TRUE;
510    }
511
512    if( moveToNextAddress )
513        if ( ++t->addressIndex >= t->addressCount )
514            t->addressIndex = 0;
515
516    return ret;
517}
518
519static tr_tracker_info *
520getCurrentAddress( const Tracker * t )
521{
522    assert( t->addresses != NULL );
523    assert( t->addressIndex >= 0 );
524    assert( t->addressIndex < t->addressCount );
525
526    return &t->addresses[t->addressIndex];
527}
528static int
529trackerSupportsScrape( const Tracker * t )
530{
531    const tr_tracker_info * info = getCurrentAddress( t );
532
533    return ( info != NULL )
534        && ( info->scrape != NULL )
535        && ( info->scrape[0] != '\0' );
536}
537
538
539static void
540addCommonHeaders( const Tracker * t,
541                  struct evhttp_request * req )
542{
543    char buf[1024];
544    tr_tracker_info * address = getCurrentAddress( t );
545    snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
546    evhttp_add_header( req->output_headers, "Host", buf );
547    evhttp_add_header( req->output_headers, "Connection", "close" );
548    evhttp_add_header( req->output_headers, "Content-Length", "0" );
549    evhttp_add_header( req->output_headers, "User-Agent",
550                                         TR_NAME "/" LONG_VERSION_STRING );
551}
552
553/***
554****
555****  SCRAPE
556****
557***/
558
559static int
560onTorrentScrapeNow( void * vtor )
561{
562    Torrent * tor = (Torrent *) vtor;
563    if( trackerSupportsScrape( tor->tracker ) ) {
564        tr_ptrArrayInsertSorted( tor->tracker->scrapeQueue, tor, torrentCompare );
565        tr_trackerScrapeSoon( tor->tracker );
566    }
567    tor->scrapeTimer = NULL;
568    return FALSE;
569}
570
571static void
572onScrapeResponse( struct evhttp_request * req, void * vt )
573{
574    char * errmsg;
575    Tracker * t = (Tracker*) vt;
576
577    tr_inf( "Got scrape response from  '%s': %s",
578            t->primaryAddress,
579            (req ? req->response_code_line : "(null)") );
580
581    if( req && ( req->response_code == HTTP_OK ) )
582    {
583        int numResponses = 0;
584        benc_val_t benc, *files;
585        const int n_scraping = tr_ptrArraySize( t->scraping );
586        const int bencLoaded = !parseBencResponse( req, &benc );
587
588        if( bencLoaded
589            && (( files = tr_bencDictFind( &benc, "files" ) ))
590            && ( files->type == TYPE_DICT ) )
591        {
592            int i;
593            for( i=0; i<files->val.l.count; i+=2 )
594            {
595                const uint8_t* hash =
596                    (const uint8_t*) files->val.l.vals[i].val.s.s;
597                benc_val_t *tmp, *flags;
598                benc_val_t *tordict = &files->val.l.vals[i+1];
599                Torrent * tor = getExistingTorrent( t, hash );
600                ++numResponses;
601                   
602                if( !tor ) {
603                    tr_err( "Got an unrequested scrape response!" );
604                    continue;
605                }
606
607                if(( tmp = tr_bencDictFind( tordict, "complete" )))
608                    tor->seeders = tmp->val.i;
609
610                if(( tmp = tr_bencDictFind( tordict, "incomplete" )))
611                    tor->leechers = tmp->val.i;
612
613                if(( tmp = tr_bencDictFind( tordict, "downloaded" )))
614                    tor->timesDownloaded = tmp->val.i;
615
616                if(( flags = tr_bencDictFind( tordict, "flags" )))
617                    if(( tmp = tr_bencDictFind( flags, "min_request_interval")))
618                        t->scrapeIntervalMsec = tmp->val.i * 1000;
619
620                assert( tr_ptrArrayFindSorted(t->scraping,tor,torrentCompare) );
621                tr_ptrArrayRemoveSorted( t->scraping, tor, torrentCompare );
622
623                assert( !tor->scrapeTimer );
624                tor->scrapeTimer = tr_timerNew( t->handle, onTorrentScrapeNow, tor, t->scrapeIntervalMsec );
625                tr_dbg( "Torrent '%s' scrape successful."
626                        "  Rescraping in %d seconds",
627                        tor->torrent->info.name, t->scrapeIntervalMsec/1000 );
628            }
629
630            if( !files->val.l.count )
631            {
632                /* got an empty files dictionary!  This probably means the
633                   torrents we're scraping have expired from the tracker,
634                   so make sure they're stopped.  It also means any previous
635                   changes to multiscrapeMax are suspect, so reset that. */
636
637                int n;
638                Torrent ** torrents = (Torrent**)
639                    tr_ptrArrayPeek( t->scraping, &n );
640                for( i=0; i<n; ++i )
641                    tr_trackerStop( torrents[i] );
642                tr_ptrArrayClear( t->scraping );
643
644                t->multiscrapeMax = INT_MAX;
645            }
646        }
647
648        if( bencLoaded )
649            tr_bencFree( &benc );
650
651        /* if the tracker gave us back fewer torrents than we
652           thought we should get, maybe our multiscrape string
653           is too big... limit it based on how many we got back */
654        if( ( 0 < numResponses ) && ( numResponses < n_scraping ) )
655            t->multiscrapeMax = numResponses;
656    }
657
658    if (( errmsg = updateAddresses( t, req ) )) {
659        tr_err( errmsg );
660        tr_free( errmsg );
661    }
662
663    if( !tr_ptrArrayEmpty( t->scraping ) )
664    {
665        int i, n;
666        Torrent ** torrents =
667            (Torrent**) tr_ptrArrayPeek( t->scraping, &n );
668        for( i=0; i<n; ++i )
669            onTorrentScrapeNow( torrents[i] );
670        tr_ptrArrayClear( t->scraping );
671    }
672
673    if( !tr_ptrArrayEmpty( t->scrapeQueue ) )
674        tr_trackerScrapeSoon( t );
675}
676
677static int
678onTrackerScrapeNow( void * vt )
679{
680    Tracker * t = (Tracker*) vt;
681    const tr_tracker_info * address = getCurrentAddress( t );
682
683    assert( tr_ptrArrayEmpty( t->scraping ) );
684
685    if( trackerSupportsScrape( t ) && !tr_ptrArrayEmpty( t->scrapeQueue ) )
686    {
687        int i, n, len, addr_len, ask_n;
688        char *march, *uri;
689        Torrent ** torrents =
690            (Torrent**) tr_ptrArrayPeek( t->scrapeQueue, &n );
691        struct evhttp_connection *evcon = NULL;
692        struct evhttp_request *req = NULL;
693
694        ask_n = n;
695        if( ask_n > t->multiscrapeMax )
696            ask_n = t->multiscrapeMax;
697
698        /**
699        ***  Build the scrape request
700        **/
701
702        len = addr_len = strlen( address->scrape );
703        for( i=0; i<ask_n; ++i )
704            len += strlen("&info_hash=") + strlen(torrents[i]->escaped);
705        ++len; /* for nul */
706        uri = march = tr_new( char, len );
707        memcpy( march, address->scrape, addr_len ); march += addr_len;
708        for( i=0; i<ask_n; ++i ) {
709            const int elen = strlen( torrents[i]->escaped );
710            *march++ = i?'&':'?';
711            memcpy( march, "info_hash=", 10); march += 10;
712            memcpy( march, torrents[i]->escaped, elen ); march += elen;
713        }
714        *march++ = '\0';
715        assert( march - uri == len );
716
717        /* move the first n_ask torrents from scrapeQueue to scraping */
718        for( i=0; i<ask_n; ++i )
719            tr_ptrArrayInsertSorted( t->scraping, torrents[i], torrentCompare );
720        tr_ptrArrayErase( t->scrapeQueue, 0, ask_n );
721
722        /* ping the tracker */
723        tr_inf( "Sending scrape to tracker %s:%d: %s",
724                address->address, address->port, uri );
725        evcon = getConnection( t, address->address, address->port );
726        evhttp_connection_set_timeout( evcon, SCRAPE_TIMEOUT_INTERVAL_SEC );
727        req = evhttp_request_new( onScrapeResponse, t );
728        assert( req );
729        addCommonHeaders( t, req );
730        tr_evhttp_make_request( t->handle, evcon, req, EVHTTP_REQ_GET, uri );
731    }
732
733    t->scrapeTimer = NULL;
734    return FALSE;
735}
736
737/***
738****
739****  TRACKER REQUESTS
740****
741***/
742
743static int
744torrentIsRunning( const Torrent * tor )
745{
746    return ( tor != NULL )
747        && ( tor->lastRequest != NULL )
748        && ( strcmp( tor->lastRequest, "stopped" ) );
749}
750
751static char*
752buildTrackerRequestURI( const Torrent * tor, const char * eventName )
753{
754    const tr_torrent * torrent = tor->torrent;
755    const int stopping = !strcmp( eventName, "stopped" );
756    const int numwant = stopping ? 0 : NUMWANT;
757    char buf[4096];
758
759    snprintf( buf, sizeof(buf), "%s"
760                                "?info_hash=%s"
761                                "&peer_id=%s"
762                                "&port=%d"
763                                "&uploaded=%"PRIu64
764                                "&downloaded=%"PRIu64
765                                "&corrupt=%"PRIu64
766                                "&left=%"PRIu64
767                                "&compact=1"
768                                "&numwant=%d"
769                                "&key=%s"
770                                "%s%s"
771                                "%s%s",
772        getCurrentAddress(tor->tracker)->announce,
773        tor->escaped,
774        tor->peer_id,
775        tr_sharedGetPublicPort( torrent->handle->shared ),
776        torrent->uploadedCur,
777        torrent->downloadedCur,
778        torrent->corruptCur,
779        tr_cpLeftUntilComplete( torrent->completion ),
780        numwant,
781        tor->tracker->key_param,
782        ( ( eventName && *eventName ) ? "&event=" : "" ),
783        ( ( eventName && *eventName ) ? eventName : "" ),
784        ( ( tor->trackerID && *tor->trackerID ) ? "&trackerid=" : "" ),
785        ( ( tor->trackerID && *tor->trackerID ) ? tor->trackerID : "" ) );
786
787    return tr_strdup( buf );
788}
789
790/* Convert to compact form */
791static uint8_t *
792parseOldPeers( benc_val_t * bePeers, int * peerCount )
793{
794    int i, count;
795    uint8_t * compact;
796
797    assert( bePeers->type == TYPE_LIST );
798
799    compact = tr_new( uint8_t, 6 * bePeers->val.l.count );
800
801    for( i=count=0; i<bePeers->val.l.count; ++i )
802    {
803        struct in_addr addr;
804        tr_port_t port;
805        benc_val_t * val;
806        benc_val_t * peer = &bePeers->val.l.vals[i];
807
808        val = tr_bencDictFind( peer, "ip" );
809        if( !val || val->type!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) )
810            continue;
811
812        memcpy( &compact[6 * count], &addr, 4 );
813
814        val = tr_bencDictFind( peer, "port" );
815        if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff )
816            continue;
817
818        port = htons( val->val.i );
819        memcpy( &compact[6 * count + 4], &port, 2 );
820        ++count;
821    }
822
823    *peerCount = count;
824    return compact;
825}
826
827/* handle braindead trackers whose minimums is higher
828   than the interval. */
829static void
830setAnnounceInterval( Tracker  * t,
831                     int        minimum,
832                     int        interval )
833{
834    assert( t != NULL );
835
836    if( minimum > 0 )
837        t->minAnnounceIntervalMsec = minimum;
838
839    if( interval > 0 )
840        t->announceIntervalMsec = interval;
841
842    if( t->announceIntervalMsec < t->minAnnounceIntervalMsec )
843        t->announceIntervalMsec = t->minAnnounceIntervalMsec;
844}
845
846static int onReannounceNow( void * vtor );
847
848static void
849onTrackerResponse( struct evhttp_request * req, void * vtor )
850{
851    char * errmsg;
852    Torrent * tor = (Torrent *) vtor;
853    const int isStopped = !torrentIsRunning( tor );
854    int reannounceInterval;
855
856    tr_inf( "Torrent \"%s\" tracker response: %s",
857            tor->torrent->info.name,
858            ( req ? req->response_code_line : "(null)") );
859
860    if( req && ( req->response_code == HTTP_OK ) )
861    {
862        benc_val_t benc;
863        const int bencLoaded = !parseBencResponse( req, &benc );
864
865        if( bencLoaded && benc.type==TYPE_DICT )
866        {
867            benc_val_t * tmp;
868
869            if(( tmp = tr_bencDictFind( &benc, "failure reason" )))
870                publishErrorMessage( tor, tmp->val.s.s );
871
872            if(( tmp = tr_bencDictFind( &benc, "warning message" )))
873                publishWarningMessage( tor, tmp->val.s.s );
874
875            if(( tmp = tr_bencDictFind( &benc, "interval" )))
876                setAnnounceInterval( tor->tracker, -1, tmp->val.i * 1000 );
877
878            if(( tmp = tr_bencDictFind( &benc, "min interval" )))
879                setAnnounceInterval( tor->tracker, tmp->val.i * 1000, -1 );
880
881            if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
882                tor->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
883
884            if(( tmp = tr_bencDictFind( &benc, "complete" )))
885                tor->seeders = tmp->val.i;
886
887            if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
888                tor->leechers = tmp->val.i;
889
890            if(( tmp = tr_bencDictFind( &benc, "peers" )))
891            {
892                int peerCount = 0;
893                uint8_t * peerCompact = NULL;
894
895                if( tmp->type == TYPE_LIST ) /* original protocol */
896                {
897                    if( tmp->val.l.count > 0 )
898                        peerCompact = parseOldPeers( tmp, &peerCount );
899                }
900                else if( tmp->type == TYPE_STR ) /* "compact" extension */
901                {
902                    if( tmp->val.s.i >= 6 )
903                    {
904                        peerCount = tmp->val.s.i / 6;
905                        peerCompact = tr_new( uint8_t, tmp->val.s.i );
906                        memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
907                    }
908                }
909
910                publishNewPeers( tor, peerCount, peerCompact );
911                tr_free( peerCompact );
912            }
913        }
914
915        reannounceInterval = isStopped
916            ? -1
917            : tor->tracker->announceIntervalMsec;
918
919        if( bencLoaded )
920            tr_bencFree( &benc );
921    }
922    else
923    {
924        tr_inf( "Bad response from tracker '%s' on request '%s' "
925                "for torrent '%s'... trying again in 30 seconds",
926                tor->tracker->primaryAddress,
927                tor->lastRequest,
928                tor->torrent->info.name );
929
930        reannounceInterval = 30 * 1000;
931    }
932
933    if (( errmsg = updateAddresses( tor->tracker, req ) )) {
934        publishErrorMessage( tor, errmsg );
935        tr_err( errmsg );
936        tr_free( errmsg );
937    }
938
939    tor->httpReq = NULL;
940
941    if( isStopped )
942        publishStopped( tor );
943    else if( reannounceInterval > 0 ) {
944        tr_dbg( "torrent '%s' reannouncing in %d seconds",
945                tor->torrent->info.name, (reannounceInterval/1000) );
946        assert( tor->reannounceTimer == NULL );
947        tor->reannounceTimer = tr_timerNew( tor->tracker->handle, onReannounceNow, tor, reannounceInterval );
948        tor->manualAnnounceAllowedAt
949                           = tr_date() + MANUAL_ANNOUNCE_INTERVAL_MSEC;
950    }
951}
952
953static int
954sendTrackerRequest( void * vtor, const char * eventName )
955{
956    Torrent * tor = (Torrent *) vtor;
957    const tr_tracker_info * address = getCurrentAddress( tor->tracker );
958    char * uri = buildTrackerRequestURI( tor, eventName );
959    struct evhttp_connection * evcon = NULL;
960
961    tr_inf( "Torrent \"%s\" sending '%s' to tracker %s:%d: %s",
962            tor->torrent->info.name,
963            (eventName ? eventName : "periodic announce"),
964            address->address, address->port,
965            uri );
966
967    /* kill any pending requests */
968    tr_timerFree( &tor->reannounceTimer );
969
970    evcon = getConnection( tor->tracker, address->address, address->port );
971    if ( !evcon ) {
972        tr_err( "Can't make a connection to %s:%d", address->address, address->port );
973        tr_free( uri );
974    } else {
975        tr_free( tor->lastRequest );
976        tor->lastRequest = tr_strdup( eventName );
977        evhttp_connection_set_timeout( evcon, REQ_TIMEOUT_INTERVAL_SEC );
978        tor->httpReq = evhttp_request_new( onTrackerResponse, tor );
979        addCommonHeaders( tor->tracker, tor->httpReq );
980        tr_evhttp_make_request( tor->tracker->handle, evcon,
981                                tor->httpReq, EVHTTP_REQ_GET, uri );
982    }
983
984    return FALSE;
985}
986
987static int
988onReannounceNow( void * vtor )
989{
990    Torrent * tor = (Torrent *) vtor;
991    sendTrackerRequest( tor, "" );
992    tor->reannounceTimer = NULL;
993    return FALSE;
994}
995
996/***
997****  PUBLIC
998***/
999
1000tr_publisher_tag
1001tr_trackerSubscribe( Torrent             * tor,
1002                     tr_delivery_func      func,
1003                     void                * user_data )
1004{
1005    return tr_publisherSubscribe( tor->publisher, func, user_data );
1006}
1007
1008void
1009tr_trackerUnsubscribe( Torrent           * tor,
1010                       tr_publisher_tag    tag )
1011{
1012    tr_publisherUnsubscribe( tor->publisher, tag );
1013}
1014
1015const tr_tracker_info *
1016tr_trackerGetAddress( const Torrent * tor )
1017{
1018    return getCurrentAddress( tor->tracker );
1019}
1020
1021int
1022tr_trackerCanManualAnnounce ( const Torrent * tor )
1023{
1024    /* return true if this torrent's currently running
1025       and it's been long enough since the last announce */
1026    return ( torrentIsRunning( tor ) )
1027        && ( tr_date() >= tor->manualAnnounceAllowedAt );
1028}
1029
1030void
1031tr_trackerGetCounts( const Torrent       * tor,
1032                     int                 * setme_completedCount,
1033                     int                 * setme_leecherCount,
1034                     int                 * setme_seederCount )
1035{
1036    if( setme_completedCount )
1037       *setme_completedCount = tor->timesDownloaded;
1038
1039    if( setme_leecherCount )
1040       *setme_leecherCount = tor->leechers;
1041
1042    if( setme_seederCount )
1043       *setme_seederCount = tor->seeders;
1044}
1045
1046void
1047tr_trackerStart( Torrent * tor )
1048{
1049    tr_peerIdNew( tor->peer_id, sizeof(tor->peer_id) );
1050
1051    if( !tor->reannounceTimer && !tor->httpReq )
1052        sendTrackerRequest( tor, "started" );
1053}
1054
1055void
1056tr_trackerReannounce( Torrent * tor )
1057{
1058    sendTrackerRequest( tor, "started" );
1059}
1060
1061void
1062tr_trackerCompleted( Torrent * tor )
1063{
1064    sendTrackerRequest( tor, "completed" );
1065}
1066
1067void
1068tr_trackerStop( Torrent * tor )
1069{
1070    sendTrackerRequest( tor, "stopped" );
1071}
1072
1073void
1074tr_trackerChangeMyPort( Torrent * tor )
1075{
1076    if( torrentIsRunning( tor ) )
1077        tr_trackerReannounce( tor );
1078}
Note: See TracBrowser for help on using the repository browser.