source: trunk/libtransmission/tracker.c @ 2573

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

continue whittling away at internal.h to reduce the global #include dependencies

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