source: trunk/libtransmission/tracker.c @ 5626

Last change on this file since 5626 was 5626, checked in by charles, 14 years ago

#879: fix segfault if tracker is freed immediately after starting a torrent

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