source: trunk/libtransmission/tracker.c @ 4024

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

take inspiration from Solomon when handling odd tracker errors that confuse tracker's request queue: stop the torrent.
when the tracker gives us errors that confuse the tracker work queue,

  • Property svn:keywords set to Date Rev Author Id
File size: 32.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 4024 2007-11-30 23:37:44Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isalnum */
15#include <stdio.h> /* snprintf */
16#include <stdlib.h>
17#include <string.h> /* strcmp, strchr */
18#include <libgen.h> /* basename */
19
20#include <sys/queue.h> /* evhttp.h needs this */
21#include <event.h>
22#include <evhttp.h>
23
24#include "transmission.h"
25#include "bencode.h"
26#include "completion.h"
27#include "list.h"
28#include "net.h"
29#include "publish.h"
30#include "shared.h"
31#include "tracker.h"
32#include "trevent.h"
33#include "utils.h"
34
35enum
36{
37    /* seconds between tracker pulses */
38    PULSE_INTERVAL_MSEC = 1000,
39
40    /* maximum number of concurrent tracker socket connections */
41    MAX_TRACKER_SOCKETS = 16,
42
43    /* unless the tracker says otherwise, rescrape this frequently */
44    DEFAULT_SCRAPE_INTERVAL_SEC = (60 * 15),
45
46    /* unless the tracker says otherwise, this is the announce interval */
47    DEFAULT_ANNOUNCE_INTERVAL_SEC = (60 * 4),
48
49    /* unless the tracker says otherwise, this is the announce min_interval */
50    DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = (60 * 2),
51
52    /* this is how long we'll leave a request hanging before timeout */
53    TIMEOUT_INTERVAL_SEC = 45,
54
55    /* this is how long we'll leave a 'stop' request hanging before timeout.
56       we wait less time for this so it doesn't slow down shutdowns */
57    STOP_TIMEOUT_INTERVAL_SEC = 5,
58
59    /* the value of the 'numwant' argument passed in tracker requests. */
60    NUMWANT = 200,
61
62    /* the length of the 'key' argument passed in tracker requests */
63    KEYLEN = 10
64};
65
66/**
67***
68**/
69
70struct tr_tracker
71{
72    tr_handle * handle;
73
74    /* these are set from the latest scrape or tracker response */
75    int announceIntervalSec;
76    int announceMinIntervalSec;
77    int scrapeIntervalSec;
78
79    tr_tracker_info * redirect;
80    tr_tracker_info * addresses;
81    int addressIndex;
82    int addressCount;
83    int * tierFronts;
84
85    /* sent as the "key" argument in tracker requests
86       to verify us if our IP address changes.
87       This is immutable for the life of the tracker object. */
88    char key_param[KEYLEN+1];
89
90    tr_publisher_t * publisher;
91
92    /* torrent hash string */
93    uint8_t hash[SHA_DIGEST_LENGTH];
94    char escaped[SHA_DIGEST_LENGTH * 3 + 1];
95    char * name;
96
97    /* corresponds to the peer_id sent as a tracker request parameter.
98       one tracker admin says: "When the same torrent is opened and
99       closed and opened again without quitting Transmission ...
100       change the peerid. It would help sometimes if a stopped event
101       was missed to ensure that we didn't think someone was cheating. */
102    char peer_id[TR_ID_LEN + 1];
103
104    /* these are set from the latest tracker response... -1 is 'unknown' */
105    int timesDownloaded;
106    int seederCount;
107    int leecherCount;
108    char * trackerID;
109
110    time_t manualAnnounceAllowedAt;
111    time_t reannounceAt;
112    time_t scrapeAt;
113
114    unsigned int isRunning     : 1;
115};
116
117/**
118***
119**/
120
121static void
122myDebug( const char * file, int line, const tr_tracker * t, const char * fmt, ... )
123{   
124    FILE * fp = tr_getLog( );
125    if( fp != NULL )
126    {
127        va_list args;
128        char timestr[64];
129        struct evbuffer * buf = evbuffer_new( );
130        char * myfile = tr_strdup( file );
131
132        evbuffer_add_printf( buf, "[%s] ", tr_getLogTimeStr( timestr, sizeof(timestr) ) );
133        if( t != NULL )
134            evbuffer_add_printf( buf, "%s ", t->name );
135        va_start( args, fmt );
136        evbuffer_add_vprintf( buf, fmt, args );
137        va_end( args );
138        evbuffer_add_printf( buf, " (%s:%d)\n", basename(myfile), line );
139        fwrite( EVBUFFER_DATA(buf), 1, EVBUFFER_LENGTH(buf), fp );
140
141        tr_free( myfile );
142        evbuffer_free( buf );
143    }
144}
145
146#define dbgmsg(t, fmt...) myDebug(__FILE__, __LINE__, t, ##fmt )
147
148/***
149****
150***/
151
152static tr_tracker_info *
153getCurrentAddress( const tr_tracker * t )
154{
155    assert( t->addresses != NULL );
156    assert( t->addressIndex >= 0 );
157    assert( t->addressIndex < t->addressCount );
158
159    return t->redirect ? t->redirect
160                       : t->addresses + t->addressIndex;
161}
162
163static int
164trackerSupportsScrape( const tr_tracker * t )
165{
166    const tr_tracker_info * info = getCurrentAddress( t );
167
168    return ( info != NULL )
169        && ( info->scrape != NULL )
170        && ( info->scrape[0] != '\0' );
171}
172
173/***
174****
175***/
176
177struct torrent_hash
178{
179    tr_handle * handle;
180    uint8_t hash[SHA_DIGEST_LENGTH];
181};
182
183static struct torrent_hash*
184torrentHashNew( tr_handle * handle, const tr_tracker * t )
185{
186    struct torrent_hash * data = tr_new( struct torrent_hash, 1 );
187    data->handle = handle;
188    memcpy( data->hash, t->hash, SHA_DIGEST_LENGTH );
189    return data;
190}
191
192tr_tracker *
193findTrackerFromHash( struct torrent_hash * data )
194{
195    tr_torrent * torrent = tr_torrentFindFromHash( data->handle, data->hash );
196    return torrent ? torrent->tracker : NULL;
197}
198
199tr_tracker *
200findTracker( tr_handle * handle, const uint8_t * hash )
201{
202    tr_torrent * torrent = tr_torrentFindFromHash( handle, hash );
203    return torrent ? torrent->tracker : NULL;
204}
205
206/***
207****  PUBLISH
208***/
209
210static const tr_tracker_event emptyEvent = { 0, NULL, NULL, NULL, 0 };
211
212static void
213publishMessage( tr_tracker * t, const char * msg, int type )
214{
215    if( t != NULL )
216    {
217        tr_tracker_event event = emptyEvent;
218        event.hash = t->hash;
219        event.messageType = type;
220        event.text = msg;
221        tr_publisherPublish( t->publisher, t, &event );
222    }
223}
224
225static void
226publishErrorClear( tr_tracker * t )
227{
228    publishMessage( t, NULL, TR_TRACKER_ERROR_CLEAR );
229}
230
231static void
232publishErrorMessageAndStop( tr_tracker * t, const char * msg )
233{
234    t->isRunning = 0;
235    publishMessage( t, msg, TR_TRACKER_ERROR );
236}
237
238static void
239publishWarning( tr_tracker * t, const char * msg )
240{
241    publishMessage( t, msg, TR_TRACKER_WARNING );
242}
243
244static void
245publishNewPeers( tr_tracker * t, int count, uint8_t * peers )
246{
247    tr_tracker_event event = emptyEvent;
248    event.hash = t->hash;
249    event.messageType = TR_TRACKER_PEERS;
250    event.peerCount = count;
251    event.peerCompact = peers;
252    tr_inf( "Torrent \"%s\" got %d new peers", t->name, count );
253    tr_publisherPublish( t->publisher, t, &event );
254}
255
256/***
257****
258***/
259
260static void
261onStoppedResponse( struct evhttp_request * req UNUSED, void * handle UNUSED )
262{
263    dbgmsg( NULL, "got a response to some `stop' message" );
264}
265
266static int
267parseBencResponse( struct evhttp_request * req, benc_val_t * setme )
268{
269    const unsigned char * body = EVBUFFER_DATA( req->input_buffer );
270    const int bodylen = EVBUFFER_LENGTH( req->input_buffer );
271    int ret = 1;
272    int i;
273
274    for( i=0; ret && i<bodylen; ++i )
275        if( !tr_bencLoad( body+i, bodylen-1, setme, NULL ) )
276            ret = 0;
277
278    return ret;
279}
280
281static const char*
282updateAddresses( tr_tracker * t, const struct evhttp_request * req )
283{
284    const char * ret = NULL;
285    int moveToNextAddress = FALSE;
286
287    if( !req )
288    {
289        ret = "Tracker hasn't responded yet.  Retrying...";
290        tr_inf( ret );
291
292        moveToNextAddress = TRUE;
293    }
294    else if( req->response_code == HTTP_OK )
295    {
296        if( t->redirect != NULL )
297        {
298            /* multitracker spec: "if a connection with a tracker is
299               successful, it will be moved to the front of the tier." */
300            const int i = t->addressIndex;
301            const int j = t->tierFronts[i];
302            const tr_tracker_info swap = t->addresses[i];
303            t->addresses[i] = t->addresses[j];
304            t->addresses[j] = swap;
305        }
306    }
307    else if(    ( req->response_code == HTTP_MOVEPERM )
308             || ( req->response_code == HTTP_MOVETEMP ) )
309    {
310        const char * loc = evhttp_find_header( req->input_headers, "Location" );
311        tr_tracker_info tmp;
312        if( tr_trackerInfoInit( &tmp, loc, -1 ) ) /* a bad redirect? */
313        {
314            moveToNextAddress = TRUE;
315        }
316        else if( req->response_code == HTTP_MOVEPERM )
317        {
318            tr_tracker_info * cur = &t->addresses[t->addressIndex];
319            tr_trackerInfoClear( cur );
320            *cur = tmp;
321        }
322        else if( req->response_code == HTTP_MOVETEMP )
323        {
324            if( t->redirect == NULL )
325                t->redirect = tr_new0( tr_tracker_info, 1 );
326            else
327                tr_trackerInfoClear( t->redirect );
328            *t->redirect = tmp;
329        }
330    }
331    else 
332    {
333        moveToNextAddress = TRUE;
334    }
335
336    if( moveToNextAddress )
337        if ( ++t->addressIndex >= t->addressCount )
338            t->addressIndex = 0;
339
340    return ret;
341}
342
343/* Convert to compact form */
344static uint8_t *
345parseOldPeers( benc_val_t * bePeers, int * setmePeerCount )
346{
347    int i;
348    uint8_t *compact, *walk;
349    const int peerCount = bePeers->val.l.count;
350
351    assert( bePeers->type == TYPE_LIST );
352
353    compact = tr_new( uint8_t, peerCount*6 );
354
355    for( i=0, walk=compact; i<peerCount; ++i )
356    {
357        struct in_addr addr;
358        tr_port_t port;
359        benc_val_t * val;
360        benc_val_t * peer = &bePeers->val.l.vals[i];
361
362        val = tr_bencDictFind( peer, "ip" );
363        if( !val || val->type!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) )
364            continue;
365
366        memcpy( walk, &addr, 4 );
367        walk += 4;
368
369        val = tr_bencDictFind( peer, "port" );
370        if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff )
371            continue;
372
373        port = htons( val->val.i );
374        memcpy( walk, &port, 2 );
375        walk += 2;
376    }
377
378    *setmePeerCount = peerCount;
379    return compact;
380}
381
382static void
383onTrackerResponse( struct evhttp_request * req, void * torrent_hash )
384{
385    const char * warning;
386    tr_tracker * t;
387    int responseCode;
388
389    t = findTrackerFromHash( torrent_hash );
390    tr_free( torrent_hash );
391    if( t == NULL ) /* tracker has been closed */
392        return;
393
394    dbgmsg( t, "got response from tracker: \"%s\"",
395            ( req && req->response_code_line ) ?  req->response_code_line
396                                               : "(null)" );
397
398    tr_inf( "Torrent \"%s\" tracker response: %s",
399            t->name,
400            ( req ? req->response_code_line : "(null)") );
401
402    if( req && ( req->response_code == HTTP_OK ) )
403    {
404        benc_val_t benc;
405        const int bencLoaded = !parseBencResponse( req, &benc );
406
407        publishErrorClear( t );
408
409        if( bencLoaded && benc.type==TYPE_DICT )
410        {
411            benc_val_t * tmp;
412
413            if(( tmp = tr_bencDictFind( &benc, "failure reason" ))) {
414                dbgmsg( t, "got failure message [%s]", tmp->val.s.s );
415                publishErrorMessageAndStop( t, tmp->val.s.s );
416            }
417
418            if(( tmp = tr_bencDictFind( &benc, "warning message" ))) {
419                dbgmsg( t, "got warning message [%s]", tmp->val.s.s );
420                publishWarning( t, tmp->val.s.s );
421            }
422
423            if(( tmp = tr_bencDictFind( &benc, "interval" ))) {
424                dbgmsg( t, "setting interval to %d", tmp->val.i );
425                t->announceIntervalSec = tmp->val.i;
426            }
427
428            if(( tmp = tr_bencDictFind( &benc, "min interval" ))) {
429                dbgmsg( t, "setting min interval to %d", tmp->val.i );
430                t->announceMinIntervalSec = tmp->val.i;
431            }
432
433            if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
434                t->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
435
436            if(( tmp = tr_bencDictFind( &benc, "complete" )))
437                t->seederCount = tmp->val.i;
438
439            if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
440                t->leecherCount = tmp->val.i;
441
442            if(( tmp = tr_bencDictFind( &benc, "peers" )))
443            {
444                int peerCount = 0;
445                uint8_t * peerCompact = NULL;
446
447                if( tmp->type == TYPE_LIST ) /* original protocol */
448                {
449                    if( tmp->val.l.count > 0 )
450                        peerCompact = parseOldPeers( tmp, &peerCount );
451                }
452                else if( tmp->type == TYPE_STR ) /* "compact" extension */
453                {
454                    if( tmp->val.s.i >= 6 )
455                    {
456                        peerCount = tmp->val.s.i / 6;
457                        peerCompact = tr_new( uint8_t, tmp->val.s.i );
458                        memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
459                    }
460                }
461
462                publishNewPeers( t, peerCount, peerCompact );
463                tr_free( peerCompact );
464            }
465        }
466
467        if( bencLoaded )
468            tr_bencFree( &benc );
469    }
470
471    if (( warning = updateAddresses( t, req ) )) {
472        publishWarning( t, warning );
473        tr_err( warning );
474    }
475
476    /**
477    ***
478    **/
479
480    responseCode = req ? req->response_code : 503;
481
482    if( 200<=responseCode && responseCode<=299 )
483    {
484        dbgmsg( t, "request succeeded. reannouncing in %d seconds",
485                   t->announceIntervalSec );
486        t->reannounceAt = time(NULL) + t->announceIntervalSec;
487        t->manualAnnounceAllowedAt = time(NULL) + t->announceMinIntervalSec;
488    }
489    else if( 300<=responseCode && responseCode<=399 )
490    {
491        dbgmsg( t, "got a redirect; retrying immediately" );
492
493        /* it's a redirect... updateAddresses() has already
494         * parsed the redirect, all that's left is to retry */
495        t->reannounceAt = time(NULL);
496        t->manualAnnounceAllowedAt = time(NULL) + t->announceMinIntervalSec;
497    }
498    else if( 400<=responseCode && responseCode<=499 )
499    {
500        const char * err = req && req->response_code_line
501            ? req->response_code_line
502            : "Unspecified 4xx error from tracker.";
503        dbgmsg( t, err );
504
505        /* The request could not be understood by the server due to
506         * malformed syntax. The client SHOULD NOT repeat the
507         * request without modifications. */
508        publishErrorMessageAndStop( t, err );
509        t->manualAnnounceAllowedAt = ~(time_t)0;
510        t->reannounceAt = 0;
511    }
512    else if( 500<=responseCode && responseCode<=599 )
513    {
514        dbgmsg( t, "Got a 5xx error... retrying in 15 seconds." );
515
516        /* Response status codes beginning with the digit "5" indicate
517         * cases in which the server is aware that it has erred or is
518         * incapable of performing the request.  So we pause a bit and
519         * try again. */
520        if( req && req->response_code_line )
521            publishWarning( t, req->response_code_line );
522        t->manualAnnounceAllowedAt = ~(time_t)0;
523        t->reannounceAt = time(NULL) + 15;
524    }
525    else
526    {
527        dbgmsg( t, "Invalid response from tracker... retrying in 60 seconds." );
528
529        /* WTF did we get?? */
530        if( req && req->response_code_line )
531            publishWarning( t, req->response_code_line );
532        t->manualAnnounceAllowedAt = ~(time_t)0;
533        t->reannounceAt = time(NULL) + 60;
534    }
535}
536
537static void
538onScrapeResponse( struct evhttp_request * req, void * vhash )
539{
540    const char * warning;
541    time_t nextScrapeSec = 60;
542    tr_tracker * t = findTrackerFromHash( vhash );
543
544    dbgmsg( t, "Got scrape response for '%s': %s (%d)", (t ? t->name : "(null)"), (req ? req->response_code_line : "(no line)"), (req ? req->response_code : -1) );
545
546    tr_free( vhash );
547    if( t == NULL ) /* tracker's been closed... */
548        return;
549
550    tr_inf( "Got scrape response for  '%s': %s",
551            t->name,
552            ( ( req && req->response_code_line ) ? req->response_code_line
553                                                 : "(null)") );
554
555    if( req && ( req->response_code == HTTP_OK ) )
556    {
557        benc_val_t benc, *files;
558        const int bencLoaded = !parseBencResponse( req, &benc );
559
560        if( bencLoaded
561            && (( files = tr_bencDictFind( &benc, "files" ) ))
562            && ( files->type == TYPE_DICT ) )
563        {
564            int i;
565            for( i=0; i<files->val.l.count; i+=2 )
566            {
567                const uint8_t* hash =
568                        (const uint8_t*) files->val.l.vals[i].val.s.s;
569                benc_val_t *tmp, *flags;
570                benc_val_t *tordict = &files->val.l.vals[i+1];
571                if( memcmp( t->hash, hash, SHA_DIGEST_LENGTH ) )
572                    continue;
573
574                publishErrorClear( t );
575
576                if(( tmp = tr_bencDictFind( tordict, "complete" )))
577                    t->seederCount = tmp->val.i;
578
579                if(( tmp = tr_bencDictFind( tordict, "incomplete" )))
580                    t->leecherCount = tmp->val.i;
581
582                if(( tmp = tr_bencDictFind( tordict, "downloaded" )))
583                    t->timesDownloaded = tmp->val.i;
584
585                if(( flags = tr_bencDictFind( tordict, "flags" )))
586                    if(( tmp = tr_bencDictFind( flags, "min_request_interval")))
587                        t->scrapeIntervalSec = tmp->val.i;
588
589                tr_dbg( "Torrent '%s' scrape successful."
590                        "  Rescraping in %d seconds",
591                        t->name, t->scrapeIntervalSec );
592
593                nextScrapeSec = t->scrapeIntervalSec;
594            }
595        }
596
597        if( bencLoaded )
598            tr_bencFree( &benc );
599    }
600
601    if (( warning = updateAddresses( t, req ) )) {
602        tr_err( warning );
603        publishWarning( t, warning );
604    }
605
606    t->scrapeAt = time(NULL) + nextScrapeSec;
607}
608
609/***
610****
611***/
612
613enum
614{
615    TR_REQ_STARTED,
616    TR_REQ_COMPLETED,
617    TR_REQ_STOPPED,
618    TR_REQ_REANNOUNCE,
619    TR_REQ_SCRAPE,
620    TR_REQ_COUNT
621};
622
623struct tr_tracker_request
624{
625    int port;
626    int timeout;
627    int reqtype; /* TR_REQ_* */
628    char * address;
629    char * uri;
630    struct evhttp_request * req;
631    uint8_t torrent_hash[SHA_DIGEST_LENGTH];
632};
633
634static void
635freeRequest( struct tr_tracker_request * req )
636{
637    tr_free( req->address );
638    tr_free( req->uri );
639    tr_free( req );
640}
641
642static void
643addCommonHeaders( const tr_tracker * t,
644                  struct evhttp_request * req )
645{
646    char buf[1024];
647    const tr_tracker_info * address = getCurrentAddress( t );
648    snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
649    evhttp_add_header( req->output_headers, "Host", buf );
650    evhttp_add_header( req->output_headers, "Connection", "close" );
651    evhttp_add_header( req->output_headers, "Content-Length", "0" );
652    evhttp_add_header( req->output_headers, "User-Agent",
653                                         TR_NAME "/" LONG_VERSION_STRING );
654}
655
656static char*
657buildTrackerRequestURI( const tr_tracker  * t,
658                        const tr_torrent  * torrent,
659                        const char        * eventName )
660{
661    const int isStopping = !strcmp( eventName, "stopped" );
662    const int numwant = isStopping ? 0 : NUMWANT;
663    struct evbuffer * buf = evbuffer_new( );
664    char * ret;
665
666    char * ann = getCurrentAddress(t)->announce;
667   
668    evbuffer_add_printf( buf, "%s"
669                              "%cinfo_hash=%s"
670                              "&peer_id=%s"
671                              "&port=%d"
672                              "&uploaded=%"PRIu64
673                              "&downloaded=%"PRIu64
674                              "&corrupt=%"PRIu64
675                              "&left=%"PRIu64
676                              "&compact=1"
677                              "&numwant=%d"
678                              "&key=%s"
679                              "&supportcrypto=1"
680                              "&requirecrypto=%d"
681                              "%s%s"
682                              "%s%s",
683        ann,
684        strchr(ann, '?') ? '&' : '?',
685        t->escaped,
686        t->peer_id,
687        tr_sharedGetPublicPort( t->handle->shared ),
688        torrent->uploadedCur,
689        torrent->downloadedCur,
690        torrent->corruptCur,
691        tr_cpLeftUntilComplete( torrent->completion ),
692        numwant,
693        t->key_param,
694        ( t->handle->encryptionMode==TR_ENCRYPTION_REQUIRED ? 1 : 0 ),
695        ( ( eventName && *eventName ) ? "&event=" : "" ),
696        ( ( eventName && *eventName ) ? eventName : "" ),
697        ( ( t->trackerID && *t->trackerID ) ? "&trackerid=" : "" ),
698        ( ( t->trackerID && *t->trackerID ) ? t->trackerID : "" ) );
699
700    ret = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
701    evbuffer_free( buf );
702    return ret;
703}
704
705static struct tr_tracker_request*
706createRequest( tr_handle * handle, const tr_tracker * tracker, int reqtype )
707{
708    static const char* strings[TR_REQ_COUNT] = { "started", "completed", "stopped", "", "err" };
709    const tr_torrent * torrent = tr_torrentFindFromHash( handle, tracker->hash );
710    const tr_tracker_info * address = getCurrentAddress( tracker );
711    const int isStopping = reqtype == TR_REQ_STOPPED;
712    const char * eventName = strings[reqtype];
713    struct tr_tracker_request * req;
714
715    req = tr_new0( struct tr_tracker_request, 1 );
716    req->address = tr_strdup( address->address );
717    req->port = address->port;
718    req->uri = buildTrackerRequestURI( tracker, torrent, eventName );
719    req->timeout = isStopping ? STOP_TIMEOUT_INTERVAL_SEC : TIMEOUT_INTERVAL_SEC;
720    req->reqtype = reqtype;
721    req->req = isStopping
722        ? evhttp_request_new( onStoppedResponse, handle )
723        : evhttp_request_new( onTrackerResponse, torrentHashNew(handle, tracker) );
724    memcpy( req->torrent_hash, tracker->hash, SHA_DIGEST_LENGTH );
725    addCommonHeaders( tracker, req->req );
726
727    return req;
728}
729
730static struct tr_tracker_request*
731createScrape( tr_handle * handle, const tr_tracker * tracker )
732{
733    const tr_tracker_info * a = getCurrentAddress( tracker );
734    struct tr_tracker_request * req;
735
736    req = tr_new0( struct tr_tracker_request, 1 );
737    req->address = tr_strdup( a->address );
738    req->port = a->port;
739    req->timeout = TIMEOUT_INTERVAL_SEC;
740    req->req = evhttp_request_new( onScrapeResponse, torrentHashNew( handle, tracker ) );
741    req->reqtype = TR_REQ_SCRAPE;
742    tr_asprintf( &req->uri, "%s%cinfo_hash=%s", a->scrape, strchr(a->scrape,'?')?'&':'?', tracker->escaped );
743    memcpy( req->torrent_hash, tracker->hash, SHA_DIGEST_LENGTH );
744    addCommonHeaders( tracker, req->req );
745
746    return req;
747}
748
749struct tr_tracker_handle
750{
751    int socketCount;
752    tr_timer * pulseTimer;
753    tr_list * requestQueue;
754    tr_list * scrapeQueue;
755};
756
757static int pulse( void * vhandle );
758
759static void
760ensureGlobalsExist( tr_handle * handle )
761{
762    if( handle->tracker == NULL )
763    {
764        handle->tracker = tr_new0( struct tr_tracker_handle, 1 );
765        handle->tracker->pulseTimer = tr_timerNew( handle, pulse, handle, PULSE_INTERVAL_MSEC );
766        dbgmsg( NULL, "creating tracker timer" );
767    }
768}
769
770static void
771freeRequest2( void * req )
772{
773    freeRequest( req );
774}
775
776void
777tr_trackerShuttingDown( tr_handle * handle )
778{
779    /* since we're shutting down, we don't need to scrape anymore... */
780    if( handle->tracker )
781        tr_list_free( &handle->tracker->scrapeQueue, freeRequest2 );
782}
783
784static int
785maybeFreeGlobals( tr_handle * handle )
786{
787    int globalsExist = handle->tracker != NULL;
788
789    if( globalsExist
790        && ( handle->tracker->socketCount < 1 )
791        && ( handle->tracker->requestQueue == NULL )
792        && ( handle->tracker->scrapeQueue == NULL )
793        && ( handle->torrentList== NULL ) )
794    {
795        dbgmsg( NULL, "freeing tracker timer" );
796        tr_timerFree( &handle->tracker->pulseTimer );
797        tr_free( handle->tracker );
798        handle->tracker = NULL;
799        globalsExist = FALSE;
800    }
801
802    return globalsExist;
803}
804
805/***
806****
807***/
808
809static int
810freeConnection( void * evcon )
811{
812    evhttp_connection_free( evcon );
813    return FALSE;
814}
815static void
816connectionClosedCB( struct evhttp_connection * evcon, void * vhandle )
817{
818    tr_handle * handle = vhandle;
819
820    assert( handle );
821    assert( handle->tracker );
822
823    /* libevent references evcon right after calling this function,
824       so we can't free it yet... defer it to after this call chain
825       has played out */
826    tr_timerNew( handle, freeConnection, evcon, 100 );
827
828    --handle->tracker->socketCount;
829    dbgmsg( NULL, "decrementing socket count to %d", handle->tracker->socketCount );
830    pulse( handle );
831}
832
833static struct evhttp_connection*
834getConnection( tr_handle * handle, const char * address, int port )
835{
836    struct evhttp_connection * c = evhttp_connection_new( address, port );
837    evhttp_connection_set_closecb( c, connectionClosedCB, handle );
838    return c;
839}
840
841static void
842invokeRequest( tr_handle * handle, const struct tr_tracker_request * req )
843{
844    struct evhttp_connection * evcon = getConnection( handle, req->address, req->port );
845    tr_tracker * t = findTracker( handle, req->torrent_hash );
846    dbgmsg( t, "sending '%s' to tracker %s:%d, timeout is %d", req->uri, req->address, req->port, (int)req->timeout );
847    evhttp_connection_set_timeout( evcon, req->timeout );
848    if( evhttp_make_request( evcon, req->req, EVHTTP_REQ_GET, req->uri ))
849        publishErrorMessageAndStop( t, "Tracker could not be reached." );
850    else {
851        ++handle->tracker->socketCount;
852        dbgmsg( t, "incremented socket count to %d", handle->tracker->socketCount );
853    }
854}
855
856static void
857invokeNextInQueue( tr_handle * handle, tr_list ** list )
858{
859    struct tr_tracker_request * req = tr_list_pop_front( list );
860    invokeRequest( handle, req );
861    freeRequest( req );
862}
863
864static int
865socketIsAvailable( tr_handle * handle )
866{
867    return handle->tracker->socketCount < MAX_TRACKER_SOCKETS;
868}
869
870static void ensureGlobalsExist( tr_handle * );
871
872static void
873enqueueScrape( tr_handle * handle, const tr_tracker * tracker )
874{
875    struct tr_tracker_request * req;
876    ensureGlobalsExist( handle );
877    req = createScrape( handle, tracker );
878    tr_list_append( &handle->tracker->scrapeQueue, req );
879}
880
881static void
882enqueueRequest( tr_handle * handle, const tr_tracker * tracker, int reqtype )
883{
884    struct tr_tracker_request * req;
885    ensureGlobalsExist( handle );
886    req = createRequest( handle, tracker, reqtype );
887    tr_list_append( &handle->tracker->requestQueue, req );
888}
889
890static int
891pulse( void * vhandle )
892{
893    tr_handle * handle = vhandle;
894    struct tr_tracker_handle * th = handle->tracker;
895    tr_torrent * tor;
896    const time_t now = time( NULL );
897
898    if( handle->tracker->socketCount || tr_list_size(th->requestQueue) || tr_list_size(th->scrapeQueue) )
899        dbgmsg( NULL, "tracker pulse... %d sockets, %d reqs left, %d scrapes left", handle->tracker->socketCount, tr_list_size(th->requestQueue), tr_list_size(th->scrapeQueue) );
900
901    /* upkeep: queue periodic rescrape / reannounce */
902    for( tor=handle->torrentList; tor; tor=tor->next )
903    {
904        tr_tracker * t = tor->tracker;
905
906        if( t->scrapeAt && trackerSupportsScrape( t ) && ( now >= t->scrapeAt ) ) {
907            t->scrapeAt = 0;
908            enqueueScrape( handle, t );
909        }
910
911        if( t->reannounceAt && t->isRunning && ( now >= t->reannounceAt ) ) {
912            t->reannounceAt = 0;
913            enqueueRequest( handle, t, TR_REQ_REANNOUNCE );
914        }
915    }
916
917    if( handle->tracker->socketCount || tr_list_size(th->requestQueue) || tr_list_size(th->scrapeQueue) )
918        dbgmsg( NULL, "tracker pulse after upkeep... %d sockets, %d reqs left, %d scrapes left", handle->tracker->socketCount, tr_list_size(th->requestQueue), tr_list_size(th->scrapeQueue) );
919
920    /* look for things to do... process all the requests, then process all the scrapes */
921    while( th->requestQueue && socketIsAvailable( handle ) )
922        invokeNextInQueue( handle, &th->requestQueue );
923    while( th->scrapeQueue && socketIsAvailable( handle ) )
924        invokeNextInQueue( handle, &th->scrapeQueue );
925
926    if( handle->tracker->socketCount || tr_list_size(th->requestQueue) || tr_list_size(th->scrapeQueue) )
927        dbgmsg( NULL, "tracker pulse done... %d sockets, %d reqs left, %d scrapes left", handle->tracker->socketCount, tr_list_size(th->requestQueue), tr_list_size(th->scrapeQueue) );
928
929    return maybeFreeGlobals( handle );
930}
931
932/***
933****  LIFE CYCLE
934***/
935
936static void
937generateKeyParam( char * msg, int len )
938{
939    int i;
940    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
941    const int poolSize = strlen( pool );
942    for( i=0; i<len; ++i )
943        *msg++ = pool[tr_rand(poolSize)];
944    *msg = '\0';
945}
946
947static void
948escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
949{
950    const uint8_t *end = in + in_len;
951    while( in != end )
952        if( isalnum(*in) )
953            *out++ = (char) *in++;
954        else 
955            out += snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
956    *out = '\0';
957}
958
959tr_tracker *
960tr_trackerNew( const tr_torrent * torrent )
961{
962    const tr_info * info = &torrent->info;
963    int i, j, sum, *iwalk;
964    tr_tracker_info * nwalk;
965    tr_tracker * t;
966
967    tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress );
968
969    t = tr_new0( tr_tracker, 1 );
970    t->handle = torrent->handle;
971    t->scrapeIntervalSec       = DEFAULT_SCRAPE_INTERVAL_SEC;
972    t->announceIntervalSec     = DEFAULT_ANNOUNCE_INTERVAL_SEC;
973    t->announceMinIntervalSec  = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
974    generateKeyParam( t->key_param, KEYLEN );
975
976    t->publisher = tr_publisherNew( );
977    t->timesDownloaded = -1;
978    t->seederCount = -1;
979    t->leecherCount = -1;
980    t->manualAnnounceAllowedAt = ~(time_t)0;
981    t->name = tr_strdup( info->name );
982    memcpy( t->hash, info->hash, SHA_DIGEST_LENGTH );
983    escape( t->escaped, info->hash, SHA_DIGEST_LENGTH );
984
985    for( sum=i=0; i<info->trackerTiers; ++i )
986         sum += info->trackerList[i].count;
987    t->addresses = nwalk = tr_new0( tr_tracker_info, sum );
988    t->addressIndex = 0;
989    t->addressCount = sum;
990    t->tierFronts = iwalk = tr_new0( int, sum );
991
992    for( i=0; i<info->trackerTiers; ++i )
993    {
994        const int tierFront = nwalk - t->addresses;
995
996        for( j=0; j<info->trackerList[i].count; ++j )
997        {
998            const tr_tracker_info * src = &info->trackerList[i].list[j];
999            nwalk->address = tr_strdup( src->address );
1000            nwalk->port = src->port;
1001            nwalk->announce = tr_strdup( src->announce );
1002            nwalk->scrape = tr_strdup( src->scrape );
1003            ++nwalk;
1004
1005            *iwalk++ = tierFront;
1006        }
1007    }
1008
1009    assert( nwalk - t->addresses == sum );
1010    assert( iwalk - t->tierFronts == sum );
1011
1012    if( trackerSupportsScrape( t ) )
1013        enqueueScrape( t->handle, t );
1014
1015    return t;
1016}
1017
1018static void
1019onTrackerFreeNow( void * vt )
1020{
1021    int i;
1022    tr_tracker * t = vt;
1023
1024    tr_publisherFree( &t->publisher );
1025    tr_free( t->name );
1026    tr_free( t->trackerID );
1027
1028    /* addresses... */
1029    for( i=0; i<t->addressCount; ++i )
1030        tr_trackerInfoClear( &t->addresses[i] );
1031    tr_free( t->addresses );
1032    tr_free( t->tierFronts );
1033
1034    /* redirect... */
1035    if( t->redirect ) {
1036        tr_trackerInfoClear( t->redirect );
1037        tr_free( t->redirect );
1038    }
1039
1040    tr_free( t );
1041}
1042
1043void
1044tr_trackerFree( tr_tracker * t )
1045{
1046    tr_runInEventThread( t->handle, onTrackerFreeNow, t );
1047}
1048
1049
1050/***
1051****  PUBLIC
1052***/
1053
1054tr_publisher_tag
1055tr_trackerSubscribe( tr_tracker          * t,
1056                     tr_delivery_func      func,
1057                     void                * user_data )
1058{
1059    return tr_publisherSubscribe( t->publisher, func, user_data );
1060}
1061
1062void
1063tr_trackerUnsubscribe( tr_tracker        * t,
1064                       tr_publisher_tag    tag )
1065{
1066    tr_publisherUnsubscribe( t->publisher, tag );
1067}
1068
1069const tr_tracker_info *
1070tr_trackerGetAddress( const tr_tracker   * t )
1071{
1072    return getCurrentAddress( t );
1073}
1074
1075int
1076tr_trackerCanManualAnnounce ( const tr_tracker * t)
1077{
1078    return t->isRunning
1079            && ( time(NULL) >= t->manualAnnounceAllowedAt );
1080}
1081
1082void
1083tr_trackerGetCounts( const tr_tracker  * t,
1084                     int               * setme_completedCount,
1085                     int               * setme_leecherCount,
1086                     int               * setme_seederCount )
1087{
1088    if( setme_completedCount )
1089       *setme_completedCount = t->timesDownloaded;
1090
1091    if( setme_leecherCount )
1092       *setme_leecherCount = t->leecherCount;
1093
1094    if( setme_seederCount )
1095       *setme_seederCount = t->seederCount;
1096}
1097
1098
1099void
1100tr_trackerStart( tr_tracker * t )
1101{
1102    tr_peerIdNew( t->peer_id, sizeof(t->peer_id) );
1103    if( t->isRunning == 0 ) {
1104        t->isRunning = 1;
1105        enqueueRequest( t->handle, t, TR_REQ_STARTED );
1106    }
1107}
1108
1109void
1110tr_trackerReannounce( tr_tracker * t )
1111{
1112    enqueueRequest( t->handle, t, TR_REQ_REANNOUNCE );
1113}
1114
1115void
1116tr_trackerCompleted( tr_tracker * t )
1117{
1118    enqueueRequest( t->handle, t, TR_REQ_COMPLETED );
1119}
1120
1121void
1122tr_trackerStop( tr_tracker * t )
1123{
1124    if( t->isRunning ) {
1125        t->isRunning = 0;
1126        enqueueRequest( t->handle, t, TR_REQ_STOPPED );
1127    }
1128}
1129
1130void
1131tr_trackerChangeMyPort( tr_tracker * t )
1132{
1133    if( t->isRunning )
1134        tr_trackerReannounce( t );
1135}
Note: See TracBrowser for help on using the repository browser.