source: trunk/libtransmission/tracker.c @ 5911

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

more cleanup work:
(1) kill the pointless "tr_tracker_stat" struct.
(2) kill the unused "tr_torrentRemoveSaved()" function.
(3) kill the redundant "nextManualAnnounceTime" variable.
(4) make the TR_ERROR_IS_* macros private.

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