source: branches/scrape/libtransmission/tracker.c @ 1135

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

store/release trackerid

  • Property svn:keywords set to Date Rev Author Id
File size: 18.1 KB
Line 
1/******************************************************************************
2 * $Id: tracker.c 1135 2006-11-25 19:24:07Z 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
33    char           started;
34    char           completed;
35    char           stopped;
36
37    int            interval;
38    int            scrapeInterval;
39    int            seeders;
40    int            leechers;
41    int            hasManyPeers;
42    int            downloaded;
43
44    uint64_t       dateTry;
45    uint64_t       dateOk;
46    uint64_t       dateScrape;
47    int            lastScrapeFailed;
48
49#define TC_ATTEMPT_NOREACH 1
50#define TC_ATTEMPT_ERROR   2
51#define TC_ATTEMPT_OK      4
52    char           lastAttempt;
53    int            scrapeNeeded;
54
55    tr_http_t    * http;
56    tr_http_t    * httpScrape;
57
58    int            bindPort;
59    int            newPort;
60};
61
62static tr_http_t * getQuery   ( tr_tracker_t * tc );
63static void        readAnswer ( tr_tracker_t * tc, const char *, int );
64static void readScrapeAnswer( tr_tracker_t * tc, const char * data, int len );
65
66tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
67{
68    tr_tracker_t * tc;
69
70    tc           = calloc( 1, sizeof( tr_tracker_t ) );
71    tc->tor      = tor;
72    tc->id       = tor->id;
73
74    tc->started  = 1;
75
76    tc->interval = 300;
77    tc->scrapeInterval = 600;
78    tc->seeders  = -1;
79    tc->leechers = -1;
80    tc->downloaded  = -1;
81
82    tc->lastAttempt = TC_ATTEMPT_NOREACH;
83    tc->scrapeNeeded = 0;
84   
85    tc->lastScrapeFailed = 0;
86
87    tc->bindPort = *(tor->bindPort);
88    tc->newPort  = -1;
89
90    return tc;
91}
92
93static int shouldConnect( tr_tracker_t * tc )
94{
95    uint64_t now = tr_date();
96   
97    /* Unreachable tracker, try 10 seconds before trying again */
98    if( tc->lastAttempt == TC_ATTEMPT_NOREACH &&
99        now < tc->dateTry + 10000 )
100    {
101        return 0;
102    }
103
104    /* The tracker rejected us (like 4XX code, unauthorized IP...),
105       don't hammer it - we'll probably get the same answer next time
106       anyway */
107    if( tc->lastAttempt == TC_ATTEMPT_ERROR &&
108        now < tc->dateTry + 1000 * tc->interval )
109    {
110        return 0;
111    }
112
113    /* Do we need to send an event? */
114    if( tc->started || tc->completed || tc->stopped || 0 < tc->newPort )
115    {
116        return 1;
117    }
118
119    /* Should we try and get more peers? */
120    if( now > tc->dateOk + 1000 * tc->interval )
121    {
122        return 1;
123    }
124
125    /* If there is quite a lot of people on this torrent, stress
126       the tracker a bit until we get a decent number of peers */
127    if( tc->hasManyPeers )
128    {
129        if( tc->tor->peerCount < 5 && now > tc->dateOk + 10000 )
130        {
131            return 1;
132        }
133        if( tc->tor->peerCount < 10 && now > tc->dateOk + 20000 )
134        {
135            return 1;
136        }
137        if( tc->tor->peerCount < 15 && now > tc->dateOk + 30000 )
138        {
139            return 1;
140        }
141    }
142
143    return 0;
144}
145
146static int shouldScrape( tr_tracker_t * tc )
147{
148    // scrape not supported
149    if( !tc->tor->scrape[0] )
150    {
151        return 0;
152    }
153
154    uint64_t now = tr_date();
155    uint64_t interval = 1000 * MAX(tc->scrapeInterval, 60);
156   
157    // scrape half as often if there is no need to
158    if (!tc->scrapeNeeded && !tc->lastScrapeFailed)
159    {
160        interval *= 2;
161    }
162   
163    return now > tc->dateScrape + interval;
164}
165
166void tr_trackerChangePort( tr_tracker_t * tc, int port )
167{
168    tc->newPort = port;
169}
170
171int tr_trackerPulse( tr_tracker_t * tc )
172{
173    tr_torrent_t * tor = tc->tor;
174    tr_info_t    * inf = &tor->info;
175    const char   * data;
176    int            len;
177
178    if( ( NULL == tc->http ) && shouldConnect( tc ) )
179    {
180        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
181        {
182            return 0;
183        }
184        tc->dateTry = tr_date();
185        tc->http = getQuery( tc );
186
187        tr_inf( "Tracker: connecting to %s:%d (%s)",
188                inf->trackerAddress, inf->trackerPort,
189                tc->started ? "sending 'started'" :
190                ( tc->completed ? "sending 'completed'" :
191                  ( tc->stopped ? "sending 'stopped'" :
192                    ( 0 < tc->newPort ? "sending 'stopped' to change port" :
193                      "getting peers" ) ) ) );
194    }
195   
196    if( NULL != tc->http )
197    {
198        switch( tr_httpPulse( tc->http, &data, &len ) )
199        {
200            case TR_WAIT:
201                break;
202
203            case TR_ERROR:
204                tr_httpClose( tc->http );
205                tr_fdSocketClosed( tor->fdlimit, 1 );
206                tc->http    = NULL;
207                tc->dateTry = tr_date();
208                break;
209
210            case TR_OK:
211                readAnswer( tc, data, len );
212                tr_httpClose( tc->http );
213                tc->http = NULL;
214                tr_fdSocketClosed( tor->fdlimit, 1 );
215                break;
216        }
217    }
218   
219    if( ( NULL == tc->httpScrape ) && shouldScrape( tc ) )
220    {
221        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
222        {
223            return 0;
224        }
225        tc->dateScrape = tr_date();
226        tc->httpScrape = tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
227                            "%s%sinfo_hash=%s", tor->scrape, strchr( tor->scrape, '?' ) ?
228                            "&" : "?", tor->escapedHashString );
229        tr_inf( "Scrape: sent http request to %s:%d",
230                    inf->trackerAddress, inf->trackerPort );
231    }
232   
233    if( NULL != tc->httpScrape )
234    {
235        switch( tr_httpPulse( tc->httpScrape, &data, &len ) )
236        {
237            case TR_WAIT:
238                return 0;
239
240            case TR_ERROR:
241                tr_httpClose( tc->httpScrape );
242                tr_fdSocketClosed( tor->fdlimit, 1 );
243                tc->httpScrape    = NULL;
244                tc->lastScrapeFailed = 1;
245                return 0;
246
247            case TR_OK:
248                readScrapeAnswer( tc, data, len );
249                tr_httpClose( tc->httpScrape );
250                tc->httpScrape = NULL;
251                tr_fdSocketClosed( tor->fdlimit, 1 );
252                break;
253        }
254    }
255
256    return 0;
257}
258
259void tr_trackerCompleted( tr_tracker_t * tc )
260{
261    tc->started   = 0;
262    tc->completed = 1;
263    tc->stopped   = 0;
264}
265
266void tr_trackerStopped( tr_tracker_t * tc )
267{
268    tr_torrent_t * tor = tc->tor;
269
270    if( NULL != tc->http )
271    {
272        /* If we are already sendy a query at the moment, we need to
273           reconnect */
274        tr_httpClose( tc->http );
275        tc->http = NULL;
276        tr_fdSocketClosed( tor->fdlimit, 1 );
277    }
278    if( NULL != tc->httpScrape )
279    {
280        /* If we are already sendy a query at the moment, we need to
281           reconnect */
282        tr_httpClose( tc->httpScrape );
283        tc->httpScrape = NULL;
284        tr_fdSocketClosed( tor->fdlimit, 1 );
285    }
286
287    tc->started   = 0;
288    tc->completed = 0;
289    tc->stopped   = 1;
290
291    /* Even if we have connected recently, reconnect right now */
292    tc->dateTry = 0;
293}
294
295void tr_trackerClose( tr_tracker_t * tc )
296{
297    tr_torrent_t * tor = tc->tor;
298
299    if( NULL != tc->http )
300    {
301        tr_httpClose( tc->http );
302        tr_fdSocketClosed( tor->fdlimit, 1 );
303    }
304    if( NULL != tc->httpScrape )
305    {
306        tr_httpClose( tc->httpScrape );
307        tr_fdSocketClosed( tor->fdlimit, 1 );
308    }
309   
310    if( tor->trackerid )
311        free( tor->trackerid );
312   
313    free( tc );
314}
315
316static tr_http_t * getQuery( tr_tracker_t * tc )
317{
318    tr_torrent_t * tor = tc->tor;
319    tr_info_t    * inf = &tor->info;
320
321    char         * event;
322    char         * trackerid;
323    uint64_t       left;
324    uint64_t       down;
325    uint64_t       up;
326    char         * start;
327    int            numwant = 50;
328
329    down = tor->downloadedCur;
330    up = tor->uploadedCur;
331    if( tc->started )
332    {
333        event = "&event=started";
334        down = up = 0;
335       
336        if( 0 < tc->newPort )
337        {
338            tc->bindPort = tc->newPort;
339            tc->newPort = -1;
340        }
341    }
342    else if( tc->completed )
343    {
344        event = "&event=completed";
345    }
346    else if( tc->stopped || 0 < tc->newPort )
347    {
348        event = "&event=stopped";
349        numwant = 0;
350    }
351    else
352    {
353        event = "";
354    }
355
356    if( NULL == strchr( inf->trackerAnnounce, '?' ) )
357    {
358        start = "?";
359    }
360    else
361    {
362        start = "&";
363    }
364
365    left = tr_cpLeftBytes( tor->completion );
366   
367    trackerid = tor->trackerid ? ( "&trackerid=%s", tor->trackerid ) : ""; 
368
369    return tr_httpClient( TR_HTTP_GET, inf->trackerAddress,
370                          inf->trackerPort,
371                          "%s%s"
372                          "info_hash=%s&"
373                          "peer_id=%s&"
374                          "port=%d&"
375                          "uploaded=%"PRIu64"&"
376                          "downloaded=%"PRIu64"&"
377                          "left=%"PRIu64"&"
378                          "compact=1&"
379                          "numwant=%d&"
380                          "key=%s"
381                          "%s"
382                          "%s",
383                          inf->trackerAnnounce, start, tor->escapedHashString, tc->id,
384                          tc->bindPort, up, down, left, numwant, tor->key, trackerid, event );
385}
386
387static void readAnswer( tr_tracker_t * tc, const char * data, int len )
388{
389    tr_torrent_t * tor = tc->tor;
390    int i;
391    int code;
392    benc_val_t   beAll;
393    benc_val_t * bePeers, * beFoo;
394    const uint8_t * body;
395    int bodylen;
396    int shouldfree;
397
398    tc->dateTry = tr_date();
399    code = tr_httpResponseCode( data, len );
400    if( 0 > code )
401    {
402        /* We don't have a valid HTTP status line */
403        tr_inf( "Tracker: invalid HTTP status line" );
404        tc->lastAttempt = TC_ATTEMPT_NOREACH;
405        return;
406    }
407
408    if( !TR_HTTP_STATUS_OK( code ) )
409    {
410        /* we didn't get a 2xx status code */
411        tr_err( "Tracker: invalid HTTP status code: %i", code );
412        tc->lastAttempt = TC_ATTEMPT_ERROR;
413        return;
414    }
415
416    /* find the end of the http headers */
417    body = (uint8_t *) tr_httpParse( data, len, NULL );
418    if( NULL == body )
419    {
420        tr_err( "Tracker: could not find end of HTTP headers" );
421        tc->lastAttempt = TC_ATTEMPT_NOREACH;
422        return;
423    }
424    bodylen = len - (body - (const uint8_t*)data);
425
426    /* Find and load the dictionary */
427    shouldfree = 0;
428    for( i = 0; i < bodylen; i++ )
429    {
430        if( !tr_bencLoad( &body[i], bodylen - i, &beAll, NULL ) )
431        {
432            shouldfree = 1;
433            break;
434        }
435    }
436
437    if( i >= bodylen )
438    {
439        if( tc->stopped || 0 < tc->newPort )
440        {
441            tc->lastAttempt = TC_ATTEMPT_OK;
442            goto nodict;
443        }
444        tr_err( "Tracker: no valid dictionary found in answer" );
445        tc->lastAttempt = TC_ATTEMPT_ERROR;
446        return;
447    }
448
449    // tr_bencPrint( &beAll );
450
451    if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) )
452    {
453        tr_err( "Tracker: %s", bePeers->val.s.s );
454        tor->error |= TR_ETRACKER;
455        snprintf( tor->trackerError, sizeof( tor->trackerError ),
456                  "%s", bePeers->val.s.s );
457        tc->lastAttempt = TC_ATTEMPT_ERROR;
458        goto cleanup;
459    }
460
461    tor->error &= ~TR_ETRACKER;
462    tc->lastAttempt = TC_ATTEMPT_OK;
463
464    if( !tc->interval )
465    {
466        /* Get the tracker interval, ignore it if it is not between
467           10 sec and 5 mins */
468        if( !( beFoo = tr_bencDictFind( &beAll, "interval" ) ) ||
469            !( beFoo->type & TYPE_INT ) )
470        {
471            tr_err( "Tracker: no 'interval' field" );
472            goto cleanup;
473        }
474
475        tc->interval = beFoo->val.i;
476        tc->interval = MIN( tc->interval, 300 );
477        tc->interval = MAX( 10, tc->interval );
478
479        tr_inf( "Tracker: interval = %d seconds", tc->interval );
480    }
481
482    int scrapeNeeded = 0;
483    if( ( beFoo = tr_bencDictFind( &beAll, "complete" ) ) &&
484        ( beFoo->type & TYPE_INT ) )
485    {
486        tc->seeders = beFoo->val.i;
487    }
488    else
489    {
490        scrapeNeeded = 1;
491    }
492    if( ( beFoo = tr_bencDictFind( &beAll, "incomplete" ) ) &&
493        ( beFoo->type & TYPE_INT ) )
494    {
495        tc->leechers = beFoo->val.i;
496    }
497    else
498    {
499        scrapeNeeded = 1;
500    }
501    tc->scrapeNeeded = scrapeNeeded;
502   
503    if( tc->seeders + tc->leechers >= 50 )
504    {
505        tc->hasManyPeers = 1;
506    }
507   
508    if( beFoo = tr_bencDictFind( &beAll, "tracker id" ) )
509    {
510        if( tor->trackerid )
511            free( tor->trackerid );
512        tor->trackerid = strdup( beFoo->val.s.s );
513        tr_inf( "Tracker: tracker id = %s", tor->trackerid);
514    }
515   
516    if( !( bePeers = tr_bencDictFind( &beAll, "peers" ) ) )
517    {
518        if( tc->stopped || 0 < tc->newPort )
519        {
520            goto nodict;
521        }
522        tr_err( "Tracker: no \"peers\" field" );
523        goto cleanup;
524    }
525
526    if( bePeers->type & TYPE_LIST )
527    {
528        char * ip;
529        int    port;
530
531        /* Original protocol */
532        tr_inf( "Tracker: got %d peers", bePeers->val.l.count );
533
534        for( i = 0; i < bePeers->val.l.count; i++ )
535        {
536            beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "ip" );
537            if( !beFoo )
538                continue;
539            ip = beFoo->val.s.s;
540            beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "port" );
541            if( !beFoo )
542                continue;
543            port = beFoo->val.i;
544
545            tr_peerAddOld( tor, ip, port );
546        }
547
548        if( bePeers->val.l.count >= 50 )
549        {
550            tc->hasManyPeers = 1;
551        }
552    }
553    else if( bePeers->type & TYPE_STR )
554    {
555        struct in_addr addr;
556        in_port_t      port;
557
558        /* "Compact" extension */
559        if( bePeers->val.s.i % 6 )
560        {
561            tr_err( "Tracker: \"peers\" of size %d",
562                    bePeers->val.s.i );
563            tr_lockUnlock( &tor->lock );
564            goto cleanup;
565        }
566
567        tr_inf( "Tracker: got %d peers", bePeers->val.s.i / 6 );
568        for( i = 0; i < bePeers->val.s.i / 6; i++ )
569        {
570            memcpy( &addr, &bePeers->val.s.s[6*i],   4 );
571            memcpy( &port, &bePeers->val.s.s[6*i+4], 2 );
572
573            tr_peerAddCompact( tor, addr, port );
574        }
575
576        if( bePeers->val.s.i / 6 >= 50 )
577        {
578            tc->hasManyPeers = 1;
579        }
580    }
581
582nodict:
583    /* Success */
584    tc->started   = 0;
585    tc->completed = 0;
586    tc->dateOk    = tr_date();
587
588    if( tc->stopped )
589    {
590        tor->status = TR_STATUS_STOPPED;
591        tc->stopped = 0;
592    }
593    else if( 0 < tc->newPort )
594    {
595        tc->started  = 1;
596    }
597
598cleanup:
599    if( shouldfree )
600    {
601        tr_bencFree( &beAll );
602    }
603}
604
605static void readScrapeAnswer( tr_tracker_t * tc, const char * data, int len )
606{
607    tr_torrent_t * tor = tc->tor;
608    int i;
609    int code;
610    const uint8_t * body;
611    int bodylen, ii;
612    benc_val_t scrape, * val1, * val2;
613
614    code = tr_httpResponseCode( data, len );
615    if( 0 > code )
616    {
617        /* We don't have a valid HTTP status line */
618        tr_inf( "Scrape: invalid HTTP status line" );
619        tc->lastScrapeFailed = 1;
620        return;
621    }
622
623    if( !TR_HTTP_STATUS_OK( code ) )
624    {
625        /* we didn't get a 2xx status code */
626        tr_err( "Scrape: invalid HTTP status code: %i", code );
627        tc->lastScrapeFailed = 1;
628        return;
629    }
630
631    /* find the end of the http headers */
632    body = (uint8_t *) tr_httpParse( data, len, NULL );
633    if( NULL == body )
634    {
635        tr_err( "Scrape: could not find end of HTTP headers" );
636        tc->lastScrapeFailed = 1;
637        return;
638    }
639   
640    tc->lastScrapeFailed = 0;
641   
642    bodylen = len - (body - (const uint8_t*)data);
643
644    for( ii = 0; ii < bodylen - 8; ii++ )
645    {
646        if( !memcmp( body + ii, "d5:files", 8 ) )
647        {
648            break;
649        }
650    }
651    if( ii >= bodylen - 8 )
652    {
653        return;
654    }
655    if( tr_bencLoad( body + ii, bodylen - ii, &scrape, NULL ) )
656    {
657        return;
658    }
659
660    val1 = tr_bencDictFind( &scrape, "files" );
661    if( !val1 )
662    {
663        tr_bencFree( &scrape );
664        return;
665    }
666    val1 = &val1->val.l.vals[1];
667    if( !val1 )
668    {
669        tr_bencFree( &scrape );
670        return;
671    }
672   
673    val2 = tr_bencDictFind( val1, "complete" );
674    if( !val2 )
675    {
676        tr_bencFree( &scrape );
677        return;
678    }
679    tc->seeders = val2->val.i;
680   
681    val2 = tr_bencDictFind( val1, "incomplete" );
682    if( !val2 )
683    {
684        tr_bencFree( &scrape );
685        return;
686    }
687    tc->leechers = val2->val.i;
688   
689    val2 = tr_bencDictFind( val1, "downloaded" );
690    if( !val2 )
691    {
692        tr_bencFree( &scrape );
693        return;
694    }
695    tc->downloaded = val2->val.i;
696   
697    val2 = tr_bencDictFind( val1, "flags" );
698    if (val2)
699    {
700        val2 = tr_bencDictFind( val2, "min_request_interval" );
701        if (val2)
702        {
703            tc->scrapeInterval = val2->val.i;
704        }
705    }
706   
707    tc->hasManyPeers = tc->seeders + tc->leechers >= 50;
708   
709    tr_bencFree( &scrape );
710}
711
712int tr_trackerSeeders( tr_tracker_t * tc )
713{
714    if( !tc )
715    {
716        return -1;
717    }
718    return tc->seeders;
719}
720
721int tr_trackerLeechers( tr_tracker_t * tc )
722{
723    if( !tc )
724    {
725        return -1;
726    }
727    return tc->leechers;
728}
729
730int tr_trackerDownloaded( tr_tracker_t * tc )
731{
732    if( !tc )
733    {
734        return -1;
735    }
736    return tc->downloaded;
737}
Note: See TracBrowser for help on using the repository browser.