source: trunk/libtransmission/tracker.c @ 2588

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

fix manual-announce but noted by BMW

  • Property svn:keywords set to Date Rev Author Id
File size: 30.4 KB
Line 
1/******************************************************************************
2 * $Id: tracker.c 2588 2007-08-02 02:38:42Z charles $
3 *
4 * Copyright (c) 2005-2006 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <assert.h>
26#include <stdio.h>
27#include <stdlib.h>
28
29#include <sys/types.h>
30
31#include "transmission.h"
32#include "bencode.h"
33#include "bsdqueue.h"
34#include "completion.h"
35#include "http.h"
36#include "net.h"
37#include "shared.h"
38#include "tracker.h"
39#include "utils.h"
40
41/* Users aren't allowed to make a manual announce more often than this. */
42static const int MANUAL_ANNOUNCE_INTERVAL_MSEC = (60*1000);
43
44struct tclist
45{
46    tr_tracker_info_t   * tl_inf;
47    int                   tl_badscrape;
48    SLIST_ENTRY( tclist ) next;
49};
50SLIST_HEAD( tchead, tclist );
51
52typedef uint8_t tr_flag_t;
53
54struct tr_tracker_s
55{
56    tr_torrent_t * tor;
57
58    const char   * peer_id;
59    char         * trackerid;
60
61    struct tchead * tiers;
62    size_t          tierCount;
63    size_t          tierIndex;
64    struct tclist * tcCur;
65
66#define TC_CHANGE_NO        0
67#define TC_CHANGE_NEXT      1
68#define TC_CHANGE_NONEXT    2
69#define TC_CHANGE_REDIRECT  4
70    uint8_t        shouldChangeAnnounce;
71   
72    char         * redirectAddress;
73    int            redirectAddressLen;
74    char         * redirectScrapeAddress;
75    int            redirectScrapeAddressLen;
76
77    tr_flag_t      started;
78    tr_flag_t      completed;
79    tr_flag_t      stopped;
80    tr_flag_t      forceAnnounce;
81
82    int            interval;
83    int            minInterval;
84    int            scrapeInterval;
85    int            seeders;
86    int            leechers;
87    int            complete;
88    int            randOffset;
89
90    tr_flag_t      hasManyPeers;
91    tr_flag_t      completelyUnconnectable;
92    tr_flag_t      allUnreachIfError;
93    tr_flag_t      lastError;
94
95    uint64_t       dateTry;
96    uint64_t       dateOk;
97    uint64_t       dateScrape;
98    tr_flag_t      lastScrapeFailed;
99    tr_flag_t      scrapeNeeded;
100
101    tr_http_t    * http;
102    tr_http_t    * httpScrape;
103
104    int            publicPort;
105};
106
107static void        setAnnounce      ( tr_tracker_t * tc, struct tclist * new );
108static void        failureAnnouncing( tr_tracker_t * tc );
109static tr_http_t * getQuery         ( tr_tracker_t * tc );
110static tr_http_t * getScrapeQuery   ( const tr_tracker_t * tc );
111static void        readAnswer       ( tr_tracker_t * tc, const char *, int,
112                                      int * peerCount, uint8_t ** peerCompact );
113static void        readScrapeAnswer ( tr_tracker_t * tc, const char *, int );
114static void        killHttp         ( tr_http_t ** http );
115static int         shouldChangePort( tr_tracker_t * tc );
116static uint8_t *   parseOriginalPeers( benc_val_t * bePeers, int * peerCount );
117
118tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
119{
120    tr_info_t * inf = &tor->info;
121
122    tr_tracker_t * tc;
123    struct tclist * item;
124    size_t ii, jj;
125
126    tc = calloc( 1, sizeof *tc );
127    if( NULL == tc )
128    {
129        return NULL;
130    }
131
132    tc->tor            = tor;
133    tc->peer_id        = tor->peer_id;
134
135    tc->started        = 1;
136   
137    tc->shouldChangeAnnounce = TC_CHANGE_NO;
138    tc->redirectAddress = NULL;
139
140    tc->interval       = 300;
141    tc->scrapeInterval = 1200;
142
143    tc->lastError      = 0;
144    tc->allUnreachIfError = 1;
145
146    tc->publicPort     = tor->publicPort;
147
148    assert( 0 <= inf->trackerTiers );
149    assert( sizeof( struct tchead ) == sizeof *tc->tiers );
150    tc->tiers          = calloc( inf->trackerTiers, sizeof *tc->tiers );
151    tc->tierCount      = inf->trackerTiers;
152    for( ii = 0; tc->tierCount > ii; ii++ )
153    {
154        assert( 0 <= inf->trackerList[ii].count );
155        SLIST_INIT( &tc->tiers[ii] );
156        for( jj = inf->trackerList[ii].count; 0 < jj; jj-- )
157        {
158            item = calloc( 1, sizeof *item );
159            if( NULL == item )
160            {
161                tr_trackerClose( tc );
162                return NULL;
163            }
164            item->tl_inf = &inf->trackerList[ii].list[jj-1];
165            SLIST_INSERT_HEAD( &tc->tiers[ii], item, next );
166        }
167    }
168
169    setAnnounce( tc, SLIST_FIRST( &tc->tiers[0] ) );
170
171    return tc;
172}
173
174static void setAnnounce( tr_tracker_t * tc, struct tclist * new )
175{
176    tc->tcCur = new;
177
178    /* Needs a new scrape */
179    tc->seeders = -1;
180    tc->leechers = -1;
181    tc->complete = -1;
182    tc->dateScrape = 0;
183}
184
185static void failureAnnouncing( tr_tracker_t * tc )
186{
187    if( NULL != SLIST_NEXT( tc->tcCur, next ) ||
188        tc->tierIndex + 1 < tc->tierCount )
189    {
190        tc->shouldChangeAnnounce = TC_CHANGE_NEXT;
191    }
192    else
193    {
194        tc->shouldChangeAnnounce = TC_CHANGE_NONEXT;
195        tc->completelyUnconnectable = 1;
196    }
197}
198
199int
200tr_trackerCanManualAnnounce( const tr_tracker_t * tc )
201{
202    return tc
203        && !tc->forceAnnounce
204        && ((tc->dateOk + MANUAL_ANNOUNCE_INTERVAL_MSEC) < tr_date());
205}
206
207static int shouldConnect( tr_tracker_t * tc )
208{
209    tr_torrent_t * tor = tc->tor;
210    const uint64_t now = tr_date();
211
212    /* User has requested a manual announce
213       and it's been long enough since the last one */
214    if( tc->forceAnnounce && now > (tc->dateOk+MANUAL_ANNOUNCE_INTERVAL_MSEC) )
215        return 1;
216
217    /* Last tracker failed, try next */
218    if( tc->shouldChangeAnnounce == TC_CHANGE_NEXT
219        || tc->shouldChangeAnnounce == TC_CHANGE_REDIRECT )
220    {
221        return 1;
222    }
223   
224    /* If last attempt was an error and it did not change trackers,
225       then all must have been errors */
226    if( tc->lastError )
227    {
228        /* Unreachable trackers, wait 10 seconds + random value before
229           trying again */
230        if( tc->allUnreachIfError )
231        {
232            if( now < tc->dateTry + tc->randOffset + 10000 )
233            {
234                return 0;
235            }
236        }
237        /* The tracker rejected us (like 4XX code, unauthorized
238            IP...), don't hammer it - we'll probably get the same
239            answer next time anyway */
240        else
241        {
242            if( now < tc->dateTry + 1000 * tc->interval + tc->randOffset )
243            {
244                return 0;
245            }
246            else
247            {
248                /* since starting at the top of the list, reset if any
249                   were reached previously */
250                tc->allUnreachIfError = 1;
251            }
252        }
253    }
254
255    /* Do we need to send an event? */
256    if( tc->started || tc->completed || tc->stopped || shouldChangePort( tc ) )
257    {
258        return 1;
259    }
260
261    /* Should we try and get more peers? */
262    if( now > tc->dateOk + 1000 * tc->interval + tc->randOffset )
263    {
264        return 1;
265    }
266
267    /* If there is quite a lot of people on this torrent, stress
268       the tracker a bit until we get a decent number of peers */
269    if( tc->hasManyPeers &&
270        (tr_cpGetStatus ( tor->completion ) == TR_CP_INCOMPLETE ))
271    {
272        /* reannounce in 10 seconds if we have less than 5 peers */
273        if( tor->peerCount < 5 )
274        {
275            if( now > tc->dateOk + 1000 * MAX( 10, tc->minInterval ) )
276            {
277                return 1;
278            }
279        }
280        /* reannounce in 20 seconds if we have less than 10 peers */
281        else if( tor->peerCount < 10 )
282        {
283            if( now > tc->dateOk + 1000 * MAX( 20, tc->minInterval ) )
284            {
285                return 1;
286            }
287        }
288        /* reannounce in 30 seconds if we have less than 15 peers */
289        else if( tor->peerCount < 15 )
290        {
291            if( now > tc->dateOk + 1000 * MAX( 30, tc->minInterval ) )
292            {
293                return 1;
294            }
295        }
296    }
297
298    return 0;
299}
300
301static int shouldScrape( const tr_tracker_t * tc )
302{
303    uint64_t now, interval;
304
305    /* in process of changing tracker or scrape not supported */
306    if( tc->shouldChangeAnnounce != TC_CHANGE_NO ||
307        NULL == tc->tcCur->tl_inf->scrape || tc->tcCur->tl_badscrape ||
308        tc->stopped )
309    {
310        return 0;
311    }
312
313    now      = tr_date();
314    interval = 1000 * MAX( tc->scrapeInterval, 600 );
315
316    /* scrape more often if needed */
317    if( tc->scrapeNeeded || tc->lastScrapeFailed )
318    {
319        interval /= 2;
320    }
321
322    return now > tc->dateScrape + interval;
323}
324
325void
326tr_trackerManualAnnounce( tr_tracker_t * tc )
327{
328    if( tc != NULL )
329        tc->forceAnnounce = 1;
330}
331
332void
333tr_trackerPulse( tr_tracker_t    * tc,
334                 int             * peerCount,
335                 uint8_t        ** peerCompact )
336{
337    const char   * data;
338    char         * address, * announce;
339    int            len, port;
340    struct tclist * next;
341    struct tchead * tier;
342
343    if( tc == NULL )
344        return;
345
346    *peerCount = 0;
347    *peerCompact = NULL;
348   
349    if( !tc->http && shouldConnect( tc ) )
350    {
351        tc->completelyUnconnectable = FALSE;
352       
353        tc->randOffset = tr_rand( 60000 );
354       
355        tc->dateTry = tr_date();
356       
357        /* Use redirected address */
358        if( tc->shouldChangeAnnounce == TC_CHANGE_REDIRECT )
359        {
360            if( !tr_httpParseUrl( tc->redirectAddress, tc->redirectAddressLen,
361                                     &address, &port, &announce ) )
362            {
363                tr_err( "Tracker: redirected URL: %s:%d", address, port );
364                tc->http = tr_httpClient( TR_HTTP_GET, address, port,
365                                          "%s", announce );
366               
367                free( address );
368                free( announce );
369            }
370           
371            free( tc->redirectAddress );
372            tc->redirectAddress = NULL;
373        }
374        else
375        {
376            /* Need to change to next address in list */
377            if( tc->shouldChangeAnnounce == TC_CHANGE_NEXT )
378            {
379                tr_inf( "Tracker: failed to connect to %s:%i, trying next",
380                        tc->tcCur->tl_inf->address, tc->tcCur->tl_inf->port );
381                next = SLIST_NEXT( tc->tcCur, next );
382                if( NULL == next )
383                {
384                    assert( tc->tierCount > tc->tierIndex + 1 );
385                    tc->tierIndex++;
386                    next = SLIST_FIRST( &tc->tiers[tc->tierIndex] );
387                    /* XXX will there always be at least one tracker
388                       in a tier? */
389                }
390               
391                tr_inf( "Tracker: switching to tracker http://%s:%i%s",
392                        next->tl_inf->address, next->tl_inf->port,
393                        next->tl_inf->announce );
394                setAnnounce( tc, next );
395            }
396            /* Need to change to first in list */
397            else if( SLIST_FIRST( &tc->tiers[0] ) != tc->tcCur )
398            {
399                tier = &tc->tiers[tc->tierIndex];
400                /* Check if the last announce was successful and
401                   wasn't the first in the sublist */
402                if( tc->shouldChangeAnnounce == TC_CHANGE_NO &&
403                    SLIST_FIRST( tier ) != tc->tcCur )
404                {
405                    SLIST_REMOVE( tier, tc->tcCur, tclist, next );
406                    SLIST_INSERT_HEAD( tier, tc->tcCur, next );
407                }
408               
409                setAnnounce( tc, SLIST_FIRST( tier ) );
410            }
411
412            tc->http = getQuery( tc );
413
414            tr_inf( "Tracker: connecting to %s:%d (%s)",
415                    tc->tcCur->tl_inf->address, tc->tcCur->tl_inf->port,
416                    tc->started ? "sending 'started'" :
417                    ( tc->completed ? "sending 'completed'" :
418                      ( tc->stopped ? "sending 'stopped'" :
419                        ( shouldChangePort( tc ) ?
420                          "sending 'stopped' to change port" :
421                          "getting peers" ) ) ) );
422        }
423       
424        tc->shouldChangeAnnounce = TC_CHANGE_NO;
425    }
426
427    if( tc->http )
428    {
429        switch( tr_httpPulse( tc->http, &data, &len ) )
430        {
431            case TR_NET_WAIT:
432                break;
433
434            case TR_NET_ERROR:
435                killHttp( &tc->http );
436                tc->dateTry = tr_date();
437               
438                failureAnnouncing( tc );
439               
440                tc->lastError = 1;
441                break;
442
443            case TR_NET_OK:
444                readAnswer( tc, data, len, peerCount, peerCompact );
445                killHttp( &tc->http );
446                break;
447        }
448    }
449   
450    if( ( NULL == tc->httpScrape ) && shouldScrape( tc ) )
451    {
452        tc->dateScrape = tr_date();
453       
454        if ( tc->redirectScrapeAddress != NULL )
455        {
456            /* Use redirected address */
457            if( !tr_httpParseUrl( tc->redirectScrapeAddress,
458                                  tc->redirectScrapeAddressLen,
459                                  &address, &port, &announce ) )
460            {
461                tr_err( "Scrape: redirected URL: %s:%d", address, port );
462                tc->httpScrape = tr_httpClient( TR_HTTP_GET, address, port,
463                                                "%s", announce );
464               
465                free( address );
466                free( announce );
467            }
468           
469            free( tc->redirectScrapeAddress );
470            tc->redirectScrapeAddress = NULL;
471        }
472        else
473        {
474            tc->httpScrape = getScrapeQuery( tc );
475            tr_inf( "Scrape: sent HTTP request for http://%s:%d%s",
476                    tc->tcCur->tl_inf->address,
477                    tc->tcCur->tl_inf->port,
478                    tc->tcCur->tl_inf->scrape );
479        }
480    }
481
482    if( NULL != tc->httpScrape )
483    {
484        switch( tr_httpPulse( tc->httpScrape, &data, &len ) )
485        {
486            case TR_NET_WAIT:
487                break;
488
489            case TR_NET_ERROR:
490                killHttp( &tc->httpScrape );
491                tc->lastScrapeFailed = 1;
492                break;
493
494            case TR_NET_OK:
495                readScrapeAnswer( tc, data, len );
496                killHttp( &tc->httpScrape );
497                break;
498        }
499    }
500
501    return;
502}
503
504void tr_trackerCompleted( tr_tracker_t * tc )
505{
506    tc->started   = 0;
507    tc->completed = 1;
508    tc->stopped   = 0;
509}
510
511void tr_trackerStopped( tr_tracker_t * tc )
512{
513    if( tc == NULL )
514        return;
515
516    /* If we are already sending a query at the moment, we need to
517       reconnect */
518    killHttp( &tc->http );
519
520    tc->started   = 0;
521    tc->completed = 0;
522    tc->stopped   = 1;
523
524    /* Even if we have connected recently, reconnect right now */
525    tc->dateTry = 0;
526}
527
528void tr_trackerClose( tr_tracker_t * tc )
529{
530    size_t          ii;
531    struct tclist * dead;
532
533    if( tc == NULL )
534        return;
535
536    killHttp( &tc->http );
537    killHttp( &tc->httpScrape );
538
539    for( ii = 0; tc->tierCount > ii; ii++ )
540    {
541        while( !SLIST_EMPTY( &tc->tiers[ii] ) )
542        {
543            dead = SLIST_FIRST( &tc->tiers[ii] );
544            SLIST_REMOVE_HEAD( &tc->tiers[ii], next );
545            free( dead );
546        }
547    }
548    free( tc->tiers );
549
550    free( tc->trackerid );
551    free( tc );
552}
553
554static tr_http_t * getQuery( tr_tracker_t * tc )
555{
556    tr_torrent_t * tor = tc->tor;
557    const tr_tracker_info_t * tcInf = tc->tcCur->tl_inf;
558
559    const char   * trackerid;
560    const char   * event;
561    const char   * idparam;
562    uint64_t       left;
563    char           start;
564    int            numwant = 50;
565
566    if( tc->started )
567    {
568        event = "&event=started";
569       
570        tr_torrentResetTransferStats( tor );
571
572        if( shouldChangePort( tc ) )
573        {
574            tc->publicPort = tor->publicPort;
575        }
576    }
577    else if( tc->completed )
578    {
579        event = "&event=completed";
580    }
581    else if( tc->stopped || shouldChangePort( tc ) )
582    {
583        event = "&event=stopped";
584        numwant = 0;
585    }
586    else
587    {
588        event = "";
589    }
590
591    if( NULL == tc->trackerid )
592    {
593        trackerid = "";
594        idparam   = "";
595    }
596    else
597    {
598        trackerid = tc->trackerid;
599        idparam   = "&trackerid=";
600    }
601
602    start = ( strchr( tcInf->announce, '?' ) ? '&' : '?' );
603    left  = tr_cpLeftUntilComplete( tor->completion );
604
605    return tr_httpClient( TR_HTTP_GET, tcInf->address, tcInf->port,
606                          "%s%c"
607                          "info_hash=%s&"
608                          "peer_id=%s&"
609                          "port=%d&"
610                          "uploaded=%"PRIu64"&"
611                          "downloaded=%"PRIu64"&"
612                          "left=%"PRIu64"&"
613                          "compact=1&"
614                          "numwant=%d&"
615                          "key=%s"
616                          "%s%s"
617                          "%s",
618                          tcInf->announce, start, tor->escapedHashString,
619                          tc->peer_id, tc->publicPort, tor->uploadedCur, tor->downloadedCur,
620                          left, numwant, tor->key, idparam, trackerid, event );
621}
622
623static tr_http_t * getScrapeQuery( const tr_tracker_t * tc )
624{
625    const tr_torrent_t * tor = tc->tor;
626    const tr_tracker_info_t * tcInf = tc->tcCur->tl_inf;
627    const char start = ( strchr( tcInf->scrape, '?' ) ? '&' : '?' );
628    return tr_httpClient( TR_HTTP_GET, tcInf->address, tcInf->port,
629                          "%s%c"
630                          "info_hash=%s",
631                          tcInf->scrape, start, tor->escapedHashString );
632}
633
634static void readAnswer( tr_tracker_t * tc, const char * data, int len,
635                        int * _peerCount, uint8_t ** _peerCompact )
636{
637    tr_torrent_t * tor = tc->tor;
638    int i;
639    int code;
640    benc_val_t   beAll;
641    benc_val_t * bePeers, * beFoo;
642    const uint8_t * body;
643    int bodylen, shouldfree, scrapeNeeded;
644    char * address;
645    int peerCount;
646    uint8_t * peerCompact;
647
648    *_peerCount = peerCount = 0;
649    *_peerCompact = peerCompact = NULL;
650
651    tc->dateTry = tr_date();
652    code = tr_httpResponseCode( data, len );
653   
654    if( 0 > code )
655    {
656        /* We don't have a valid HTTP status line */
657        tr_inf( "Tracker: invalid HTTP status line" );
658        tc->lastError = 1;
659        failureAnnouncing( tc );
660        return;
661    }
662   
663    if( code == 301 || code == 302 )
664    {
665        tr_http_header_t hdr[] = { { "Location", NULL, 0 }, { NULL, NULL, 0 } };
666
667        tr_err( "Tracker: HTTP status code: %i", code );
668       
669        tr_httpParse( data, len, hdr );
670       
671        address = calloc( hdr->len+1, sizeof( char ) );
672        snprintf( address, hdr->len+1, "%s", hdr->data );
673       
674        tc->shouldChangeAnnounce = TC_CHANGE_REDIRECT;
675        tc->redirectAddress = address;
676        tc->redirectAddressLen = hdr->len;
677       
678        return;
679    }
680
681    if( !TR_HTTP_STATUS_OK( code ) )
682    {
683        /* we didn't get a 2xx status code */
684        tr_err( "Tracker: invalid HTTP status code: %i", code );
685        tc->lastError = 1;
686        tc->allUnreachIfError = 0;
687        failureAnnouncing( tc );
688        return;
689    }
690
691    /* find the end of the http headers */
692    body = (uint8_t *) tr_httpParse( data, len, NULL );
693    if( NULL == body )
694    {
695        tr_err( "Tracker: could not find end of HTTP headers" );
696        tc->lastError = 1;
697        failureAnnouncing( tc );
698        return;
699    }
700    bodylen = len - ( body - (const uint8_t*)data );
701
702    /* Find and load the dictionary */
703    shouldfree = 0;
704    for( i = 0; i < bodylen; i++ )
705    {
706        if( !tr_bencLoad( &body[i], bodylen - i, &beAll, NULL ) )
707        {
708            shouldfree = 1;
709            break;
710        }
711    }
712
713    if( i >= bodylen )
714    {
715        if( tc->stopped || shouldChangePort( tc ) )
716        {
717            tc->lastError = 0;
718            goto nodict;
719        }
720        tr_err( "Tracker: no valid dictionary found in answer" );
721        tc->lastError = 1;
722        tc->allUnreachIfError = 0;
723        failureAnnouncing( tc );
724        return;
725    }
726
727    /* tr_bencPrint( &beAll ); */
728
729    if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) )
730    {
731        tr_err( "Tracker: Error - %s", bePeers->val.s.s );
732        tor->error = TR_ERROR_TC_ERROR;
733        snprintf( tor->errorString, sizeof( tor->errorString ),
734                  "%s", bePeers->val.s.s );
735        tc->lastError = 1;
736        tc->allUnreachIfError = 0;
737        failureAnnouncing( tc );
738        goto cleanup;
739    }
740    else if( ( bePeers = tr_bencDictFind( &beAll, "warning message" ) ) )
741    {
742        tr_err( "Tracker: Warning - %s", bePeers->val.s.s );
743        tor->error = TR_ERROR_TC_WARNING;
744        snprintf( tor->errorString, sizeof( tor->errorString ),
745                  "%s", bePeers->val.s.s );
746    }
747    else if( tor->error & TR_ERROR_TC_MASK )
748    {
749        tor->error = TR_OK;
750    }
751
752    tc->lastError = 0;
753    tc->allUnreachIfError = 0;
754
755    /* Get the tracker interval */
756    beFoo = tr_bencDictFind( &beAll, "interval" );
757    if( !beFoo || TYPE_INT != beFoo->type )
758    {
759        tr_err( "Tracker: no 'interval' field" );
760        goto cleanup;
761    }
762
763    tc->interval = beFoo->val.i;
764    tr_inf( "Tracker: interval = %d seconds", tc->interval );
765    tc->interval = MAX( 10, tc->interval );
766
767    /* Get the tracker minimum interval */
768    beFoo = tr_bencDictFind( &beAll, "min interval" );
769    if( beFoo && TYPE_INT == beFoo->type )
770    {
771        tc->minInterval = beFoo->val.i;
772        tr_inf( "Tracker: min interval = %d seconds", tc->minInterval );
773        tc->minInterval = MAX( 10, tc->minInterval );
774
775        if( tc->interval < tc->minInterval )
776        {
777            tc->interval = tc->minInterval;
778            tr_inf( "Tracker: 'interval' less than 'min interval', "
779                    "using 'min interval'" );
780        }
781    }
782    else
783    {
784        tc->minInterval = 0;
785        tr_inf( "Tracker: no 'min interval' field" );
786    }
787
788    scrapeNeeded = 0;
789    beFoo = tr_bencDictFind( &beAll, "complete" );
790    if( beFoo && TYPE_INT == beFoo->type )
791    {
792        tc->seeders = beFoo->val.i;
793    }
794    else
795    {
796        scrapeNeeded = 1;
797    }
798
799    beFoo = tr_bencDictFind( &beAll, "incomplete" );
800    if( beFoo && TYPE_INT == beFoo->type )
801    {
802        tc->leechers = beFoo->val.i;
803    }
804    else
805    {
806        scrapeNeeded = 1;
807    }
808
809    tc->scrapeNeeded = scrapeNeeded;
810    if( !scrapeNeeded )
811    {
812        tc->hasManyPeers = ( tc->seeders + tc->leechers >= 50 );
813    }
814
815    beFoo = tr_bencDictFind( &beAll, "tracker id" );
816    if( beFoo )
817    {
818        free( tc->trackerid );
819        tc->trackerid = strdup( beFoo->val.s.s );
820        tr_inf( "Tracker: tracker id = %s", tc->trackerid );
821    }
822
823    bePeers = tr_bencDictFind( &beAll, "peers" );
824    if( !bePeers )
825    {
826        if( tc->stopped || shouldChangePort( tc ) )
827        {
828            goto nodict;
829        }
830        tr_err( "Tracker: no \"peers\" field" );
831        failureAnnouncing( tc );
832        goto cleanup;
833    }
834
835    if( TYPE_LIST == bePeers->type )
836    {
837        /* Original protocol */
838        if( bePeers->val.l.count > 0 )
839        {
840            peerCompact = parseOriginalPeers( bePeers, &peerCount );
841        }
842    }
843    else if( TYPE_STR == bePeers->type )
844    {
845        /* "Compact" extension */
846        if( bePeers->val.s.i >= 6 )
847        {
848            peerCount = bePeers->val.s.i / 6;
849            peerCompact = malloc( bePeers->val.s.i );
850            memcpy( peerCompact, bePeers->val.s.s, bePeers->val.s.i );
851        }
852    }
853
854    if( peerCount > 0 )
855    {
856        tr_inf( "Tracker: got %d peers", peerCount );
857        if( peerCount >= 50 )
858        {
859            tc->hasManyPeers = 1;
860        }
861        *_peerCount = peerCount;
862        *_peerCompact = peerCompact;
863    }
864
865nodict:
866    /* Success */
867    tc->started       = 0;
868    tc->completed     = 0;
869    tc->dateOk        = tr_date();
870    tc->forceAnnounce = FALSE;
871
872    if( tc->stopped )
873    {
874        tc->stopped = 0;
875    }
876    else if( shouldChangePort( tc ) )
877    {
878        tc->started  = 1;
879    }
880
881cleanup:
882    if( shouldfree )
883    {
884        tr_bencFree( &beAll );
885    }
886}
887
888static void readScrapeAnswer( tr_tracker_t * tc, const char * data, int len )
889{
890    int code;
891    const uint8_t * body;
892    int bodylen, ii;
893    benc_val_t scrape, * val1, * val2;
894    char * address;
895
896    code = tr_httpResponseCode( data, len );
897    if( 0 > code )
898    {
899        /* We don't have a valid HTTP status line */
900        tr_inf( "Scrape: invalid HTTP status line" );
901        tc->lastScrapeFailed = 1;
902        return;
903    }
904   
905    if( code == 301 || code == 302 )
906    {
907        tr_http_header_t hdr[] = { { "Location", NULL, 0 }, { NULL, NULL, 0 } };
908       
909        tr_err( "Scrape: HTTP status code: %i", code );
910
911        tr_httpParse( data, len, hdr );
912       
913        address = calloc( hdr->len+1, sizeof( char ) );
914        snprintf( address, hdr->len+1, "%s", hdr->data );
915       
916        /* Needs a new scrape */
917        tc->dateScrape = 0;
918       
919        tc->redirectScrapeAddress = address;
920        tc->redirectScrapeAddressLen = hdr->len;
921       
922        return;
923    }
924
925    if( !TR_HTTP_STATUS_OK( code ) )
926    {
927        /* we didn't get a 2xx status code */
928        tr_err( "Scrape: invalid HTTP status code: %i", code );
929        if( TR_HTTP_STATUS_FAIL_CLIENT( code ) )
930        {
931            tc->tcCur->tl_badscrape = 1;
932        }
933        tc->lastScrapeFailed = 1;
934        return;
935    }
936
937    /* find the end of the http headers */
938    body = (uint8_t *) tr_httpParse( data, len, NULL );
939    if( NULL == body )
940    {
941        tr_err( "Scrape: could not find end of HTTP headers" );
942        tc->lastScrapeFailed = 1;
943        return;
944    }
945
946    tc->lastScrapeFailed = 0;
947    bodylen = len - ( body - (const uint8_t*)data );
948
949    for( ii = 0; ii < bodylen; ii++ )
950    {
951        if( !tr_bencLoad( body + ii, bodylen - ii, &scrape, NULL ) )
952        {
953            break;
954        }
955    }
956    if( ii >= bodylen )
957    {
958        return;
959    }
960
961    val1 = tr_bencDictFind( &scrape, "files" );
962    if( !val1 || val1->type != TYPE_DICT || val1->val.l.count < 1 )
963    {
964        tr_bencFree( &scrape );
965        return;
966    }
967    val1 = &val1->val.l.vals[1];
968   
969    val2 = tr_bencDictFind( val1, "complete" );
970    if( !val2 )
971    {
972        tr_bencFree( &scrape );
973        return;
974    }
975    tc->seeders = val2->val.i;
976   
977    val2 = tr_bencDictFind( val1, "incomplete" );
978    if( !val2 )
979    {
980        tr_bencFree( &scrape );
981        return;
982    }
983    tc->leechers = val2->val.i;
984   
985    val2 = tr_bencDictFind( val1, "downloaded" );
986    if( !val2 )
987    {
988        tr_bencFree( &scrape );
989        return;
990    }
991    tc->complete = val2->val.i;
992   
993    val2 = tr_bencDictFind( &scrape, "flags" );
994    if( val2 )
995    {
996        val2 = tr_bencDictFind( val2, "min_request_interval" );
997        if( val2 )
998        {
999            tc->scrapeInterval = val2->val.i;
1000            tr_inf( "Scrape: min_request_interval = %d seconds", tc->scrapeInterval );
1001        }
1002    }
1003   
1004    tc->hasManyPeers = ( tc->seeders + tc->leechers >= 50 );
1005   
1006    tr_bencFree( &scrape );
1007}
1008
1009int tr_trackerSeeders( const tr_tracker_t * tc )
1010{
1011    return tc ? tc->seeders : -1;
1012}
1013
1014int tr_trackerLeechers( const tr_tracker_t * tc )
1015{
1016    return tc ? tc->leechers : -1;
1017}
1018
1019int tr_trackerDownloaded( const tr_tracker_t * tc )
1020{
1021    return tc ? tc->complete : -1;
1022}
1023
1024const tr_tracker_info_t * tr_trackerGet( const tr_tracker_t * tc )
1025{
1026    return tc ? tc->tcCur->tl_inf : NULL;
1027}
1028
1029int tr_trackerCannotConnect( const tr_tracker_t * tc )
1030{
1031    return tc && tc->completelyUnconnectable;
1032}
1033
1034uint64_t tr_trackerLastResponseDate ( const tr_tracker_t * tc )
1035{
1036    return tc ? tc->dateOk : 0;
1037}
1038
1039
1040#if 0
1041/* Blocking version */
1042int tr_trackerScrape( tr_torrent_t * tor, int * s, int * l, int * d )
1043{
1044    tr_tracker_t * tc;
1045    tr_http_t    * http;
1046    const char   * data;
1047    int            len;
1048    int            ret;
1049   
1050    tc = tr_trackerInit( tor );
1051
1052    if( NULL == tc->tcCur->tl_inf->scrape || tc->tcCur->tl_badscrape )
1053    {
1054        tr_trackerClose( tc );
1055        return 1;
1056    }
1057
1058    http = getScrapeQuery( tc );
1059
1060    for( data = NULL; !data; tr_wait( 10 ) )
1061    {
1062        switch( tr_httpPulse( http, &data, &len ) )
1063        {
1064            case TR_NET_WAIT:
1065                break;
1066
1067            case TR_NET_ERROR:
1068                goto scrapeDone;
1069
1070            case TR_NET_OK:
1071                readScrapeAnswer( tc, data, len );
1072                goto scrapeDone;
1073        }
1074    }
1075
1076scrapeDone:
1077    tr_httpClose( http );
1078
1079    ret = 1;
1080    if( tc->seeders > -1 && tc->leechers > -1 && tc->complete > -1 )
1081    {
1082        *s = tc->seeders;
1083        *l = tc->leechers;
1084        *d = tc->complete;
1085        ret = 0;
1086    }
1087
1088    tr_trackerClose( tc );
1089    return ret;
1090}
1091#endif
1092
1093static void killHttp( tr_http_t ** http )
1094{
1095    if( NULL != *http )
1096    {
1097        tr_httpClose( *http );
1098        *http = NULL;
1099    }
1100}
1101
1102static int shouldChangePort( tr_tracker_t * tc )
1103{
1104    tr_torrent_t * tor = tc->tor;
1105
1106    return ( tor->publicPort != tc->publicPort );
1107}
1108
1109/* Convert to compact form */
1110static uint8_t *
1111parseOriginalPeers( benc_val_t * bePeers, int * peerCount )
1112{
1113    struct in_addr addr;
1114    tr_port_t      port;
1115    uint8_t      * peerCompact;
1116    benc_val_t   * peer, * addrval, * portval;
1117    int            ii, count;
1118
1119    assert( TYPE_LIST == bePeers->type );
1120
1121    count  = 0;
1122    peerCompact = malloc( 6 * bePeers->val.l.count );
1123    if( NULL == peerCompact )
1124    {
1125        return NULL;
1126    }
1127
1128    for( ii = 0; bePeers->val.l.count > ii; ii++ )
1129    {
1130        peer = &bePeers->val.l.vals[ii];
1131        addrval = tr_bencDictFind( peer, "ip" );
1132        if( NULL == addrval || TYPE_STR != addrval->type ||
1133            tr_netResolve( addrval->val.s.s, &addr ) )
1134        {
1135            continue;
1136        }
1137        memcpy( &peerCompact[6 * count], &addr, 4 );
1138
1139        portval = tr_bencDictFind( peer, "port" );
1140        if( NULL == portval || TYPE_INT != portval->type ||
1141            0 > portval->val.i || 0xffff < portval->val.i )
1142        {
1143            continue;
1144        }
1145        port = htons( portval->val.i );
1146        memcpy( &peerCompact[6 * count + 4], &port, 2 );
1147
1148        count++;
1149    }
1150
1151    *peerCount = count;
1152
1153    return peerCompact;
1154}
Note: See TracBrowser for help on using the repository browser.