source: trunk/libtransmission/tracker.c @ 2544

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

this looks bug but it's not: just janitorial cleanup, moving #includes from headers into source file

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