source: branches/daemon/libtransmission/tracker.c @ 1712

Last change on this file since 1712 was 1712, checked in by joshe, 15 years ago

Merge libT revs 1616:1711 from trunk to daemon branch.

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