transmission 0.1.0

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