source: branches/multitracker/libtransmission/tracker.c @ 1233

Last change on this file since 1233 was 1233, checked in by livings124, 16 years ago

len-1

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