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

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

Quick 'n dirty fix so incoming connections work again

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_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 = *(tor->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->error |= TR_ETRACKER;
378        snprintf( tor->trackerError, sizeof( tor->trackerError ),
379                  "%s", bePeers->val.s.s );
380        goto cleanup;
381    }
382    tor->error &= ~TR_ETRACKER;
383
384    if( !tc->interval )
385    {
386        /* Get the tracker interval, ignore it if it is not between
387           10 sec and 5 mins */
388        if( !( beFoo = tr_bencDictFind( &beAll, "interval" ) ) ||
389            !( beFoo->type & TYPE_INT ) )
390        {
391            tr_err( "Tracker: no 'interval' field" );
392            goto cleanup;
393        }
394
395        tc->interval = beFoo->val.i;
396        tc->interval = MIN( tc->interval, 300 );
397        tc->interval = MAX( 10, tc->interval );
398
399        tr_inf( "Tracker: interval = %d seconds", tc->interval );
400    }
401
402    if( ( beFoo = tr_bencDictFind( &beAll, "complete" ) ) &&
403        ( beFoo->type & TYPE_INT ) )
404    {
405        tc->seeders = beFoo->val.i;
406    }
407    if( ( beFoo = tr_bencDictFind( &beAll, "incomplete" ) ) &&
408        ( beFoo->type & TYPE_INT ) )
409    {
410        tc->leechers = beFoo->val.i;
411    }
412    if( tc->seeders + tc->leechers >= 50 )
413    {
414        tc->hasManyPeers = 1;
415    }
416
417    if( !( bePeers = tr_bencDictFind( &beAll, "peers" ) ) )
418    {
419        tr_err( "Tracker: no \"peers\" field" );
420        goto cleanup;
421    }
422
423    if( bePeers->type & TYPE_LIST )
424    {
425        char * ip;
426        int    port;
427
428        /* Original protocol */
429        tr_inf( "Tracker: got %d peers", bePeers->val.l.count );
430
431        for( i = 0; i < bePeers->val.l.count; i++ )
432        {
433            beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "ip" );
434            if( !beFoo )
435                continue;
436            ip = beFoo->val.s.s;
437            beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "port" );
438            if( !beFoo )
439                continue;
440            port = beFoo->val.i;
441
442            tr_peerAddOld( tor, ip, port );
443        }
444
445        if( bePeers->val.l.count >= 50 )
446        {
447            tc->hasManyPeers = 1;
448        }
449    }
450    else if( bePeers->type & TYPE_STR )
451    {
452        struct in_addr addr;
453        in_port_t      port;
454
455        /* "Compact" extension */
456        if( bePeers->val.s.i % 6 )
457        {
458            tr_err( "Tracker: \"peers\" of size %d",
459                    bePeers->val.s.i );
460            tr_lockUnlock( &tor->lock );
461            goto cleanup;
462        }
463
464        tr_inf( "Tracker: got %d peers", bePeers->val.s.i / 6 );
465        for( i = 0; i < bePeers->val.s.i / 6; i++ )
466        {
467            memcpy( &addr, &bePeers->val.s.s[6*i],   4 );
468            memcpy( &port, &bePeers->val.s.s[6*i+4], 2 );
469
470            tr_peerAddCompact( tor, addr, port );
471        }
472
473        if( bePeers->val.s.i / 6 >= 50 )
474        {
475            tc->hasManyPeers = 1;
476        }
477    }
478
479    /* Success */
480    tc->started   = 0;
481    tc->completed = 0;
482    tc->dateOk    = tr_date();
483
484    if( tc->stopped )
485    {
486        tor->status = TR_STATUS_STOPPED;
487        tc->stopped = 0;
488    }
489    else if( 0 < tc->newPort )
490    {
491        tc->started = 1;
492    }
493
494cleanup:
495    tr_bencFree( &beAll );
496}
497
498int tr_trackerScrape( tr_torrent_t * tor, int * seeders, int * leechers )
499{
500    tr_info_t * inf = &tor->info;
501
502    int s, i, ret;
503    uint8_t buf[1024];
504    benc_val_t scrape, * val1, * val2;
505    struct in_addr addr;
506    uint64_t date;
507    int pos, len;
508
509    if( !tor->scrape[0] )
510    {
511        /* scrape not supported */
512        return 1;
513    }
514
515    if( tr_netResolve( inf->trackerAddress, &addr ) )
516    {
517        return 0;
518    }
519    s = tr_netOpen( addr, htons( inf->trackerPort ) );
520    if( s < 0 )
521    {
522        return 1;
523    }
524
525    len = snprintf( (char *) buf, sizeof( buf ),
526              "GET %s?info_hash=%s HTTP/1.1\r\n"
527              "Host: %s\r\n"
528              "Connection: close\r\n\r\n",
529              tor->scrape, tor->hashString,
530              inf->trackerAddress );
531
532    for( date = tr_date();; )
533    {
534        ret = tr_netSend( s, buf, len );
535        if( ret & TR_NET_CLOSE )
536        {
537            fprintf( stderr, "Could not connect to tracker\n" );
538            tr_netClose( s );
539            return 1;
540        }
541        else if( ret & TR_NET_BLOCK )
542        {
543            if( tr_date() > date + 10000 )
544            {
545                fprintf( stderr, "Could not connect to tracker\n" );
546                tr_netClose( s );
547                return 1;
548            }
549        }
550        else
551        {
552            break;
553        }
554        tr_wait( 10 );
555    }
556
557    pos = 0;
558    for( date = tr_date();; )
559    {
560        ret = tr_netRecv( s, &buf[pos], sizeof( buf ) - pos );
561        if( ret & TR_NET_CLOSE )
562        {
563            break;
564        }
565        else if( ret & TR_NET_BLOCK )
566        {
567            if( tr_date() > date + 10000 )
568            {
569                fprintf( stderr, "Could not read from tracker\n" );
570                tr_netClose( s );
571                return 1;
572            }
573        }
574        else
575        {
576            pos += ret;
577        }
578        tr_wait( 10 );
579    }
580
581    if( pos < 1 )
582    {
583        fprintf( stderr, "Could not read from tracker\n" );
584        tr_netClose( s );
585        return 1;
586    }
587
588    for( i = 0; i < pos - 8; i++ )
589    {
590        if( !memcmp( &buf[i], "d5:files", 8 ) )
591        {
592            break;
593        }
594    }
595    if( i >= pos - 8 )
596    {
597        return 1;
598    }
599    if( tr_bencLoad( &buf[i], &scrape, NULL ) )
600    {
601        return 1;
602    }
603
604    val1 = tr_bencDictFind( &scrape, "files" );
605    if( !val1 )
606    {
607        return 1;
608    }
609    val1 = &val1->val.l.vals[1];
610    if( !val1 )
611    {
612        return 1;
613    }
614    val2 = tr_bencDictFind( val1, "complete" );
615    if( !val2 )
616    {
617        return 1;
618    }
619    *seeders = val2->val.i;
620    val2 = tr_bencDictFind( val1, "incomplete" );
621    if( !val2 )
622    {
623        return 1;
624    }
625    *leechers = val2->val.i;
626    tr_bencFree( &scrape );
627
628    return 0;
629}
630
631int tr_trackerSeeders( tr_torrent_t * tor)
632{
633        if (tor->status != TR_STATUS_PAUSE)
634        {
635                return (tor->tracker)->seeders;
636        }
637        return 0;
638}
639
640int tr_trackerLeechers( tr_torrent_t * tor)
641{
642        if (tor->status != TR_STATUS_PAUSE) 
643        {
644                return (tor->tracker)->leechers;
645        }
646        return 0;
647}
Note: See TracBrowser for help on using the repository browser.