source: trunk/libtransmission/tracker.c @ 2555

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

add portability wrapper for in_port_t...

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