source: trunk/libtransmission/tracker.c @ 1420

Last change on this file since 1420 was 1420, checked in by titer, 15 years ago

Officially give up on making libT reentrant, and simplify our code instead

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