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

Last change on this file since 1176 was 1176, checked in by livings124, 14 years ago

small change to message output text

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