source: trunk/libtransmission/tracker.c @ 4852

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

(1.0x) #660: fix HTTP syntax error that caused "Bad Request" messages on lighttpd-based trackers. Reported by _Psih

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