source: trunk/libtransmission/tracker.c @ 4065

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

fix tracker connection error reported by Gimp_

  • Property svn:keywords set to Date Rev Author Id
File size: 32.6 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 4065 2007-12-05 02:06:05Z 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 onReqDone( tr_handle * handle );
383
384static void
385onTrackerResponse( struct evhttp_request * req, void * vhash )
386{
387    const char * warning;
388    int responseCode;
389    struct torrent_hash * torrent_hash = (struct torrent_hash*) vhash;
390    tr_tracker * t = findTrackerFromHash( torrent_hash );
391
392    onReqDone( torrent_hash->handle );
393    tr_free( torrent_hash );
394
395    if( t == NULL ) /* tracker has been closed */
396        return;
397
398    dbgmsg( t, "got response from tracker: \"%s\"",
399            ( req && req->response_code_line ) ?  req->response_code_line
400                                               : "(null)" );
401
402    tr_inf( "Torrent \"%s\" tracker response: %s",
403            t->name,
404            ( req ? req->response_code_line : "(null)") );
405
406    if( req && ( req->response_code == HTTP_OK ) )
407    {
408        benc_val_t benc;
409        const int bencLoaded = !parseBencResponse( req, &benc );
410
411        publishErrorClear( t );
412
413        if( bencLoaded && benc.type==TYPE_DICT )
414        {
415            benc_val_t * tmp;
416
417            if(( tmp = tr_bencDictFind( &benc, "failure reason" ))) {
418                dbgmsg( t, "got failure message [%s]", tmp->val.s.s );
419                publishErrorMessageAndStop( t, tmp->val.s.s );
420            }
421
422            if(( tmp = tr_bencDictFind( &benc, "warning message" ))) {
423                dbgmsg( t, "got warning message [%s]", tmp->val.s.s );
424                publishWarning( t, tmp->val.s.s );
425            }
426
427            if(( tmp = tr_bencDictFind( &benc, "interval" ))) {
428                dbgmsg( t, "setting interval to %d", tmp->val.i );
429                t->announceIntervalSec = tmp->val.i;
430            }
431
432            if(( tmp = tr_bencDictFind( &benc, "min interval" ))) {
433                dbgmsg( t, "setting min interval to %d", tmp->val.i );
434                t->announceMinIntervalSec = tmp->val.i;
435            }
436
437            if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
438                t->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
439
440            if(( tmp = tr_bencDictFind( &benc, "complete" )))
441                t->seederCount = tmp->val.i;
442
443            if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
444                t->leecherCount = tmp->val.i;
445
446            if(( tmp = tr_bencDictFind( &benc, "peers" )))
447            {
448                int peerCount = 0;
449                uint8_t * peerCompact = NULL;
450
451                if( tmp->type == TYPE_LIST ) /* original protocol */
452                {
453                    if( tmp->val.l.count > 0 )
454                        peerCompact = parseOldPeers( tmp, &peerCount );
455                }
456                else if( tmp->type == TYPE_STR ) /* "compact" extension */
457                {
458                    if( tmp->val.s.i >= 6 )
459                    {
460                        peerCount = tmp->val.s.i / 6;
461                        peerCompact = tr_new( uint8_t, tmp->val.s.i );
462                        memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
463                    }
464                }
465
466                publishNewPeers( t, peerCount, peerCompact );
467                tr_free( peerCompact );
468            }
469        }
470
471        if( bencLoaded )
472            tr_bencFree( &benc );
473    }
474
475    if (( warning = updateAddresses( t, req ) )) {
476        publishWarning( t, warning );
477        tr_err( warning );
478    }
479
480    /**
481    ***
482    **/
483
484    responseCode = req ? req->response_code : 503;
485
486    if( 200<=responseCode && responseCode<=299 )
487    {
488        dbgmsg( t, "request succeeded. reannouncing in %d seconds",
489                   t->announceIntervalSec );
490        t->reannounceAt = time(NULL) + t->announceIntervalSec;
491        t->manualAnnounceAllowedAt = time(NULL) + t->announceMinIntervalSec;
492    }
493    else if( 300<=responseCode && responseCode<=399 )
494    {
495        dbgmsg( t, "got a redirect; retrying immediately" );
496
497        /* it's a redirect... updateAddresses() has already
498         * parsed the redirect, all that's left is to retry */
499        t->reannounceAt = time(NULL);
500        t->manualAnnounceAllowedAt = time(NULL) + t->announceMinIntervalSec;
501    }
502    else if( 400<=responseCode && responseCode<=499 )
503    {
504        const char * err = req && req->response_code_line
505            ? req->response_code_line
506            : "Unspecified 4xx error from tracker.";
507        dbgmsg( t, err );
508
509        /* The request could not be understood by the server due to
510         * malformed syntax. The client SHOULD NOT repeat the
511         * request without modifications. */
512        publishErrorMessageAndStop( t, err );
513        t->manualAnnounceAllowedAt = ~(time_t)0;
514        t->reannounceAt = 0;
515    }
516    else if( 500<=responseCode && responseCode<=599 )
517    {
518        dbgmsg( t, "Got a 5xx error... retrying in 15 seconds." );
519
520        /* Response status codes beginning with the digit "5" indicate
521         * cases in which the server is aware that it has erred or is
522         * incapable of performing the request.  So we pause a bit and
523         * try again. */
524        if( req && req->response_code_line )
525            publishWarning( t, req->response_code_line );
526        t->manualAnnounceAllowedAt = ~(time_t)0;
527        t->reannounceAt = time(NULL) + 15;
528    }
529    else
530    {
531        dbgmsg( t, "Invalid response from tracker... retrying in 60 seconds." );
532
533        /* WTF did we get?? */
534        if( req && req->response_code_line )
535            publishWarning( t, req->response_code_line );
536        t->manualAnnounceAllowedAt = ~(time_t)0;
537        t->reannounceAt = time(NULL) + 60;
538    }
539}
540
541static void
542onScrapeResponse( struct evhttp_request * req, void * vhash )
543{
544    const char * warning;
545    time_t nextScrapeSec = 60;
546    struct torrent_hash * torrent_hash = (struct torrent_hash*) vhash;
547    tr_tracker * t = findTrackerFromHash( torrent_hash );
548
549    onReqDone( torrent_hash->handle );
550    tr_free( torrent_hash );
551
552    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) );
553
554    if( t == NULL ) /* tracker's been closed... */
555        return;
556
557    tr_inf( "Got scrape response for  '%s': %s",
558            t->name,
559            ( ( req && req->response_code_line ) ? req->response_code_line
560                                                 : "(null)") );
561
562    if( req && ( req->response_code == HTTP_OK ) )
563    {
564        benc_val_t benc, *files;
565        const int bencLoaded = !parseBencResponse( req, &benc );
566
567        if( bencLoaded
568            && (( files = tr_bencDictFind( &benc, "files" ) ))
569            && ( files->type == TYPE_DICT ) )
570        {
571            int i;
572            for( i=0; i<files->val.l.count; i+=2 )
573            {
574                const uint8_t* hash =
575                        (const uint8_t*) files->val.l.vals[i].val.s.s;
576                benc_val_t *tmp, *flags;
577                benc_val_t *tordict = &files->val.l.vals[i+1];
578                if( memcmp( t->hash, hash, SHA_DIGEST_LENGTH ) )
579                    continue;
580
581                publishErrorClear( t );
582
583                if(( tmp = tr_bencDictFind( tordict, "complete" )))
584                    t->seederCount = tmp->val.i;
585
586                if(( tmp = tr_bencDictFind( tordict, "incomplete" )))
587                    t->leecherCount = tmp->val.i;
588
589                if(( tmp = tr_bencDictFind( tordict, "downloaded" )))
590                    t->timesDownloaded = tmp->val.i;
591
592                if(( flags = tr_bencDictFind( tordict, "flags" )))
593                    if(( tmp = tr_bencDictFind( flags, "min_request_interval")))
594                        t->scrapeIntervalSec = tmp->val.i;
595
596                tr_dbg( "Torrent '%s' scrape successful."
597                        "  Rescraping in %d seconds",
598                        t->name, t->scrapeIntervalSec );
599
600                nextScrapeSec = t->scrapeIntervalSec;
601            }
602        }
603
604        if( bencLoaded )
605            tr_bencFree( &benc );
606    }
607
608    if (( warning = updateAddresses( t, req ) )) {
609        tr_err( warning );
610        publishWarning( t, warning );
611    }
612
613    t->scrapeAt = time(NULL) + nextScrapeSec;
614}
615
616/***
617****
618***/
619
620enum
621{
622    TR_REQ_STARTED,
623    TR_REQ_COMPLETED,
624    TR_REQ_STOPPED,
625    TR_REQ_REANNOUNCE,
626    TR_REQ_SCRAPE,
627    TR_REQ_COUNT
628};
629
630struct tr_tracker_request
631{
632    int port;
633    int timeout;
634    int reqtype; /* TR_REQ_* */
635    char * address;
636    char * uri;
637    struct evhttp_request * req;
638    uint8_t torrent_hash[SHA_DIGEST_LENGTH];
639};
640
641static void
642freeRequest( struct tr_tracker_request * req )
643{
644    tr_free( req->address );
645    tr_free( req->uri );
646    tr_free( req );
647}
648
649static void
650addCommonHeaders( const tr_tracker * t,
651                  struct evhttp_request * req )
652{
653    char buf[1024];
654    const tr_tracker_info * address = getCurrentAddress( t );
655    snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
656    evhttp_add_header( req->output_headers, "Host", buf );
657    evhttp_add_header( req->output_headers, "Connection", "close" );
658    evhttp_add_header( req->output_headers, "Content-Length", "0" );
659    evhttp_add_header( req->output_headers, "User-Agent",
660                                         TR_NAME "/" LONG_VERSION_STRING );
661}
662
663static char*
664buildTrackerRequestURI( const tr_tracker  * t,
665                        const tr_torrent  * torrent,
666                        const char        * eventName )
667{
668    const int isStopping = !strcmp( eventName, "stopped" );
669    const int numwant = isStopping ? 0 : NUMWANT;
670    struct evbuffer * buf = evbuffer_new( );
671    char * ret;
672
673    char * ann = getCurrentAddress(t)->announce;
674   
675    evbuffer_add_printf( buf, "%s"
676                              "%cinfo_hash=%s"
677                              "&peer_id=%s"
678                              "&port=%d"
679                              "&uploaded=%"PRIu64
680                              "&downloaded=%"PRIu64
681                              "&corrupt=%"PRIu64
682                              "&left=%"PRIu64
683                              "&compact=1"
684                              "&numwant=%d"
685                              "&key=%s"
686                              "&supportcrypto=1"
687                              "&requirecrypto=%d"
688                              "%s%s"
689                              "%s%s",
690        ann,
691        strchr(ann, '?') ? '&' : '?',
692        t->escaped,
693        t->peer_id,
694        tr_sharedGetPublicPort( t->handle->shared ),
695        torrent->uploadedCur,
696        torrent->downloadedCur,
697        torrent->corruptCur,
698        tr_cpLeftUntilComplete( torrent->completion ),
699        numwant,
700        t->key_param,
701        ( t->handle->encryptionMode==TR_ENCRYPTION_REQUIRED ? 1 : 0 ),
702        ( ( eventName && *eventName ) ? "&event=" : "" ),
703        ( ( eventName && *eventName ) ? eventName : "" ),
704        ( ( t->trackerID && *t->trackerID ) ? "&trackerid=" : "" ),
705        ( ( t->trackerID && *t->trackerID ) ? t->trackerID : "" ) );
706
707    ret = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
708    evbuffer_free( buf );
709    return ret;
710}
711
712static struct tr_tracker_request*
713createRequest( tr_handle * handle, const tr_tracker * tracker, int reqtype )
714{
715    static const char* strings[TR_REQ_COUNT] = { "started", "completed", "stopped", "", "err" };
716    const tr_torrent * torrent = tr_torrentFindFromHash( handle, tracker->hash );
717    const tr_tracker_info * address = getCurrentAddress( tracker );
718    const int isStopping = reqtype == TR_REQ_STOPPED;
719    const char * eventName = strings[reqtype];
720    struct tr_tracker_request * req;
721
722    req = tr_new0( struct tr_tracker_request, 1 );
723    req->address = tr_strdup( address->address );
724    req->port = address->port;
725    req->uri = buildTrackerRequestURI( tracker, torrent, eventName );
726    req->timeout = isStopping ? STOP_TIMEOUT_INTERVAL_SEC : TIMEOUT_INTERVAL_SEC;
727    req->reqtype = reqtype;
728    req->req = isStopping
729        ? evhttp_request_new( onStoppedResponse, handle )
730        : evhttp_request_new( onTrackerResponse, torrentHashNew(handle, tracker) );
731    memcpy( req->torrent_hash, tracker->hash, SHA_DIGEST_LENGTH );
732    addCommonHeaders( tracker, req->req );
733
734    return req;
735}
736
737static struct tr_tracker_request*
738createScrape( tr_handle * handle, const tr_tracker * tracker )
739{
740    const tr_tracker_info * a = getCurrentAddress( tracker );
741    struct tr_tracker_request * req;
742
743    req = tr_new0( struct tr_tracker_request, 1 );
744    req->address = tr_strdup( a->address );
745    req->port = a->port;
746    req->timeout = TIMEOUT_INTERVAL_SEC;
747    req->req = evhttp_request_new( onScrapeResponse, torrentHashNew( handle, tracker ) );
748    req->reqtype = TR_REQ_SCRAPE;
749    tr_asprintf( &req->uri, "%s%cinfo_hash=%s", a->scrape, strchr(a->scrape,'?')?'&':'?', tracker->escaped );
750    memcpy( req->torrent_hash, tracker->hash, SHA_DIGEST_LENGTH );
751    addCommonHeaders( tracker, req->req );
752
753    return req;
754}
755
756struct tr_tracker_handle
757{
758    int socketCount;
759    tr_timer * pulseTimer;
760    tr_list * requestQueue;
761    tr_list * scrapeQueue;
762};
763
764static int pulse( void * vhandle );
765
766static void
767ensureGlobalsExist( tr_handle * handle )
768{
769    if( handle->tracker == NULL )
770    {
771        handle->tracker = tr_new0( struct tr_tracker_handle, 1 );
772        handle->tracker->pulseTimer = tr_timerNew( handle, pulse, handle, PULSE_INTERVAL_MSEC );
773        dbgmsg( NULL, "creating tracker timer" );
774    }
775}
776
777static void
778freeRequest2( void * req )
779{
780    freeRequest( req );
781}
782
783void
784tr_trackerShuttingDown( tr_handle * handle )
785{
786    /* since we're shutting down, we don't need to scrape anymore... */
787    if( handle->tracker )
788        tr_list_free( &handle->tracker->scrapeQueue, freeRequest2 );
789}
790
791static int
792maybeFreeGlobals( tr_handle * handle )
793{
794    int globalsExist = handle->tracker != NULL;
795
796    if( globalsExist
797        && ( handle->tracker->socketCount < 1 )
798        && ( handle->tracker->requestQueue == NULL )
799        && ( handle->tracker->scrapeQueue == NULL )
800        && ( handle->torrentList== NULL ) )
801    {
802        dbgmsg( NULL, "freeing tracker timer" );
803        tr_timerFree( &handle->tracker->pulseTimer );
804        tr_free( handle->tracker );
805        handle->tracker = NULL;
806        globalsExist = FALSE;
807    }
808
809    return globalsExist;
810}
811
812/***
813****
814***/
815
816static int
817freeConnection( void * evcon )
818{
819    evhttp_connection_free( evcon );
820    return FALSE;
821}
822static void
823connectionClosedCB( struct evhttp_connection * evcon, void * vhandle )
824{
825    tr_handle * handle = vhandle;
826
827    /* libevent references evcon right after calling this function,
828       so we can't free it yet... defer it to after this call chain
829       has played out */
830    tr_timerNew( handle, freeConnection, evcon, 100 );
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
932static void
933onReqDone( tr_handle * handle )
934{
935    pulse( handle );
936    --handle->tracker->socketCount;
937    dbgmsg( NULL, "decrementing socket count to %d", handle->tracker->socketCount );
938}
939
940/***
941****  LIFE CYCLE
942***/
943
944static void
945generateKeyParam( char * msg, int len )
946{
947    int i;
948    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
949    const int poolSize = strlen( pool );
950    for( i=0; i<len; ++i )
951        *msg++ = pool[tr_rand(poolSize)];
952    *msg = '\0';
953}
954
955static void
956escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
957{
958    const uint8_t *end = in + in_len;
959    while( in != end )
960        if( isalnum(*in) )
961            *out++ = (char) *in++;
962        else 
963            out += snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
964    *out = '\0';
965}
966
967tr_tracker *
968tr_trackerNew( const tr_torrent * torrent )
969{
970    const tr_info * info = &torrent->info;
971    int i, j, sum, *iwalk;
972    tr_tracker_info * nwalk;
973    tr_tracker * t;
974
975    tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress );
976
977    t = tr_new0( tr_tracker, 1 );
978    t->handle = torrent->handle;
979    t->scrapeIntervalSec       = DEFAULT_SCRAPE_INTERVAL_SEC;
980    t->announceIntervalSec     = DEFAULT_ANNOUNCE_INTERVAL_SEC;
981    t->announceMinIntervalSec  = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
982    generateKeyParam( t->key_param, KEYLEN );
983
984    t->publisher = tr_publisherNew( );
985    t->timesDownloaded = -1;
986    t->seederCount = -1;
987    t->leecherCount = -1;
988    t->manualAnnounceAllowedAt = ~(time_t)0;
989    t->name = tr_strdup( info->name );
990    memcpy( t->hash, info->hash, SHA_DIGEST_LENGTH );
991    escape( t->escaped, info->hash, SHA_DIGEST_LENGTH );
992
993    for( sum=i=0; i<info->trackerTiers; ++i )
994         sum += info->trackerList[i].count;
995    t->addresses = nwalk = tr_new0( tr_tracker_info, sum );
996    t->addressIndex = 0;
997    t->addressCount = sum;
998    t->tierFronts = iwalk = tr_new0( int, sum );
999
1000    for( i=0; i<info->trackerTiers; ++i )
1001    {
1002        const int tierFront = nwalk - t->addresses;
1003
1004        for( j=0; j<info->trackerList[i].count; ++j )
1005        {
1006            const tr_tracker_info * src = &info->trackerList[i].list[j];
1007            nwalk->address = tr_strdup( src->address );
1008            nwalk->port = src->port;
1009            nwalk->announce = tr_strdup( src->announce );
1010            nwalk->scrape = tr_strdup( src->scrape );
1011            ++nwalk;
1012
1013            *iwalk++ = tierFront;
1014        }
1015    }
1016
1017    assert( nwalk - t->addresses == sum );
1018    assert( iwalk - t->tierFronts == sum );
1019
1020    if( trackerSupportsScrape( t ) )
1021        enqueueScrape( t->handle, t );
1022
1023    return t;
1024}
1025
1026static void
1027onTrackerFreeNow( void * vt )
1028{
1029    int i;
1030    tr_tracker * t = vt;
1031
1032    tr_publisherFree( &t->publisher );
1033    tr_free( t->name );
1034    tr_free( t->trackerID );
1035
1036    /* addresses... */
1037    for( i=0; i<t->addressCount; ++i )
1038        tr_trackerInfoClear( &t->addresses[i] );
1039    tr_free( t->addresses );
1040    tr_free( t->tierFronts );
1041
1042    /* redirect... */
1043    if( t->redirect ) {
1044        tr_trackerInfoClear( t->redirect );
1045        tr_free( t->redirect );
1046    }
1047
1048    tr_free( t );
1049}
1050
1051void
1052tr_trackerFree( tr_tracker * t )
1053{
1054    tr_runInEventThread( t->handle, onTrackerFreeNow, t );
1055}
1056
1057
1058/***
1059****  PUBLIC
1060***/
1061
1062tr_publisher_tag
1063tr_trackerSubscribe( tr_tracker          * t,
1064                     tr_delivery_func      func,
1065                     void                * user_data )
1066{
1067    return tr_publisherSubscribe( t->publisher, func, user_data );
1068}
1069
1070void
1071tr_trackerUnsubscribe( tr_tracker        * t,
1072                       tr_publisher_tag    tag )
1073{
1074    tr_publisherUnsubscribe( t->publisher, tag );
1075}
1076
1077const tr_tracker_info *
1078tr_trackerGetAddress( const tr_tracker   * t )
1079{
1080    return getCurrentAddress( t );
1081}
1082
1083int
1084tr_trackerCanManualAnnounce ( const tr_tracker * t)
1085{
1086    return t->isRunning
1087            && ( time(NULL) >= t->manualAnnounceAllowedAt );
1088}
1089
1090void
1091tr_trackerGetCounts( const tr_tracker  * t,
1092                     int               * setme_completedCount,
1093                     int               * setme_leecherCount,
1094                     int               * setme_seederCount )
1095{
1096    if( setme_completedCount )
1097       *setme_completedCount = t->timesDownloaded;
1098
1099    if( setme_leecherCount )
1100       *setme_leecherCount = t->leecherCount;
1101
1102    if( setme_seederCount )
1103       *setme_seederCount = t->seederCount;
1104}
1105
1106
1107void
1108tr_trackerStart( tr_tracker * t )
1109{
1110    tr_peerIdNew( t->peer_id, sizeof(t->peer_id) );
1111    if( t->isRunning == 0 ) {
1112        t->isRunning = 1;
1113        enqueueRequest( t->handle, t, TR_REQ_STARTED );
1114    }
1115}
1116
1117void
1118tr_trackerReannounce( tr_tracker * t )
1119{
1120    enqueueRequest( t->handle, t, TR_REQ_REANNOUNCE );
1121}
1122
1123void
1124tr_trackerCompleted( tr_tracker * t )
1125{
1126    enqueueRequest( t->handle, t, TR_REQ_COMPLETED );
1127}
1128
1129void
1130tr_trackerStop( tr_tracker * t )
1131{
1132    if( t->isRunning ) {
1133        t->isRunning = 0;
1134        enqueueRequest( t->handle, t, TR_REQ_STOPPED );
1135    }
1136}
1137
1138void
1139tr_trackerChangeMyPort( tr_tracker * t )
1140{
1141    if( t->isRunning )
1142        tr_trackerReannounce( t );
1143}
Note: See TracBrowser for help on using the repository browser.