source: trunk/libtransmission/tracker.c @ 61

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

Removed now unused tables and some now unused code

File size: 15.3 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_handle_t * h, 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       = h->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->newPort  = -1;
79
80    return tc;
81}
82
83static int shouldConnect( tr_tracker_t * tc )
84{
85    uint64_t now = tr_date();
86
87    /* In any case, always wait 5 seconds between two requests */
88    if( now < tc->dateTry + 5000 )
89    {
90        return 0;
91    }
92
93    /* Do we need to send an event? */
94    if( tc->started || tc->completed || tc->stopped || 0 < tc->newPort )
95    {
96        return 1;
97    }
98
99    /* Should we try and get more peers? */
100    if( now > tc->dateOk + 1000 * tc->interval )
101    {
102        return 1;
103    }
104
105    /* If there is quite a lot of people on this torrent, stress
106       the tracker a bit until we get a decent number of peers */
107    if( tc->hasManyPeers )
108    {
109        if( tc->tor->peerCount < 5 && now > tc->dateOk + 10000 )
110        {
111            return 1;
112        }
113        if( tc->tor->peerCount < 10 && now > tc->dateOk + 20000 )
114        {
115            return 1;
116        }
117        if( tc->tor->peerCount < 15 && now > tc->dateOk + 30000 )
118        {
119            return 1;
120        }
121    }
122
123    return 0;
124}
125
126void tr_trackerChangePort( tr_tracker_t * tc, int port )
127{
128    tc->newPort = port;
129}
130
131int tr_trackerPulse( tr_tracker_t * tc )
132{
133    tr_torrent_t * tor = tc->tor;
134    tr_info_t    * inf = &tor->info;
135    uint64_t       now = tr_date();
136
137    if( ( tc->status & TC_STATUS_IDLE ) && shouldConnect( tc ) )
138    {
139        struct in_addr addr;
140
141        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
142        {
143            return 0;
144        }
145
146        if( tr_netResolve( inf->trackerAddress, &addr ) )
147        {
148            tr_fdSocketClosed( tor->fdlimit, 1 );
149            return 0;
150        }
151
152        tc->socket = tr_netOpen( addr, htons( inf->trackerPort ) );
153        if( tc->socket < 0 )
154        {
155            tr_fdSocketClosed( tor->fdlimit, 1 );
156            return 0;
157        }
158
159        tr_inf( "Tracker: connecting to %s:%d (%s)",
160                inf->trackerAddress, inf->trackerPort,
161                tc->started ? "sending 'started'" :
162                ( tc->completed ? "sending 'completed'" :
163                  ( tc->stopped ? "sending 'stopped'" :
164                    ( 0 < tc->newPort ? "sending 'stopped' to change port" :
165                      "getting peers" ) ) ) );
166
167        tc->status  = TC_STATUS_CONNECT;
168        tc->dateTry = tr_date();
169    }
170
171    if( tc->status & TC_STATUS_CONNECT )
172    {
173        /* We are connecting to the tracker. Try to send the query */
174        sendQuery( tc );
175    }
176
177    if( tc->status & TC_STATUS_RECV )
178    {
179        /* Try to get something */
180        recvAnswer( tc );
181    }
182
183    if( tc->status > TC_STATUS_IDLE && now > tc->dateTry + 60000 )
184    {
185        /* Give up if the request wasn't successful within 60 seconds */
186        tr_inf( "Tracker: timeout reached (60 s)" );
187
188        tr_netClose( tc->socket );
189        tr_fdSocketClosed( tor->fdlimit, 1 );
190
191        tc->status  = TC_STATUS_IDLE;
192        tc->dateTry = tr_date();
193    }
194
195    return 0;
196}
197
198void tr_trackerCompleted( tr_tracker_t * tc )
199{
200    tc->started   = 0;
201    tc->completed = 1;
202    tc->stopped   = 0;
203}
204
205void tr_trackerStopped( tr_tracker_t * tc )
206{
207    tr_torrent_t * tor = tc->tor;
208
209    if( tc->status > TC_STATUS_CONNECT )
210    {
211        /* If we are already sendy a query at the moment, we need to
212           reconnect */
213        tr_netClose( tc->socket );
214        tr_fdSocketClosed( tor->fdlimit, 1 );
215        tc->status = TC_STATUS_IDLE;
216    }
217
218    tc->started   = 0;
219    tc->completed = 0;
220    tc->stopped   = 1;
221
222    /* Even if we have connected recently, reconnect right now */
223    if( tc->status & TC_STATUS_IDLE )
224    {
225        tc->dateTry = 0;
226    }
227}
228
229void tr_trackerClose( tr_tracker_t * tc )
230{
231    tr_torrent_t * tor = tc->tor;
232
233    if( tc->status > TC_STATUS_IDLE )
234    {
235        tr_netClose( tc->socket );
236        tr_fdSocketClosed( tor->fdlimit, 1 );
237    }
238    free( tc->buf );
239    free( tc );
240}
241
242static void sendQuery( tr_tracker_t * tc )
243{
244    tr_torrent_t * tor = tc->tor;
245    tr_info_t    * inf = &tor->info;
246
247    char     * event;
248    uint64_t   left;
249    int        ret;
250
251    if( tc->started && 0 < tc->newPort )
252    {
253        tc->bindPort = tc->newPort;
254        tc->newPort = -1;
255    }
256
257    if( tc->started )
258        event = "&event=started";
259    else if( tc->completed )
260        event = "&event=completed";
261    else if( tc->stopped || 0 < tc->newPort )
262        event = "&event=stopped";
263    else
264        event = "";
265
266    left = tr_cpLeftBytes( tor->completion );
267
268    ret = snprintf( (char *) tc->buf, tc->size,
269            "GET %s?"
270            "info_hash=%s&"
271            "peer_id=%s&"
272            "port=%d&"
273            "uploaded=%lld&"
274            "downloaded=%lld&"
275            "left=%lld&"
276            "compact=1&"
277            "numwant=50&"
278            "key=%s"
279            "%s "
280            "HTTP/1.1\r\n"
281            "Host: %s\r\n"
282            "User-Agent: Transmission/%d.%d\r\n"
283            "Connection: close\r\n\r\n",
284            inf->trackerAnnounce, tor->hashString, tc->id,
285            tc->bindPort, tor->uploaded, tor->downloaded,
286            left, tor->key, event, inf->trackerAddress,
287            VERSION_MAJOR, VERSION_MINOR );
288
289    ret = tr_netSend( tc->socket, tc->buf, ret );
290    if( ret & TR_NET_CLOSE )
291    {
292        tr_inf( "Tracker: connection failed" );
293        tr_netClose( tc->socket );
294        tr_fdSocketClosed( tor->fdlimit, 1 );
295        tc->status  = TC_STATUS_IDLE;
296        tc->dateTry = tr_date();
297    }
298    else if( !( ret & TR_NET_BLOCK ) )
299    {
300        // printf( "Tracker: sent %s", tc->buf );
301        tc->status = TC_STATUS_RECV;
302        tc->pos    = 0;
303    }
304}
305
306static void recvAnswer( tr_tracker_t * tc )
307{
308    tr_torrent_t * tor = tc->tor;
309    int ret;
310    int i;
311    benc_val_t   beAll;
312    benc_val_t * bePeers, * beFoo;
313
314    if( tc->pos == tc->size )
315    {
316        tc->size *= 2;
317        tc->buf   = realloc( tc->buf, tc->size );
318    }
319   
320    ret = tr_netRecv( tc->socket, &tc->buf[tc->pos],
321                    tc->size - tc->pos );
322
323    if( ret & TR_NET_BLOCK )
324    {
325        return;
326    }
327    if( !( ret & TR_NET_CLOSE ) )
328    {
329        // printf( "got %d bytes\n", ret );
330        tc->pos += ret;
331        return;
332    }
333
334    tr_netClose( tc->socket );
335    tr_fdSocketClosed( tor->fdlimit, 1 );
336    // printf( "connection closed, got total %d bytes\n", tc->pos );
337
338    tc->status  = TC_STATUS_IDLE;
339    tc->dateTry = tr_date();
340
341    if( tc->pos < 1 )
342    {
343        /* We got nothing */
344        return;
345    }
346
347    /* Find the beginning of the dictionary */
348    for( i = 0; i < tc->pos - 18; i++ )
349    {
350        /* Hem */
351        if( !memcmp( &tc->buf[i], "d8:interval", 11 ) ||
352            !memcmp( &tc->buf[i], "d8:complete", 11 ) ||
353            !memcmp( &tc->buf[i], "d14:failure reason", 18 ) )
354        {
355            break;
356        }
357    }
358
359    if( i >= tc->pos - 18 )
360    {
361        tr_err( "Tracker: no dictionary in answer" );
362        // printf( "%s\n", tc->buf );
363        return;
364    }
365
366    if( tr_bencLoad( &tc->buf[i], &beAll, NULL ) )
367    {
368        tr_err( "Tracker: error parsing bencoded data" );
369        return;
370    }
371
372    // tr_bencPrint( &beAll );
373
374    if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) )
375    {
376        tr_err( "Tracker: %s", bePeers->val.s.s );
377        tor->status |= TR_TRACKER_ERROR;
378        snprintf( tor->error, sizeof( tor->error ),
379                  "%s", bePeers->val.s.s );
380        goto cleanup;
381    }
382
383    tor->status &= ~TR_TRACKER_ERROR;
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.