source: trunk/libtransmission/tracker.c @ 3308

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

fix John_Clay's ptrarray.c:201 crash. (maybe.)

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