source: trunk/libtransmission/tracker.c @ 1517

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

Use a tr_tracker_info_t * directly in tr_info_t and tr_tracker_t.

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