source: branches/new_api/libtransmission/tracker.c @ 139

Last change on this file since 139 was 139, checked in by titer, 16 years ago

Seperates status and error in tr_stat_t.

File size: 15.4 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005 Eric Petit
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23#include "transmission.h"
24
25struct tr_tracker_s
26{
27    tr_torrent_t * tor;
28
29    char         * id;
30
31    char           started;
32    char           completed;
33    char           stopped;
34
35    int            interval;
36    int            seeders;
37    int            leechers;
38    int            hasManyPeers;
39
40    uint64_t       dateTry;
41    uint64_t       dateOk;
42
43#define TC_STATUS_IDLE    1
44#define TC_STATUS_CONNECT 2
45#define TC_STATUS_RECV    4
46    char           status;
47
48    int            socket;
49    uint8_t      * buf;
50    int            size;
51    int            pos;
52
53    int            bindPort;
54    int            newPort;
55};
56
57static void sendQuery  ( tr_tracker_t * tc );
58static void recvAnswer ( tr_tracker_t * tc );
59
60tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
61{
62    tr_tracker_t * tc;
63
64    tc           = calloc( 1, sizeof( tr_tracker_t ) );
65    tc->tor      = tor;
66    tc->id       = tor->id;
67
68    tc->started  = 1;
69
70    tc->seeders  = -1;
71    tc->leechers = -1;
72
73    tc->status   = TC_STATUS_IDLE;
74    tc->size     = 1024;
75    tc->buf      = malloc( tc->size );
76
77    //tc->bindPort = h->bindPort;
78    tc->bindPort = 9090; /*FIXME*/
79    tc->newPort  = -1;
80
81    return tc;
82}
83
84static int shouldConnect( tr_tracker_t * tc )
85{
86    uint64_t now = tr_date();
87
88    /* In any case, always wait 5 seconds between two requests */
89    if( now < tc->dateTry + 5000 )
90    {
91        return 0;
92    }
93
94    /* Do we need to send an event? */
95    if( tc->started || tc->completed || tc->stopped || 0 < tc->newPort )
96    {
97        return 1;
98    }
99
100    /* Should we try and get more peers? */
101    if( now > tc->dateOk + 1000 * tc->interval )
102    {
103        return 1;
104    }
105
106    /* If there is quite a lot of people on this torrent, stress
107       the tracker a bit until we get a decent number of peers */
108    if( tc->hasManyPeers )
109    {
110        if( tc->tor->peerCount < 5 && now > tc->dateOk + 10000 )
111        {
112            return 1;
113        }
114        if( tc->tor->peerCount < 10 && now > tc->dateOk + 20000 )
115        {
116            return 1;
117        }
118        if( tc->tor->peerCount < 15 && now > tc->dateOk + 30000 )
119        {
120            return 1;
121        }
122    }
123
124    return 0;
125}
126
127void tr_trackerChangePort( tr_tracker_t * tc, int port )
128{
129    tc->newPort = port;
130}
131
132int tr_trackerPulse( tr_tracker_t * tc )
133{
134    tr_torrent_t * tor = tc->tor;
135    tr_info_t    * inf = &tor->info;
136    uint64_t       now = tr_date();
137
138    if( ( tc->status & TC_STATUS_IDLE ) && shouldConnect( tc ) )
139    {
140        struct in_addr addr;
141
142        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
143        {
144            return 0;
145        }
146
147        if( tr_netResolve( inf->trackerAddress, &addr ) )
148        {
149            tr_fdSocketClosed( tor->fdlimit, 1 );
150            return 0;
151        }
152
153        tc->socket = tr_netOpen( addr, htons( inf->trackerPort ) );
154        if( tc->socket < 0 )
155        {
156            tr_fdSocketClosed( tor->fdlimit, 1 );
157            return 0;
158        }
159
160        tr_inf( "Tracker: connecting to %s:%d (%s)",
161                inf->trackerAddress, inf->trackerPort,
162                tc->started ? "sending 'started'" :
163                ( tc->completed ? "sending 'completed'" :
164                  ( tc->stopped ? "sending 'stopped'" :
165                    ( 0 < tc->newPort ? "sending 'stopped' to change port" :
166                      "getting peers" ) ) ) );
167
168        tc->status  = TC_STATUS_CONNECT;
169        tc->dateTry = tr_date();
170    }
171
172    if( tc->status & TC_STATUS_CONNECT )
173    {
174        /* We are connecting to the tracker. Try to send the query */
175        sendQuery( tc );
176    }
177
178    if( tc->status & TC_STATUS_RECV )
179    {
180        /* Try to get something */
181        recvAnswer( tc );
182    }
183
184    if( tc->status > TC_STATUS_IDLE && now > tc->dateTry + 60000 )
185    {
186        /* Give up if the request wasn't successful within 60 seconds */
187        tr_inf( "Tracker: timeout reached (60 s)" );
188
189        tr_netClose( tc->socket );
190        tr_fdSocketClosed( tor->fdlimit, 1 );
191
192        tc->status  = TC_STATUS_IDLE;
193        tc->dateTry = tr_date();
194    }
195
196    return 0;
197}
198
199void tr_trackerCompleted( tr_tracker_t * tc )
200{
201    tc->started   = 0;
202    tc->completed = 1;
203    tc->stopped   = 0;
204}
205
206void tr_trackerStopped( tr_tracker_t * tc )
207{
208    tr_torrent_t * tor = tc->tor;
209
210    if( tc->status > TC_STATUS_CONNECT )
211    {
212        /* If we are already sendy a query at the moment, we need to
213           reconnect */
214        tr_netClose( tc->socket );
215        tr_fdSocketClosed( tor->fdlimit, 1 );
216        tc->status = TC_STATUS_IDLE;
217    }
218
219    tc->started   = 0;
220    tc->completed = 0;
221    tc->stopped   = 1;
222
223    /* Even if we have connected recently, reconnect right now */
224    if( tc->status & TC_STATUS_IDLE )
225    {
226        tc->dateTry = 0;
227    }
228}
229
230void tr_trackerClose( tr_tracker_t * tc )
231{
232    tr_torrent_t * tor = tc->tor;
233
234    if( tc->status > TC_STATUS_IDLE )
235    {
236        tr_netClose( tc->socket );
237        tr_fdSocketClosed( tor->fdlimit, 1 );
238    }
239    free( tc->buf );
240    free( tc );
241}
242
243static void sendQuery( tr_tracker_t * tc )
244{
245    tr_torrent_t * tor = tc->tor;
246    tr_info_t    * inf = &tor->info;
247
248    char     * event;
249    uint64_t   left;
250    int        ret;
251
252    if( tc->started && 0 < tc->newPort )
253    {
254        tc->bindPort = tc->newPort;
255        tc->newPort = -1;
256    }
257
258    if( tc->started )
259        event = "&event=started";
260    else if( tc->completed )
261        event = "&event=completed";
262    else if( tc->stopped || 0 < tc->newPort )
263        event = "&event=stopped";
264    else
265        event = "";
266
267    left = tr_cpLeftBytes( tor->completion );
268
269    ret = snprintf( (char *) tc->buf, tc->size,
270            "GET %s?"
271            "info_hash=%s&"
272            "peer_id=%s&"
273            "port=%d&"
274            "uploaded=%lld&"
275            "downloaded=%lld&"
276            "left=%lld&"
277            "compact=1&"
278            "numwant=50&"
279            "key=%s"
280            "%s "
281            "HTTP/1.1\r\n"
282            "Host: %s\r\n"
283            "User-Agent: Transmission/%d.%d\r\n"
284            "Connection: close\r\n\r\n",
285            inf->trackerAnnounce, tor->hashString, tc->id,
286            tc->bindPort, tor->uploaded, tor->downloaded,
287            left, tor->key, event, inf->trackerAddress,
288            VERSION_MAJOR, VERSION_MINOR );
289
290    ret = tr_netSend( tc->socket, tc->buf, ret );
291    if( ret & TR_NET_CLOSE )
292    {
293        tr_inf( "Tracker: connection failed" );
294        tr_netClose( tc->socket );
295        tr_fdSocketClosed( tor->fdlimit, 1 );
296        tc->status  = TC_STATUS_IDLE;
297        tc->dateTry = tr_date();
298    }
299    else if( !( ret & TR_NET_BLOCK ) )
300    {
301        // printf( "Tracker: sent %s", tc->buf );
302        tc->status = TC_STATUS_RECV;
303        tc->pos    = 0;
304    }
305}
306
307static void recvAnswer( tr_tracker_t * tc )
308{
309    tr_torrent_t * tor = tc->tor;
310    int ret;
311    int i;
312    benc_val_t   beAll;
313    benc_val_t * bePeers, * beFoo;
314
315    if( tc->pos == tc->size )
316    {
317        tc->size *= 2;
318        tc->buf   = realloc( tc->buf, tc->size );
319    }
320   
321    ret = tr_netRecv( tc->socket, &tc->buf[tc->pos],
322                    tc->size - tc->pos );
323
324    if( ret & TR_NET_BLOCK )
325    {
326        return;
327    }
328    if( !( ret & TR_NET_CLOSE ) )
329    {
330        // printf( "got %d bytes\n", ret );
331        tc->pos += ret;
332        return;
333    }
334
335    tr_netClose( tc->socket );
336    tr_fdSocketClosed( tor->fdlimit, 1 );
337    // printf( "connection closed, got total %d bytes\n", tc->pos );
338
339    tc->status  = TC_STATUS_IDLE;
340    tc->dateTry = tr_date();
341
342    if( tc->pos < 1 )
343    {
344        /* We got nothing */
345        return;
346    }
347
348    /* Find the beginning of the dictionary */
349    for( i = 0; i < tc->pos - 18; i++ )
350    {
351        /* Hem */
352        if( !memcmp( &tc->buf[i], "d8:interval", 11 ) ||
353            !memcmp( &tc->buf[i], "d8:complete", 11 ) ||
354            !memcmp( &tc->buf[i], "d14:failure reason", 18 ) )
355        {
356            break;
357        }
358    }
359
360    if( i >= tc->pos - 18 )
361    {
362        tr_err( "Tracker: no dictionary in answer" );
363        // printf( "%s\n", tc->buf );
364        return;
365    }
366
367    if( tr_bencLoad( &tc->buf[i], &beAll, NULL ) )
368    {
369        tr_err( "Tracker: error parsing bencoded data" );
370        return;
371    }
372
373    // tr_bencPrint( &beAll );
374
375    if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) )
376    {
377        tr_err( "Tracker: %s", bePeers->val.s.s );
378        tor->error |= TR_ETRACKER;
379        snprintf( tor->trackerError, sizeof( tor->trackerError ),
380                  "%s", bePeers->val.s.s );
381        goto cleanup;
382    }
383    tor->error &= ~TR_ETRACKER;
384
385    if( !tc->interval )
386    {
387        /* Get the tracker interval, ignore it if it is not between
388           10 sec and 5 mins */
389        if( !( beFoo = tr_bencDictFind( &beAll, "interval" ) ) ||
390            !( beFoo->type & TYPE_INT ) )
391        {
392            tr_err( "Tracker: no 'interval' field" );
393            goto cleanup;
394        }
395
396        tc->interval = beFoo->val.i;
397        tc->interval = MIN( tc->interval, 300 );
398        tc->interval = MAX( 10, tc->interval );
399
400        tr_inf( "Tracker: interval = %d seconds", tc->interval );
401    }
402
403    if( ( beFoo = tr_bencDictFind( &beAll, "complete" ) ) &&
404        ( beFoo->type & TYPE_INT ) )
405    {
406        tc->seeders = beFoo->val.i;
407    }
408    if( ( beFoo = tr_bencDictFind( &beAll, "incomplete" ) ) &&
409        ( beFoo->type & TYPE_INT ) )
410    {
411        tc->leechers = beFoo->val.i;
412    }
413    if( tc->seeders + tc->leechers >= 50 )
414    {
415        tc->hasManyPeers = 1;
416    }
417
418    if( !( bePeers = tr_bencDictFind( &beAll, "peers" ) ) )
419    {
420        tr_err( "Tracker: no \"peers\" field" );
421        goto cleanup;
422    }
423
424    if( bePeers->type & TYPE_LIST )
425    {
426        char * ip;
427        int    port;
428
429        /* Original protocol */
430        tr_inf( "Tracker: got %d peers", bePeers->val.l.count );
431
432        for( i = 0; i < bePeers->val.l.count; i++ )
433        {
434            beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "ip" );
435            if( !beFoo )
436                continue;
437            ip = beFoo->val.s.s;
438            beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "port" );
439            if( !beFoo )
440                continue;
441            port = beFoo->val.i;
442
443            tr_peerAddOld( tor, ip, port );
444        }
445
446        if( bePeers->val.l.count >= 50 )
447        {
448            tc->hasManyPeers = 1;
449        }
450    }
451    else if( bePeers->type & TYPE_STR )
452    {
453        struct in_addr addr;
454        in_port_t      port;
455
456        /* "Compact" extension */
457        if( bePeers->val.s.i % 6 )
458        {
459            tr_err( "Tracker: \"peers\" of size %d",
460                    bePeers->val.s.i );
461            tr_lockUnlock( &tor->lock );
462            goto cleanup;
463        }
464
465        tr_inf( "Tracker: got %d peers", bePeers->val.s.i / 6 );
466        for( i = 0; i < bePeers->val.s.i / 6; i++ )
467        {
468            memcpy( &addr, &bePeers->val.s.s[6*i],   4 );
469            memcpy( &port, &bePeers->val.s.s[6*i+4], 2 );
470
471            tr_peerAddCompact( tor, addr, port );
472        }
473
474        if( bePeers->val.s.i / 6 >= 50 )
475        {
476            tc->hasManyPeers = 1;
477        }
478    }
479
480    /* Success */
481    tc->started   = 0;
482    tc->completed = 0;
483    tc->dateOk    = tr_date();
484
485    if( tc->stopped )
486    {
487        tor->status = TR_STATUS_STOPPED;
488        tc->stopped = 0;
489    }
490    else if( 0 < tc->newPort )
491    {
492        tc->started = 1;
493    }
494
495cleanup:
496    tr_bencFree( &beAll );
497}
498
499int tr_trackerScrape( tr_torrent_t * tor, int * seeders, int * leechers )
500{
501    tr_info_t * inf = &tor->info;
502
503    int s, i, ret;
504    uint8_t buf[1024];
505    benc_val_t scrape, * val1, * val2;
506    struct in_addr addr;
507    uint64_t date;
508    int pos, len;
509
510    if( !tor->scrape[0] )
511    {
512        /* scrape not supported */
513        return 1;
514    }
515
516    if( tr_netResolve( inf->trackerAddress, &addr ) )
517    {
518        return 0;
519    }
520    s = tr_netOpen( addr, htons( inf->trackerPort ) );
521    if( s < 0 )
522    {
523        return 1;
524    }
525
526    len = snprintf( (char *) buf, sizeof( buf ),
527              "GET %s?info_hash=%s HTTP/1.1\r\n"
528              "Host: %s\r\n"
529              "Connection: close\r\n\r\n",
530              tor->scrape, tor->hashString,
531              inf->trackerAddress );
532
533    for( date = tr_date();; )
534    {
535        ret = tr_netSend( s, buf, len );
536        if( ret & TR_NET_CLOSE )
537        {
538            fprintf( stderr, "Could not connect to tracker\n" );
539            tr_netClose( s );
540            return 1;
541        }
542        else if( ret & TR_NET_BLOCK )
543        {
544            if( tr_date() > date + 10000 )
545            {
546                fprintf( stderr, "Could not connect to tracker\n" );
547                tr_netClose( s );
548                return 1;
549            }
550        }
551        else
552        {
553            break;
554        }
555        tr_wait( 10 );
556    }
557
558    pos = 0;
559    for( date = tr_date();; )
560    {
561        ret = tr_netRecv( s, &buf[pos], sizeof( buf ) - pos );
562        if( ret & TR_NET_CLOSE )
563        {
564            break;
565        }
566        else if( ret & TR_NET_BLOCK )
567        {
568            if( tr_date() > date + 10000 )
569            {
570                fprintf( stderr, "Could not read from tracker\n" );
571                tr_netClose( s );
572                return 1;
573            }
574        }
575        else
576        {
577            pos += ret;
578        }
579        tr_wait( 10 );
580    }
581
582    if( pos < 1 )
583    {
584        fprintf( stderr, "Could not read from tracker\n" );
585        tr_netClose( s );
586        return 1;
587    }
588
589    for( i = 0; i < pos - 8; i++ )
590    {
591        if( !memcmp( &buf[i], "d5:files", 8 ) )
592        {
593            break;
594        }
595    }
596    if( i >= pos - 8 )
597    {
598        return 1;
599    }
600    if( tr_bencLoad( &buf[i], &scrape, NULL ) )
601    {
602        return 1;
603    }
604
605    val1 = tr_bencDictFind( &scrape, "files" );
606    if( !val1 )
607    {
608        return 1;
609    }
610    val1 = &val1->val.l.vals[1];
611    if( !val1 )
612    {
613        return 1;
614    }
615    val2 = tr_bencDictFind( val1, "complete" );
616    if( !val2 )
617    {
618        return 1;
619    }
620    *seeders = val2->val.i;
621    val2 = tr_bencDictFind( val1, "incomplete" );
622    if( !val2 )
623    {
624        return 1;
625    }
626    *leechers = val2->val.i;
627    tr_bencFree( &scrape );
628
629    return 0;
630}
631
632int tr_trackerSeeders( tr_torrent_t * tor)
633{
634        if (tor->status != TR_STATUS_PAUSE)
635        {
636                return (tor->tracker)->seeders;
637        }
638        return 0;
639}
640
641int tr_trackerLeechers( tr_torrent_t * tor)
642{
643        if (tor->status != TR_STATUS_PAUSE) 
644        {
645                return (tor->tracker)->leechers;
646        }
647        return 0;
648}
Note: See TracBrowser for help on using the repository browser.