source: trunk/libtransmission/tracker.c @ 1425

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

More simplifications

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