source: trunk/libtransmission/tracker.c @ 6617

Last change on this file since 6617 was 6617, checked in by charles, 13 years ago

maybe fix m1b's tracker issue

  • Property svn:keywords set to Date Rev Author Id
File size: 28.8 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 6617 2008-08-21 20:18:34Z charles $
11 */
12
13#include <assert.h>
14#include <stdlib.h>
15#include <string.h> /* strcmp, strchr */
16
17#include <event.h>
18
19#include "transmission.h"
20#include "bencode.h"
21#include "crypto.h"
22#include "completion.h"
23#include "net.h"
24#include "publish.h"
25#include "resume.h"
26#include "torrent.h"
27#include "tracker.h"
28#include "trevent.h"
29#include "utils.h"
30#include "web.h"
31
32enum
33{
34    HTTP_OK = 200,
35
36    /* seconds between tracker pulses */
37    PULSE_INTERVAL_MSEC = 1000,
38
39    /* unless the tracker says otherwise, rescrape this frequently */
40    DEFAULT_SCRAPE_INTERVAL_SEC = (60 * 15),
41
42    /* unless the tracker says otherwise, this is the announce interval */
43    DEFAULT_ANNOUNCE_INTERVAL_SEC = (60 * 4),
44
45    /* unless the tracker says otherwise, this is the announce min_interval */
46    DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = (60 * 2),
47
48    /* how long to wait before a rescrape the first time we get an error */
49    FIRST_SCRAPE_RETRY_INTERVAL_SEC = 30,
50
51    /* how long to wait before a reannounce the first time we get an error */
52    FIRST_ANNOUNCE_RETRY_INTERVAL_SEC = 30,
53
54    /* the value of the 'numwant' argument passed in tracker requests. */
55    NUMWANT = 80,
56
57    /* the length of the 'key' argument passed in tracker requests */
58    KEYLEN = 10
59};
60
61/**
62***
63**/
64
65struct tr_tracker
66{
67    unsigned int isRunning     : 1;
68
69    uint8_t randOffset;
70
71    /* sent as the "key" argument in tracker requests
72       to verify us if our IP address changes.
73       This is immutable for the life of the tracker object. */
74    char key_param[KEYLEN+1];
75
76    /* these are set from the latest scrape or tracker response */
77    int announceIntervalSec;
78    int announceMinIntervalSec;
79    int scrapeIntervalSec;
80    int retryScrapeIntervalSec;
81    int retryAnnounceIntervalSec;
82
83    /* index into the torrent's tr_info.trackers array */
84    int trackerIndex;
85
86    tr_session * session;
87
88    tr_publisher_t * publisher;
89
90    /* torrent hash string */
91    uint8_t hash[SHA_DIGEST_LENGTH];
92    char escaped[SHA_DIGEST_LENGTH*3 + 1];
93    char * name;
94
95    /* corresponds to the peer_id sent as a tracker request parameter.
96       one tracker admin says: "When the same torrent is opened and
97       closed and opened again without quitting Transmission ...
98       change the peerid. It would help sometimes if a stopped event
99       was missed to ensure that we didn't think someone was cheating. */
100    uint8_t * peer_id;
101
102    /* these are set from the latest tracker response... -1 is 'unknown' */
103    int timesDownloaded;
104    int seederCount;
105    int leecherCount;
106    char * trackerID;
107
108    time_t manualAnnounceAllowedAt;
109    time_t reannounceAt;
110
111    /* 0==never, 1==in progress, other values==when to scrape */
112    time_t scrapeAt;
113
114    time_t lastScrapeTime;
115    long lastScrapeResponse;
116
117    time_t lastAnnounceTime;
118    long lastAnnounceResponse;
119};
120
121#define dbgmsg(name, fmt...) tr_deepLog(__FILE__, __LINE__, name, ##fmt )
122
123/***
124****
125***/
126
127static const tr_tracker_info *
128getCurrentAddressFromTorrent( tr_tracker * t, const tr_torrent * tor )
129{
130    /* user might have removed trackers,
131     * so check to make sure our current index is in-bounds */
132    if( t->trackerIndex >= tor->info.trackerCount )
133        t->trackerIndex = 0;
134
135    assert( t->trackerIndex >= 0 );
136    assert( t->trackerIndex < tor->info.trackerCount );
137    return tor->info.trackers + t->trackerIndex;
138}
139   
140static const tr_tracker_info *
141getCurrentAddress( tr_tracker * t )
142{
143    const tr_torrent * torrent;
144    if(( torrent = tr_torrentFindFromHash( t->session, t->hash )))
145        return getCurrentAddressFromTorrent( t, torrent );
146    return NULL;
147}
148
149static int
150trackerSupportsScrape( tr_tracker * t, const tr_torrent * tor )
151{
152    const tr_tracker_info * info = getCurrentAddressFromTorrent( t, tor );
153    return info && info->scrape;
154}
155
156/***
157****
158***/
159
160static tr_tracker *
161findTracker( tr_session * session, const uint8_t * hash )
162{
163    tr_torrent * torrent = tr_torrentFindFromHash( session, hash );
164    return torrent ? torrent->tracker : NULL;
165}
166
167/***
168****  PUBLISH
169***/
170
171static const tr_tracker_event emptyEvent = { 0, NULL, NULL, NULL, 0, 0 };
172
173static void
174publishMessage( tr_tracker * t, const char * msg, int type )
175{
176    if( t )
177    {
178        tr_tracker_event event = emptyEvent;
179        event.hash = t->hash;
180        event.messageType = type;
181        event.text = msg;
182        tr_publisherPublish( t->publisher, t, &event );
183    }
184}
185
186static void
187publishErrorClear( tr_tracker * t )
188{
189    publishMessage( t, NULL, TR_TRACKER_ERROR_CLEAR );
190}
191
192static void
193publishErrorMessageAndStop( tr_tracker * t, const char * msg )
194{
195    t->isRunning = 0;
196    publishMessage( t, msg, TR_TRACKER_ERROR );
197}
198
199static void
200publishWarning( tr_tracker * t, const char * msg )
201{
202    publishMessage( t, msg, TR_TRACKER_WARNING );
203}
204
205static void
206publishNewPeers( tr_tracker * t, int allAreSeeds,
207                 void * compact, int compactLen )
208{
209    tr_tracker_event event = emptyEvent;
210    event.hash = t->hash;
211    event.messageType = TR_TRACKER_PEERS;
212    event.allAreSeeds = allAreSeeds;
213    event.compact = compact;
214    event.compactLen = compactLen;
215    if( compactLen )
216        tr_publisherPublish( t->publisher, t, &event );
217}
218
219/***
220****
221***/
222
223static void onReqDone( tr_session * session );
224
225static int
226updateAddresses( tr_tracker  * t, int success )
227{
228    int retry;
229
230    tr_torrent * torrent = tr_torrentFindFromHash( t->session, t->hash );
231
232    if( success )
233    {
234        /* multitracker spec: "if a connection with a tracker is
235           successful, it will be moved to the front of the tier." */
236        t->trackerIndex = tr_torrentPromoteTracker( torrent, t->trackerIndex );
237        retry = FALSE; /* we succeeded; no need to retry */
238    }
239    else if ( ++t->trackerIndex >= torrent->info.trackerCount )
240    {
241        t->trackerIndex = 0;
242        retry = FALSE; /* we've tried them all */
243    }
244    else
245    {
246        const tr_tracker_info * n = getCurrentAddressFromTorrent( t, torrent );
247        tr_ninf( t->name, _( "Trying tracker \"%s\"" ), n->announce );
248        retry = TRUE;
249    }
250
251    return retry;
252}
253
254/* Convert to compact form */
255static uint8_t *
256parseOldPeers( tr_benc * bePeers, size_t * byteCount )
257{
258    int i;
259    uint8_t *compact, *walk;
260    const int peerCount = bePeers->val.l.count;
261
262    assert( bePeers->type == TYPE_LIST );
263
264    compact = tr_new( uint8_t, peerCount*6 );
265
266    for( i=0, walk=compact; i<peerCount; ++i )
267    {
268        const char * s;
269        int64_t itmp;
270        struct in_addr addr;
271        tr_port_t port;
272        tr_benc * peer = &bePeers->val.l.vals[i];
273
274        if( !tr_bencDictFindStr( peer, "ip", &s ) || tr_netResolve( s, &addr ) )
275            continue;
276
277        memcpy( walk, &addr, 4 );
278        walk += 4;
279
280        if( !tr_bencDictFindInt( peer, "port", &itmp ) || itmp<0 || itmp>0xffff )
281            continue;
282
283        port = htons( itmp );
284        memcpy( walk, &port, 2 );
285        walk += 2;
286    }
287
288    *byteCount = peerCount * 6;
289    return compact;
290}
291
292static void
293onStoppedResponse( tr_session    * session,
294                   long            responseCode  UNUSED,
295                   const void    * response      UNUSED,
296                   size_t          responseLen   UNUSED,
297                   void          * torrent_hash  )
298{
299    dbgmsg( NULL, "got a response to some `stop' message" );
300    tr_free( torrent_hash );
301    onReqDone( session );
302}
303
304static void
305onTrackerResponse( tr_session    * session,
306                   long            responseCode,
307                   const void    * response,
308                   size_t          responseLen,
309                   void          * torrent_hash )
310{
311    int retry;
312    int success = FALSE;
313    tr_tracker * t;
314
315    onReqDone( session );
316    t = findTracker( session, torrent_hash );
317    tr_free( torrent_hash );
318    if( !t ) /* tracker's been closed */
319        return;
320
321    dbgmsg( t->name, "tracker response: %ld", responseCode );
322    tr_ndbg( t->name, "tracker response: %ld", responseCode );
323    t->lastAnnounceResponse = responseCode;
324
325    if( responseCode == HTTP_OK )
326    {
327        tr_benc benc;
328        const int bencLoaded = !tr_bencLoad( response, responseLen,
329                                             &benc, NULL );
330        publishErrorClear( t );
331        if( bencLoaded && tr_bencIsDict( &benc ) )
332        {
333            tr_benc * tmp;
334            int64_t i;
335            int incomplete = -1;
336            const char * str;
337
338            success = TRUE;
339            t->retryAnnounceIntervalSec = FIRST_SCRAPE_RETRY_INTERVAL_SEC;
340
341            if(( tr_bencDictFindStr( &benc, "failure reason", &str ))) {
342                publishMessage( t, str, TR_TRACKER_ERROR );
343                success = FALSE;
344            }
345
346            if(( tr_bencDictFindStr( &benc, "warning message", &str )))
347                publishWarning( t, str );
348
349            if(( tr_bencDictFindInt( &benc, "interval", &i ))) {
350                dbgmsg( t->name, "setting interval to %d", (int)i );
351                t->announceIntervalSec = i;
352            }
353
354            if(( tr_bencDictFindInt( &benc, "min interval", &i ))) {
355                dbgmsg( t->name, "setting min interval to %d", (int)i );
356                t->announceMinIntervalSec = i;
357            }
358
359            if(( tr_bencDictFindStr( &benc, "tracker id", &str )))
360                t->trackerID = tr_strdup( str );
361
362            if(( tr_bencDictFindInt( &benc, "complete", &i )))
363                t->seederCount = i;
364
365            if(( tr_bencDictFindInt( &benc, "incomplete", &i )))
366                t->leecherCount = incomplete = i;
367
368            if(( tmp = tr_bencDictFind( &benc, "peers" )))
369            {
370                const int allAreSeeds = incomplete == 0;
371
372                if( tmp->type == TYPE_STR ) /* "compact" extension */
373                {
374                    publishNewPeers( t, allAreSeeds, tmp->val.s.s, tmp->val.s.i );
375                }
376                else if( tmp->type == TYPE_LIST ) /* original protocol */
377                {
378                    size_t byteCount = 0;
379                    uint8_t * compact = parseOldPeers( tmp, &byteCount );
380                    publishNewPeers( t, allAreSeeds, compact, byteCount );
381                    tr_free( compact );
382                }
383            }
384        }
385
386        if( bencLoaded )
387            tr_bencFree( &benc );
388    }
389
390    retry = updateAddresses( t, success );
391
392    /**
393    ***
394    **/
395
396    if( retry )
397        responseCode = 300;
398
399    if( 200<=responseCode && responseCode<=299 )
400    {
401        const int interval = t->announceIntervalSec + t->randOffset;
402        const time_t now = time ( NULL );
403        dbgmsg( t->name, "request succeeded. reannouncing in %d seconds", interval );
404        t->scrapeAt = now + t->scrapeIntervalSec + t->randOffset;
405        t->reannounceAt = now + interval;
406        t->manualAnnounceAllowedAt = now + t->announceMinIntervalSec;
407
408        /* #319: save the .resume file after an announce so that, in case
409         * of a crash, our stats still match up with the tracker's stats */
410        tr_torrentSaveResume( tr_torrentFindFromHash( t->session, t->hash ) );
411    }
412    else if( 300<=responseCode && responseCode<=399 )
413    {
414        /* it's a redirect... updateAddresses() has already
415         * parsed the redirect, all that's left is to retry */
416        const int interval = 5;
417        dbgmsg( t->name, "got a redirect. retrying in %d seconds", interval );
418        t->reannounceAt = time( NULL ) + interval;
419        t->manualAnnounceAllowedAt = time( NULL ) + t->announceMinIntervalSec;
420    }
421    else if( 400<=responseCode && responseCode<=499 )
422    {
423        /* The request could not be understood by the server due to
424         * malformed syntax. The client SHOULD NOT repeat the
425         * request without modifications. */
426        publishErrorMessageAndStop( t, _( "Tracker returned a 4xx message" ) );
427        t->manualAnnounceAllowedAt = ~(time_t)0;
428        t->reannounceAt = 0;
429    }
430    else if( 500<=responseCode && responseCode<=599 )
431    {
432        /* Response status codes beginning with the digit "5" indicate
433         * cases in which the server is aware that it has erred or is
434         * incapable of performing the request.  So we pause a bit and
435         * try again. */
436        t->manualAnnounceAllowedAt = ~(time_t)0;
437        t->reannounceAt = time( NULL ) + t->retryAnnounceIntervalSec;
438        t->retryAnnounceIntervalSec *= 2;
439    }
440    else
441    {
442        /* WTF did we get?? */
443        dbgmsg( t->name, "Invalid response from tracker... retrying in two minutes." );
444        t->manualAnnounceAllowedAt = ~(time_t)0;
445        t->reannounceAt = time( NULL ) + t->randOffset + 120;
446    }
447}
448
449static void
450onScrapeResponse( tr_session   * session,
451                  long           responseCode,
452                  const void   * response,
453                  size_t         responseLen,
454                  void         * torrent_hash )
455{
456    int success = FALSE;
457    int retry;
458    tr_tracker * t;
459
460    onReqDone( session );
461    t = findTracker( session, torrent_hash );
462    tr_free( torrent_hash );
463    if( !t ) /* tracker's been closed... */
464        return;
465
466    dbgmsg( t->name, "scrape response: %ld\n", responseCode );
467    tr_ndbg( t->name, "scrape response: %ld", responseCode );
468    t->lastScrapeResponse = responseCode;
469
470    if( responseCode == HTTP_OK )
471    {
472        tr_benc benc, *files;
473        const int bencLoaded = !tr_bencLoad( response, responseLen,
474                                             &benc, NULL );
475        if( bencLoaded && tr_bencDictFindDict( &benc, "files", &files ) )
476        {
477            int i;
478            for( i=0; i<files->val.l.count; i+=2 )
479            {
480                int64_t itmp;
481                const uint8_t* hash =
482                    (const uint8_t*) files->val.l.vals[i].val.s.s;
483                tr_benc * flags;
484                tr_benc * tordict = &files->val.l.vals[i+1];
485                if( memcmp( t->hash, hash, SHA_DIGEST_LENGTH ) )
486                    continue;
487
488                publishErrorClear( t );
489
490                if(( tr_bencDictFindInt( tordict, "complete", &itmp )))
491                    t->seederCount = itmp;
492
493                if(( tr_bencDictFindInt( tordict, "incomplete", &itmp )))
494                    t->leecherCount = itmp;
495
496                if(( tr_bencDictFindInt( tordict, "downloaded", &itmp )))
497                    t->timesDownloaded = itmp;
498
499                if( tr_bencDictFindDict( tordict, "flags", &flags ))
500                    if(( tr_bencDictFindInt( flags, "min_request_interval", &itmp )))
501                        t->scrapeIntervalSec = i;
502
503                /* as per ticket #1045, safeguard against trackers returning
504                 * a very low min_request_interval... */
505                if( t->scrapeIntervalSec < DEFAULT_SCRAPE_INTERVAL_SEC )
506                    t->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC;
507
508                tr_ndbg( t->name, "Scrape successful.  Rescraping in %d seconds.",
509                         t->scrapeIntervalSec );
510
511                success = TRUE;
512                t->retryScrapeIntervalSec = FIRST_SCRAPE_RETRY_INTERVAL_SEC;
513            }
514        }
515
516        if( bencLoaded )
517            tr_bencFree( &benc );
518    }
519
520    retry = updateAddresses( t, success );
521
522    /**
523    ***
524    **/
525
526    if( retry )
527        responseCode = 300;
528
529    if( 200<=responseCode && responseCode<=299 )
530    {
531        const int interval = t->scrapeIntervalSec + t->randOffset;
532        dbgmsg( t->name, "request succeeded. rescraping in %d seconds", interval );
533        tr_ndbg( t->name, "request succeeded. rescraping in %d seconds", interval );
534        t->scrapeAt = time( NULL ) + interval;
535    }
536    else if( 300<=responseCode && responseCode<=399 )
537    {
538        const int interval = 5;
539        dbgmsg( t->name, "got a redirect. retrying in %d seconds", interval );
540        t->scrapeAt = time( NULL ) + interval;
541    }
542    else
543    {
544        const int interval = t->retryScrapeIntervalSec + t->randOffset;
545        dbgmsg( t->name, "Tracker responded to scrape with %ld.  Retrying in %d seconds.",
546                   responseCode,  interval );
547        t->retryScrapeIntervalSec *= 2;
548        t->scrapeAt = time( NULL ) + interval;
549    }
550}
551
552/***
553****
554***/
555
556enum
557{
558    TR_REQ_STARTED,
559    TR_REQ_COMPLETED,
560    TR_REQ_STOPPED,
561    TR_REQ_REANNOUNCE,
562    TR_REQ_SCRAPE,
563    TR_REQ_COUNT
564};
565
566struct tr_tracker_request
567{
568    uint8_t torrent_hash[SHA_DIGEST_LENGTH];
569    int reqtype; /* TR_REQ_* */
570    char * url;
571    tr_web_done_func * done_func;
572    tr_session * session;
573};
574
575static void
576freeRequest( struct tr_tracker_request * req )
577{
578    tr_free( req->url );
579    tr_free( req );
580}
581
582static void
583buildTrackerRequestURI( tr_tracker        * t,
584                        const tr_torrent  * torrent,
585                        const char        * eventName,
586                        struct evbuffer   * buf )
587{
588    const int isStopping = !strcmp( eventName, "stopped" );
589    const int numwant = isStopping ? 0 : NUMWANT;
590    const char * ann = getCurrentAddressFromTorrent(t,torrent)->announce;
591   
592    evbuffer_add_printf( buf, "%cinfo_hash=%s"
593                              "&peer_id=%s"
594                              "&port=%d"
595                              "&uploaded=%"PRIu64
596                              "&downloaded=%"PRIu64
597                              "&corrupt=%"PRIu64
598                              "&left=%"PRIu64
599                              "&compact=1"
600                              "&numwant=%d"
601                              "&key=%s"
602                              "%s%s"
603                              "%s%s",
604        strchr(ann, '?') ? '&' : '?',
605        t->escaped,
606        t->peer_id,
607        tr_sessionGetPeerPort( t->session ),
608        torrent->uploadedCur,
609        torrent->downloadedCur,
610        torrent->corruptCur,
611        tr_cpLeftUntilComplete( torrent->completion ),
612        numwant,
613        t->key_param,
614        ( ( eventName && *eventName ) ? "&event=" : "" ),
615        ( ( eventName && *eventName ) ? eventName : "" ),
616        ( ( t->trackerID && *t->trackerID ) ? "&trackerid=" : "" ),
617        ( ( t->trackerID && *t->trackerID ) ? t->trackerID : "" ) );
618}
619
620static struct tr_tracker_request*
621createRequest( tr_session * session, tr_tracker * tracker, int reqtype )
622{
623    static const char* strings[] = { "started", "completed", "stopped", "", "err" };
624    const tr_torrent * torrent = tr_torrentFindFromHash( session, tracker->hash );
625    const tr_tracker_info * address = getCurrentAddressFromTorrent( tracker, torrent );
626    const int isStopping = reqtype == TR_REQ_STOPPED;
627    struct tr_tracker_request * req;
628    struct evbuffer * url;
629
630    url = evbuffer_new( );
631    evbuffer_add_printf( url, "%s", address->announce );
632    buildTrackerRequestURI( tracker, torrent, strings[reqtype], url );
633
634    req = tr_new0( struct tr_tracker_request, 1 );
635    req->session = session;
636    req->reqtype = reqtype;
637    req->done_func =  isStopping ? onStoppedResponse : onTrackerResponse;
638    req->url = tr_strdup( EVBUFFER_DATA( url ) );
639    memcpy( req->torrent_hash, tracker->hash, SHA_DIGEST_LENGTH );
640
641    evbuffer_free( url );
642    return req;
643}
644
645static struct tr_tracker_request*
646createScrape( tr_session * session, tr_tracker * tracker )
647{
648    const tr_tracker_info * a = getCurrentAddress( tracker );
649    struct tr_tracker_request * req;
650    struct evbuffer * url = evbuffer_new( );
651
652    evbuffer_add_printf( url, "%s%cinfo_hash=%s",
653                         a->scrape, strchr(a->scrape,'?')?'&':'?',
654                         tracker->escaped ); 
655
656    req = tr_new0( struct tr_tracker_request, 1 );
657    req->session = session;
658    req->reqtype = TR_REQ_SCRAPE;
659    req->url = tr_strdup( EVBUFFER_DATA( url ) );
660    req->done_func = onScrapeResponse;
661    memcpy( req->torrent_hash, tracker->hash, SHA_DIGEST_LENGTH );
662
663    evbuffer_free( url );
664    return req;
665}
666
667struct tr_tracker_handle
668{
669    int runningCount;
670    tr_timer * pulseTimer;
671};
672
673static int trackerPulse( void * vsession );
674
675static void
676ensureGlobalsExist( tr_session * session )
677{
678    if( session->tracker == NULL )
679    {
680        session->tracker = tr_new0( struct tr_tracker_handle, 1 );
681        session->tracker->pulseTimer = tr_timerNew( session, trackerPulse, session, PULSE_INTERVAL_MSEC );
682        dbgmsg( NULL, "creating tracker timer" );
683    }
684}
685
686void
687tr_trackerSessionClose( tr_session * session )
688{
689    if( session && session->tracker )
690    {
691        dbgmsg( NULL, "freeing tracker timer" );
692        tr_timerFree( &session->tracker->pulseTimer );
693        tr_free( session->tracker );
694        session->tracker = NULL;
695    }
696}
697
698/***
699****
700***/
701
702static void
703invokeRequest( void * vreq )
704{
705    struct tr_tracker_request * req = vreq;
706    tr_tracker * t = findTracker( req->session, req->torrent_hash );
707
708    if( t )
709    {
710        const time_t now = time( NULL );
711
712        if( req->reqtype == TR_REQ_SCRAPE )
713        {
714            t->lastScrapeTime = now;
715            t->scrapeAt = 1;
716        }
717        else
718        {
719            t->lastAnnounceTime = now;
720            t->reannounceAt = 1;
721            t->manualAnnounceAllowedAt = 1;
722        }
723    }
724
725    ++req->session->tracker->runningCount;
726
727    tr_webRun( req->session, req->url, NULL, req->done_func,
728               tr_memdup( req->torrent_hash, SHA_DIGEST_LENGTH ) );
729
730    freeRequest( req );
731}
732
733static void
734enqueueScrape( tr_session * session, tr_tracker * tracker )
735{
736    struct tr_tracker_request * req;
737    req = createScrape( session, tracker );
738    tr_runInEventThread( session, invokeRequest, req );
739}
740
741static void
742enqueueRequest( tr_session * session, tr_tracker * tracker, int reqtype )
743{
744    struct tr_tracker_request * req;
745    req = createRequest( session, tracker, reqtype );
746    tr_runInEventThread( session, invokeRequest, req );
747}
748
749static int
750trackerPulse( void * vsession )
751{
752    tr_session * session = vsession;
753    struct tr_tracker_handle * th = session->tracker;
754    tr_torrent * tor;
755    const time_t now = time( NULL );
756
757    if( !session->tracker )
758        return FALSE;
759
760    if( th->runningCount )
761        dbgmsg( NULL, "tracker pulse... %d running", th->runningCount );
762
763    /* upkeep: queue periodic rescrape / reannounce */
764    for( tor=session->torrentList; tor; tor=tor->next )
765    {
766        tr_tracker * t = tor->tracker;
767
768        if( ( t->scrapeAt > 1 ) &&
769            ( t->scrapeAt <= now ) &&
770            ( trackerSupportsScrape( t, tor ) ) )
771        {
772            t->scrapeAt = 1;
773            enqueueScrape( session, t );
774        }
775
776        if( ( t->reannounceAt > 1 ) && 
777            ( t->reannounceAt <= now ) &&
778            ( t->isRunning ) )
779        {
780            t->reannounceAt = 1;
781            t->manualAnnounceAllowedAt = 1;
782            enqueueRequest( session, t, TR_REQ_REANNOUNCE );
783        }
784    }
785
786    if( th->runningCount )
787        dbgmsg( NULL, "tracker pulse after upkeep... %d running", th->runningCount );
788
789    /* free the tracker manager if no torrents are left */
790    if(    ( session->tracker )
791        && ( session->tracker->runningCount < 1 )
792        && ( session->torrentList == NULL ) )
793    {
794        tr_trackerSessionClose( session );
795    }
796
797    /* if there are still running torrents (as indicated by
798     * the existence of the tracker manager) then keep the
799     * trackerPulse() timer alive */
800    return session->tracker != NULL;
801}
802
803static void
804onReqDone( tr_session * session )
805{
806    if( session->tracker )
807    {
808        --session->tracker->runningCount;
809        dbgmsg( NULL, "decrementing runningCount to %d", session->tracker->runningCount );
810        trackerPulse( session );
811    }
812}
813
814/***
815****  LIFE CYCLE
816***/
817
818static void
819generateKeyParam( char * msg, int len )
820{
821    int i;
822    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
823    const int poolSize = strlen( pool );
824    for( i=0; i<len; ++i )
825        *msg++ = pool[tr_cryptoRandInt(poolSize)];
826    *msg = '\0';
827}
828
829static int
830is_rfc2396_alnum( char ch )
831{
832    return     ( '0' <= ch && ch <= '9' )
833            || ( 'A' <= ch && ch <= 'Z' )
834            || ( 'a' <= ch && ch <= 'z' );
835}
836
837static void
838escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
839{
840    const uint8_t *end = in + in_len;
841    while( in != end )
842        if( is_rfc2396_alnum(*in) )
843            *out++ = (char) *in++;
844        else 
845            out += tr_snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
846    *out = '\0';
847}
848
849tr_tracker *
850tr_trackerNew( const tr_torrent * torrent )
851{
852    const tr_info * info = &torrent->info;
853    tr_tracker * t;
854
855    ensureGlobalsExist( torrent->handle );
856
857    t = tr_new0( tr_tracker, 1 );
858    t->publisher = tr_publisherNew( );
859    t->session                  = torrent->handle;
860    t->scrapeIntervalSec        = DEFAULT_SCRAPE_INTERVAL_SEC;
861    t->retryScrapeIntervalSec   = FIRST_SCRAPE_RETRY_INTERVAL_SEC;
862    t->retryAnnounceIntervalSec = FIRST_ANNOUNCE_RETRY_INTERVAL_SEC;
863    t->announceIntervalSec      = DEFAULT_ANNOUNCE_INTERVAL_SEC;
864    t->announceMinIntervalSec   = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
865    t->timesDownloaded          = -1;
866    t->seederCount              = -1;
867    t->leecherCount             = -1;
868    t->lastAnnounceResponse     = -1;
869    t->lastScrapeResponse       = -1;
870    t->manualAnnounceAllowedAt  = ~(time_t)0;
871    t->name = tr_strdup( info->name );
872    t->randOffset = tr_cryptoRandInt( 30 );
873    memcpy( t->hash, info->hash, SHA_DIGEST_LENGTH );
874    escape( t->escaped, info->hash, SHA_DIGEST_LENGTH );
875    generateKeyParam( t->key_param, KEYLEN );
876
877    t->trackerIndex = 0;
878
879    if( trackerSupportsScrape( t, torrent ) )
880        t->scrapeAt = time( NULL ) + t->randOffset;
881
882    return t;
883}
884
885static void
886onTrackerFreeNow( void * vt )
887{
888    tr_tracker * t = vt;
889
890    tr_publisherFree( &t->publisher );
891    tr_free( t->name );
892    tr_free( t->trackerID );
893    tr_free( t->peer_id );
894
895    tr_free( t );
896}
897
898/***
899****  PUBLIC
900***/
901
902void
903tr_trackerFree( tr_tracker * t )
904{
905    if( t )
906        tr_runInEventThread( t->session, onTrackerFreeNow, t );
907}
908
909tr_publisher_tag
910tr_trackerSubscribe( tr_tracker          * t,
911                     tr_delivery_func      func,
912                     void                * user_data )
913{
914    return tr_publisherSubscribe( t->publisher, func, user_data );
915}
916
917void
918tr_trackerUnsubscribe( tr_tracker        * t,
919                       tr_publisher_tag    tag )
920{
921    if( t )
922        tr_publisherUnsubscribe( t->publisher, tag );
923}
924
925const tr_tracker_info *
926tr_trackerGetAddress( tr_tracker   * t )
927{
928    return getCurrentAddress( t );
929}
930
931time_t
932tr_trackerGetManualAnnounceTime( const struct tr_tracker * t )
933{
934    return t->isRunning ? t->manualAnnounceAllowedAt : 0;
935}
936
937int
938tr_trackerCanManualAnnounce ( const tr_tracker * t)
939{
940    const time_t allow = tr_trackerGetManualAnnounceTime( t );
941    return allow && ( allow <= time( NULL ) );
942}
943
944void
945tr_trackerGetCounts( const tr_tracker  * t,
946                     int               * setme_completedCount,
947                     int               * setme_leecherCount,
948                     int               * setme_seederCount )
949{
950    if( setme_completedCount )
951       *setme_completedCount = t->timesDownloaded;
952
953    if( setme_leecherCount )
954       *setme_leecherCount = t->leecherCount;
955
956    if( setme_seederCount )
957       *setme_seederCount = t->seederCount;
958}
959
960
961void
962tr_trackerStart( tr_tracker * t )
963{
964    if( t && !t->isRunning )
965    {
966        tr_free( t->peer_id );
967        t->peer_id = tr_peerIdNew( );
968
969        t->isRunning = 1;
970        enqueueRequest( t->session, t, TR_REQ_STARTED );
971    }
972}
973
974void
975tr_trackerReannounce( tr_tracker * t )
976{
977    enqueueRequest( t->session, t, TR_REQ_REANNOUNCE );
978}
979
980void
981tr_trackerCompleted( tr_tracker * t )
982{
983    enqueueRequest( t->session, t, TR_REQ_COMPLETED );
984}
985
986void
987tr_trackerStop( tr_tracker * t )
988{
989    if( t && t->isRunning ) {
990        t->isRunning = 0;
991        t->reannounceAt = t->manualAnnounceAllowedAt = 0;
992        enqueueRequest( t->session, t, TR_REQ_STOPPED );
993    }
994}
995
996void
997tr_trackerChangeMyPort( tr_tracker * t )
998{
999    if( t->isRunning )
1000        tr_trackerReannounce( t );
1001}
1002
1003void
1004tr_trackerStat( const tr_tracker * t,
1005                struct tr_stat   * setme)
1006{
1007    assert( t );
1008    assert( setme );
1009
1010    setme->lastScrapeTime = t->lastScrapeTime;
1011    setme->nextScrapeTime = t->scrapeAt;
1012    setme->lastAnnounceTime = t->lastAnnounceTime;
1013    setme->nextAnnounceTime = t->reannounceAt;
1014    setme->manualAnnounceTime = t->manualAnnounceAllowedAt;
1015
1016    if( t->lastScrapeResponse == -1 ) /* never been scraped */
1017        *setme->scrapeResponse = '\0';
1018    else
1019        tr_snprintf( setme->scrapeResponse,
1020                     sizeof( setme->scrapeResponse ),
1021                     "%s (%ld)",
1022                     tr_webGetResponseStr( t->lastScrapeResponse ),
1023                     t->lastScrapeResponse );
1024
1025    if( t->lastAnnounceResponse == -1 ) /* never been announced */
1026        *setme->announceResponse = '\0';
1027    else
1028        tr_snprintf( setme->announceResponse,
1029                     sizeof( setme->announceResponse ),
1030                     "%s (%ld)",
1031                     tr_webGetResponseStr( t->lastAnnounceResponse ),
1032                     t->lastAnnounceResponse );
1033}
Note: See TracBrowser for help on using the repository browser.