source: trunk/libtransmission/tracker.c @ 1612

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

Remove tr_bencListIter(), it was a stupid idea.

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