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

Last change on this file since 1156 was 1156, checked in by livings124, 15 years ago

When trying next address in sublist, move it to the front of the sublist.

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