source: trunk/libtransmission/tracker.c @ 1417

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

small memleaks

  • Property svn:keywords set to Date Rev Author Id
File size: 31.2 KB
Line 
1/******************************************************************************
2 * $Id: tracker.c 1417 2007-01-20 05:07:00Z 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, tr_fd_t * fdlimit );
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( tor->fdlimit, 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, tor->fdlimit );
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, tor->fdlimit );
456                break;
457        }
458    }
459   
460    if( ( NULL == tc->httpScrape ) && shouldScrape( tc ) )
461    {
462        if( tr_fdSocketWillCreate( tor->fdlimit, 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, tor->fdlimit );
500                tc->lastScrapeFailed = 1;
501                break;
502
503            case TR_NET_OK:
504                readScrapeAnswer( tc, data, len );
505                killHttp( &tc->httpScrape, tor->fdlimit );
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    tr_torrent_t * tor = tc->tor;
523
524    /* If we are already sending a query at the moment, we need to
525       reconnect */
526    killHttp( &tc->http, tor->fdlimit );
527
528    tc->started   = 0;
529    tc->completed = 0;
530    tc->stopped   = 1;
531
532    /* Even if we have connected recently, reconnect right now */
533    tc->dateTry = 0;
534}
535
536void tr_trackerClose( tr_tracker_t * tc )
537{
538    tr_torrent_t * tor = tc->tor;
539    tr_info_t * inf = &tor->info;
540    tr_announce_list_ptr_t * cur, * curFree;
541    int ii;
542
543    killHttp( &tc->http, tor->fdlimit );
544    killHttp( &tc->httpScrape, tor->fdlimit );
545
546    for( ii = 0; ii < inf->trackerTiers; ii++ )
547    {
548        for( cur = tc->trackerAnnounceListPtr[ii]; cur; )
549        {
550            curFree = cur;
551            cur = cur->nextItem;
552            free( curFree );
553        }
554    }
555    free( tc->trackerAnnounceListPtr );
556
557    free( tc->trackerid );
558    free( tc );
559}
560
561static tr_http_t * getQuery( tr_tracker_t * tc )
562{
563    tr_torrent_t * tor = tc->tor;
564
565    char         * event, * trackerid, * idparam;
566    uint64_t       left;
567    uint64_t       down;
568    uint64_t       up;
569    char           start;
570    int            numwant = 50;
571
572    down = tor->downloadedCur;
573    up   = tor->uploadedCur;
574    if( tc->started )
575    {
576        event = "&event=started";
577        down  = 0;
578        up    = 0;
579
580        if( 0 < tc->newPort )
581        {
582            tc->bindPort = tc->newPort;
583            tc->newPort  = -1;
584        }
585    }
586    else if( tc->completed )
587    {
588        event = "&event=completed";
589    }
590    else if( tc->stopped || 0 < tc->newPort )
591    {
592        event = "&event=stopped";
593        numwant = 0;
594    }
595    else
596    {
597        event = "";
598    }
599
600    if( NULL == tc->trackerid )
601    {
602        trackerid = "";
603        idparam   = "";
604    }
605    else
606    {
607        trackerid = tc->trackerid;
608        idparam   = "&trackerid=";
609    }
610
611    start = ( strchr( tc->trackerAnnounce, '?' ) ? '&' : '?' );
612    left  = tr_cpLeftBytes( tor->completion );
613
614    return tr_httpClient( TR_HTTP_GET, tc->trackerAddress, tc->trackerPort,
615                          "%s%c"
616                          "info_hash=%s&"
617                          "peer_id=%s&"
618                          "port=%d&"
619                          "uploaded=%"PRIu64"&"
620                          "downloaded=%"PRIu64"&"
621                          "left=%"PRIu64"&"
622                          "compact=1&"
623                          "numwant=%d&"
624                          "key=%s"
625                          "%s%s"
626                          "%s",
627                          tc->trackerAnnounce, start, tor->escapedHashString,
628                          tc->id, tc->bindPort, up, down, left, numwant,
629                          tor->key, idparam, trackerid, event );
630}
631
632static tr_http_t * getScrapeQuery( tr_tracker_t * tc )
633{
634    tr_torrent_t * tor = tc->tor;
635    char           start;
636
637    start = ( strchr( tc->trackerScrape, '?' ) ? '&' : '?' );
638
639    return tr_httpClient( TR_HTTP_GET, tc->trackerAddress, tc->trackerPort,
640                          "%s%c"
641                          "info_hash=%s",
642                          tc->trackerScrape, start, tor->escapedHashString );
643}
644
645static void readAnswer( tr_tracker_t * tc, const char * data, int len,
646                        int * _peerCount, uint8_t ** _peerCompact )
647{
648    tr_torrent_t * tor = tc->tor;
649    int i;
650    int code;
651    benc_val_t   beAll;
652    benc_val_t * bePeers, * beFoo;
653    const uint8_t * body;
654    int bodylen, shouldfree, scrapeNeeded;
655    char * address;
656    int peerCount;
657    uint8_t * peerCompact;
658
659    *_peerCount = peerCount = 0;
660    *_peerCompact = peerCompact = NULL;
661
662    tc->dateTry = tr_date();
663    code = tr_httpResponseCode( data, len );
664   
665    if( 0 > code )
666    {
667        /* We don't have a valid HTTP status line */
668        tr_inf( "Tracker: invalid HTTP status line" );
669        tc->lastError = 1;
670        failureAnnouncing( tc );
671        return;
672    }
673   
674    if( code == 301 || code == 302 )
675    {
676        tr_http_header_t hdr[] = { { "Location", NULL, 0 }, { NULL, NULL, 0 } };
677
678        tr_err( "Tracker: HTTP status code: %i", code );
679       
680        tr_httpParse( data, len, hdr );
681       
682        address = calloc( sizeof( char ), hdr->len+1 );
683        snprintf( address, hdr->len+1, "%s", hdr->data );
684       
685        tc->shouldChangeAnnounce = TC_CHANGE_REDIRECT;
686        tc->redirectAddress = address;
687        tc->redirectAddressLen = hdr->len;
688       
689        return;
690    }
691
692    if( !TR_HTTP_STATUS_OK( code ) )
693    {
694        /* we didn't get a 2xx status code */
695        tr_err( "Tracker: invalid HTTP status code: %i", code );
696        tc->lastError = 1;
697        tc->allUnreachIfError = 0;
698        failureAnnouncing( tc );
699        return;
700    }
701
702    /* find the end of the http headers */
703    body = (uint8_t *) tr_httpParse( data, len, NULL );
704    if( NULL == body )
705    {
706        tr_err( "Tracker: could not find end of HTTP headers" );
707        tc->lastError = 1;
708        failureAnnouncing( tc );
709        return;
710    }
711    bodylen = len - ( body - (const uint8_t*)data );
712
713    /* Find and load the dictionary */
714    shouldfree = 0;
715    for( i = 0; i < bodylen; i++ )
716    {
717        if( !tr_bencLoad( &body[i], bodylen - i, &beAll, NULL ) )
718        {
719            shouldfree = 1;
720            break;
721        }
722    }
723
724    if( i >= bodylen )
725    {
726        if( tc->stopped || 0 < tc->newPort )
727        {
728            tc->lastError = 0;
729            goto nodict;
730        }
731        tr_err( "Tracker: no valid dictionary found in answer" );
732        tc->lastError = 1;
733        tc->allUnreachIfError = 0;
734        failureAnnouncing( tc );
735        return;
736    }
737
738    // tr_bencPrint( &beAll );
739
740    if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) )
741    {
742        tr_err( "Tracker: Error - %s", bePeers->val.s.s );
743        tor->error = TR_ERROR_TC_ERROR;
744        snprintf( tor->errorString, sizeof( tor->errorString ),
745                  "%s", bePeers->val.s.s );
746        tc->lastError = 1;
747        tc->allUnreachIfError = 0;
748        failureAnnouncing( tc );
749        goto cleanup;
750    }
751    else if( ( bePeers = tr_bencDictFind( &beAll, "warning message" ) ) )
752    {
753        tr_err( "Tracker: Warning - %s", bePeers->val.s.s );
754        tor->error = TR_ERROR_TC_WARNING;
755        snprintf( tor->errorString, sizeof( tor->errorString ),
756                  "%s", bePeers->val.s.s );
757    }
758    else if( tor->error & TR_ERROR_TC_MASK )
759    {
760        tor->error = TR_OK;
761    }
762
763    tc->lastError = 0;
764    tc->allUnreachIfError = 0;
765
766    /* Get the tracker interval, force to between
767       10 sec and 5 mins */
768    beFoo = tr_bencDictFind( &beAll, "interval" );
769    if( !beFoo || TYPE_INT != beFoo->type )
770    {
771        tr_err( "Tracker: no 'interval' field" );
772        goto cleanup;
773    }
774
775    tc->interval = beFoo->val.i;
776    tr_inf( "Tracker: interval = %d seconds", tc->interval );
777
778    tc->interval = MIN( tc->interval, 300 );
779    tc->interval = MAX( 10, tc->interval );
780
781    /* Get the tracker minimum interval, force to between
782       10 sec and 5 mins  */
783    beFoo = tr_bencDictFind( &beAll, "min interval" );
784    if( beFoo && TYPE_INT == beFoo->type )
785    {
786        tc->minInterval = beFoo->val.i;
787        tr_inf( "Tracker: min interval = %d seconds", tc->minInterval );
788
789        tc->minInterval = MIN( tc->minInterval, 300 );
790        tc->minInterval = MAX( 10, tc->minInterval );
791
792        if( tc->interval < tc->minInterval )
793        {
794            tc->interval = tc->minInterval;
795            tr_inf( "Tracker: 'interval' less than 'min interval', "
796                    "using 'min interval'" );
797        }
798    }
799    else
800    {
801        tc->minInterval = 0;
802        tr_inf( "Tracker: no 'min interval' field" );
803    }
804
805    scrapeNeeded = 0;
806    beFoo = tr_bencDictFind( &beAll, "complete" );
807    if( beFoo && TYPE_INT == beFoo->type )
808    {
809        tc->seeders = beFoo->val.i;
810    }
811    else
812    {
813        scrapeNeeded = 1;
814    }
815
816    beFoo = tr_bencDictFind( &beAll, "incomplete" );
817    if( beFoo && TYPE_INT == beFoo->type )
818    {
819        tc->leechers = beFoo->val.i;
820    }
821    else
822    {
823        scrapeNeeded = 1;
824    }
825
826    tc->scrapeNeeded = scrapeNeeded;
827    if( !scrapeNeeded )
828    {
829        tc->hasManyPeers = ( tc->seeders + tc->leechers >= 50 );
830    }
831
832    beFoo = tr_bencDictFind( &beAll, "tracker id" );
833    if( beFoo )
834    {
835        free( tc->trackerid );
836        tc->trackerid = strdup( beFoo->val.s.s );
837        tr_inf( "Tracker: tracker id = %s", tc->trackerid );
838    }
839
840    bePeers = tr_bencDictFind( &beAll, "peers" );
841    if( !bePeers )
842    {
843        if( tc->stopped || 0 < tc->newPort )
844        {
845            goto nodict;
846        }
847        tr_err( "Tracker: no \"peers\" field" );
848        failureAnnouncing( tc );
849        goto cleanup;
850    }
851
852    if( bePeers->type & TYPE_LIST )
853    {
854        /* Original protocol */
855        if( bePeers->val.l.count > 0 )
856        {
857            struct in_addr addr;
858            in_port_t port;
859
860            peerCount = 0;
861            peerCompact = malloc( 6 * bePeers->val.l.count );
862
863            /* Convert to compact form */
864            for( i = 0; i < bePeers->val.l.count; i++ )
865            {
866                if( !( beFoo = tr_bencDictFind(
867                    &bePeers->val.l.vals[i], "ip" ) ) )
868                    continue;
869                if( tr_netResolve( beFoo->val.s.s, &addr ) )
870                    continue;
871                memcpy( &peerCompact[6 * peerCount], &addr, 4 );
872
873                if( !( beFoo = tr_bencDictFind(
874                    &bePeers->val.l.vals[i], "port" ) ) )
875                    continue;
876                port = htons( beFoo->val.i );
877                memcpy( &peerCompact[6 * peerCount + 4], &port, 2 );
878
879                peerCount++;
880            }
881        }
882    }
883    else if( bePeers->type & TYPE_STR )
884    {
885        /* "Compact" extension */
886        if( bePeers->val.s.i >= 6 )
887        {
888            peerCount = bePeers->val.s.i / 6;
889            peerCompact = malloc( bePeers->val.s.i );
890            memcpy( peerCompact, bePeers->val.s.s, bePeers->val.s.i );
891        }
892    }
893
894    if( peerCount > 0 )
895    {
896        tr_inf( "Tracker: got %d peers", peerCount );
897        if( peerCount >= 50 )
898        {
899            tc->hasManyPeers = 1;
900        }
901        *_peerCount = peerCount;
902        *_peerCompact = peerCompact;
903    }
904
905nodict:
906    /* Success */
907    tc->started   = 0;
908    tc->completed = 0;
909    tc->dateOk    = tr_date();
910
911    if( tc->stopped )
912    {
913        tor->status = TR_STATUS_STOPPED;
914        tc->stopped = 0;
915    }
916    else if( 0 < tc->newPort )
917    {
918        tc->started  = 1;
919    }
920
921cleanup:
922    if( shouldfree )
923    {
924        tr_bencFree( &beAll );
925    }
926}
927
928static void readScrapeAnswer( tr_tracker_t * tc, const char * data, int len )
929{
930    int code;
931    const uint8_t * body;
932    int bodylen, ii;
933    benc_val_t scrape, * val1, * val2;
934    char * address;
935
936    code = tr_httpResponseCode( data, len );
937    if( 0 > code )
938    {
939        /* We don't have a valid HTTP status line */
940        tr_inf( "Scrape: invalid HTTP status line" );
941        tc->lastScrapeFailed = 1;
942        return;
943    }
944   
945    if( code == 301 || code == 302 )
946    {
947        tr_http_header_t hdr[] = { { "Location", NULL, 0 }, { NULL, NULL, 0 } };
948       
949        tr_err( "Scrape: HTTP status code: %i", code );
950
951        tr_httpParse( data, len, hdr );
952       
953        address = calloc( sizeof( char ), hdr->len+1 );
954        snprintf( address, hdr->len+1, "%s", hdr->data );
955       
956        /* Needs a new scrape */
957        tc->dateScrape = 0;
958       
959        tc->redirectScrapeAddress = address;
960        tc->redirectScrapeAddressLen = hdr->len;
961       
962        return;
963    }
964
965    if( !TR_HTTP_STATUS_OK( code ) )
966    {
967        /* we didn't get a 2xx status code */
968        tr_err( "Scrape: invalid HTTP status code: %i", code );
969        if( TR_HTTP_STATUS_FAIL_CLIENT( code ) )
970            tc->trackerCanScrape = 0;
971        tc->lastScrapeFailed = 1;
972        return;
973    }
974
975    /* find the end of the http headers */
976    body = (uint8_t *) tr_httpParse( data, len, NULL );
977    if( NULL == body )
978    {
979        tr_err( "Scrape: could not find end of HTTP headers" );
980        tc->lastScrapeFailed = 1;
981        return;
982    }
983
984    tc->lastScrapeFailed = 0;
985    bodylen = len - ( body - (const uint8_t*)data );
986
987    for( ii = 0; ii < bodylen; ii++ )
988    {
989        if( !tr_bencLoad( body + ii, bodylen - ii, &scrape, NULL ) )
990        {
991            break;
992        }
993    }
994    if( ii >= bodylen )
995    {
996        return;
997    }
998
999    val1 = tr_bencDictFind( &scrape, "files" );
1000    if( !val1 )
1001    {
1002        tr_bencFree( &scrape );
1003        return;
1004    }
1005    val1 = &val1->val.l.vals[1];
1006    if( !val1 )
1007    {
1008        tr_bencFree( &scrape );
1009        return;
1010    }
1011   
1012    val2 = tr_bencDictFind( val1, "complete" );
1013    if( !val2 )
1014    {
1015        tr_bencFree( &scrape );
1016        return;
1017    }
1018    tc->seeders = val2->val.i;
1019   
1020    val2 = tr_bencDictFind( val1, "incomplete" );
1021    if( !val2 )
1022    {
1023        tr_bencFree( &scrape );
1024        return;
1025    }
1026    tc->leechers = val2->val.i;
1027   
1028    val2 = tr_bencDictFind( val1, "downloaded" );
1029    if( !val2 )
1030    {
1031        tr_bencFree( &scrape );
1032        return;
1033    }
1034    tc->complete = val2->val.i;
1035   
1036    val2 = tr_bencDictFind( &scrape, "flags" );
1037    if( val2 )
1038    {
1039        val2 = tr_bencDictFind( val2, "min_request_interval" );
1040        if( val2 )
1041        {
1042            tc->scrapeInterval = val2->val.i;
1043            tr_inf( "Scrape: min_request_interval = %d seconds", tc->scrapeInterval );
1044        }
1045    }
1046   
1047    tc->hasManyPeers = ( tc->seeders + tc->leechers >= 50 );
1048   
1049    tr_bencFree( &scrape );
1050}
1051
1052int tr_trackerSeeders( tr_tracker_t * tc )
1053{
1054    if( !tc )
1055    {
1056        return -1;
1057    }
1058    return tc->seeders;
1059}
1060
1061int tr_trackerLeechers( tr_tracker_t * tc )
1062{
1063    if( !tc )
1064    {
1065        return -1;
1066    }
1067    return tc->leechers;
1068}
1069
1070int tr_trackerDownloaded( tr_tracker_t * tc )
1071{
1072    if( !tc )
1073    {
1074        return -1;
1075    }
1076    return tc->complete;
1077}
1078
1079const char * tr_trackerAddress( tr_tracker_t * tc )
1080{
1081    if( !tc )
1082    {
1083        return NULL;
1084    }
1085    return tc->trackerAddress;
1086}
1087
1088int tr_trackerPort( tr_tracker_t * tc )
1089{
1090    if( !tc )
1091    {
1092        return 0;
1093    }
1094    return tc->trackerPort;
1095}
1096
1097const char * tr_trackerAnnounce( tr_tracker_t * tc )
1098{
1099    if( !tc )
1100    {
1101        return NULL;
1102    }
1103    return tc->trackerAnnounce;
1104}
1105
1106int tr_trackerCannotConnect( tr_tracker_t * tc )
1107{
1108    if( !tc )
1109    {
1110        return 0;
1111    }
1112    return tc->completelyUnconnectable;
1113}
1114
1115/* Blocking version */
1116int tr_trackerScrape( tr_torrent_t * tor, int * s, int * l, int * d )
1117{
1118    tr_tracker_t * tc;
1119    tr_http_t    * http;
1120    const char   * data;
1121    int            len;
1122    int            ret;
1123   
1124    tc = tr_trackerInit( tor );
1125
1126    if( !tc->trackerCanScrape )
1127    {
1128        return 1;
1129    }
1130
1131    http = getScrapeQuery( tc );
1132
1133    for( data = NULL; !data; tr_wait( 10 ) )
1134    {
1135        switch( tr_httpPulse( http, &data, &len ) )
1136        {
1137            case TR_NET_WAIT:
1138                break;
1139
1140            case TR_NET_ERROR:
1141                goto scrapeDone;
1142
1143            case TR_NET_OK:
1144                readScrapeAnswer( tc, data, len );
1145                goto scrapeDone;
1146        }
1147    }
1148
1149scrapeDone:
1150    tr_httpClose( http );
1151
1152    ret = 1;
1153    if( tc->seeders > -1 && tc->leechers > -1 && tc->complete > -1 )
1154    {
1155        *s = tc->seeders;
1156        *l = tc->leechers;
1157        *d = tc->complete;
1158        ret = 0;
1159    }
1160
1161    tr_trackerClose( tc );
1162    return ret;
1163}
1164
1165static void killHttp( tr_http_t ** http, tr_fd_t * fdlimit )
1166{
1167    if( NULL != *http )
1168    {
1169        tr_httpClose( *http );
1170        tr_fdSocketClosed( fdlimit, 1 );
1171        *http = NULL;
1172    }
1173}
Note: See TracBrowser for help on using the repository browser.