transmission 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,584 @@
1
+ /******************************************************************************
2
+ * $Id: peer.c 261 2006-05-29 21:27:31Z 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
+ #define MAX_REQUEST_COUNT 32
28
+ #define OUR_REQUEST_COUNT 8 /* TODO: we should detect if we are on a
29
+ high-speed network and adapt */
30
+
31
+ typedef struct tr_request_s
32
+ {
33
+ int index;
34
+ int begin;
35
+ int length;
36
+
37
+ } tr_request_t;
38
+
39
+ struct tr_peer_s
40
+ {
41
+ struct in_addr addr;
42
+ in_port_t port;
43
+
44
+ #define PEER_STATUS_IDLE 1 /* Need to connect */
45
+ #define PEER_STATUS_CONNECTING 2 /* Trying to send handshake */
46
+ #define PEER_STATUS_HANDSHAKE 4 /* Waiting for peer's handshake */
47
+ #define PEER_STATUS_CONNECTED 8 /* Got peer's handshake */
48
+ int status;
49
+ int socket;
50
+ uint64_t date;
51
+ uint64_t keepAlive;
52
+
53
+ char amChoking;
54
+ char amInterested;
55
+ char peerChoking;
56
+ char peerInterested;
57
+
58
+ int optimistic;
59
+ uint64_t lastChoke;
60
+
61
+ uint8_t id[20];
62
+
63
+ /* The pieces that the peer has */
64
+ uint8_t * bitfield;
65
+
66
+ int goodPcs;
67
+ int badPcs;
68
+ int banned;
69
+ /* The pieces that the peer is contributing to */
70
+ uint8_t * blamefield;
71
+ /* The bad pieces that the peer has contributed to */
72
+ uint8_t * banfield;
73
+
74
+ uint8_t * buf;
75
+ int size;
76
+ int pos;
77
+
78
+ uint8_t * outMessages;
79
+ int outMessagesSize;
80
+ int outMessagesPos;
81
+ uint8_t outBlock[13+16384];
82
+ int outBlockSize;
83
+ int outBlockLoaded;
84
+ int outBlockSending;
85
+
86
+ int inRequestCount;
87
+ tr_request_t inRequests[OUR_REQUEST_COUNT];
88
+ int inIndex;
89
+ int inBegin;
90
+ int inLength;
91
+ uint64_t inTotal;
92
+
93
+ int outRequestCount;
94
+ tr_request_t outRequests[MAX_REQUEST_COUNT];
95
+ uint64_t outTotal;
96
+ uint64_t outDate;
97
+ int outSlow;
98
+
99
+ tr_ratecontrol_t * download;
100
+ };
101
+
102
+ #define peer_dbg( a... ) __peer_dbg( peer, ## a )
103
+ static void __peer_dbg( tr_peer_t * peer, char * msg, ... )
104
+ {
105
+ char string[256];
106
+ va_list args;
107
+
108
+ va_start( args, msg );
109
+ sprintf( string, "%08x:%04x ",
110
+ (uint32_t) peer->addr.s_addr, peer->port );
111
+ vsnprintf( &string[14], sizeof( string ) - 14, msg, args );
112
+ va_end( args );
113
+
114
+ tr_dbg( "%s", string );
115
+ }
116
+
117
+ #include "peermessages.h"
118
+ #include "peerutils.h"
119
+ #include "peerparse.h"
120
+
121
+ /***********************************************************************
122
+ * tr_peerAddOld
123
+ ***********************************************************************
124
+ * Tries to add a peer given its IP and port (received from a tracker
125
+ * which doesn't support the "compact" extension).
126
+ **********************************************************************/
127
+ void tr_peerAddOld( tr_torrent_t * tor, char * ip, int port )
128
+ {
129
+ struct in_addr addr;
130
+
131
+ if( tr_netResolve( ip, &addr ) )
132
+ {
133
+ return;
134
+ }
135
+
136
+ addWithAddr( tor, addr, htons( port ) );
137
+ }
138
+
139
+ /***********************************************************************
140
+ * tr_peerAddCompact
141
+ ***********************************************************************
142
+ * Tries to add a peer, using 'addr' and 'port' to connect to the peer.
143
+ **********************************************************************/
144
+ void tr_peerAddCompact( tr_torrent_t * tor, struct in_addr addr,
145
+ in_port_t port )
146
+ {
147
+ addWithAddr( tor, addr, port );
148
+ }
149
+
150
+ /***********************************************************************
151
+ * tr_peerInit
152
+ ***********************************************************************
153
+ * Initializes a new peer.
154
+ **********************************************************************/
155
+ tr_peer_t * tr_peerInit( struct in_addr addr, in_port_t port, int s )
156
+ {
157
+ tr_peer_t * peer = peerInit();
158
+
159
+ peer->socket = s;
160
+ peer->addr = addr;
161
+ peer->port = port;
162
+ peer->status = PEER_STATUS_CONNECTING;
163
+
164
+ return peer;
165
+ }
166
+
167
+ void tr_peerAttach( tr_torrent_t * tor, tr_peer_t * peer )
168
+ {
169
+ peerAttach( tor, peer );
170
+ }
171
+
172
+ void tr_peerDestroy( tr_fd_t * fdlimit, tr_peer_t * peer )
173
+ {
174
+ if( peer->bitfield )
175
+ {
176
+ free( peer->bitfield );
177
+ }
178
+ if( peer->blamefield )
179
+ {
180
+ free( peer->blamefield );
181
+ }
182
+ if( peer->banfield )
183
+ {
184
+ free( peer->banfield );
185
+ }
186
+ if( peer->buf )
187
+ {
188
+ free( peer->buf );
189
+ }
190
+ if( peer->outMessages )
191
+ {
192
+ free( peer->outMessages );
193
+ }
194
+ if( peer->status > PEER_STATUS_IDLE )
195
+ {
196
+ tr_netClose( peer->socket );
197
+ tr_fdSocketClosed( fdlimit, 0 );
198
+ }
199
+ tr_rcClose( peer->download );
200
+ free( peer );
201
+ }
202
+
203
+ /***********************************************************************
204
+ * tr_peerRem
205
+ ***********************************************************************
206
+ * Frees and closes everything related to the peer at index 'i', and
207
+ * removes it from the peers list.
208
+ **********************************************************************/
209
+ void tr_peerRem( tr_torrent_t * tor, int i )
210
+ {
211
+ tr_peer_t * peer = tor->peers[i];
212
+ int j;
213
+
214
+ for( j = 0; j < peer->inRequestCount; j++ )
215
+ {
216
+ tr_request_t * r;
217
+ int block;
218
+
219
+ r = &peer->inRequests[j];
220
+ block = tr_block( r->index,r->begin );
221
+ tr_cpDownloaderRem( tor->completion, block );
222
+ }
223
+ tr_peerDestroy( tor->fdlimit, peer );
224
+ tor->peerCount--;
225
+ memmove( &tor->peers[i], &tor->peers[i+1],
226
+ ( tor->peerCount - i ) * sizeof( tr_peer_t * ) );
227
+ }
228
+
229
+ /***********************************************************************
230
+ * tr_peerRead
231
+ ***********************************************************************
232
+ *
233
+ **********************************************************************/
234
+ int tr_peerRead( tr_torrent_t * tor, tr_peer_t * peer )
235
+ {
236
+ int ret;
237
+
238
+ /* Try to read */
239
+ for( ;; )
240
+ {
241
+ if( tor && !tr_rcCanTransfer( tor->globalDownload ) )
242
+ {
243
+ break;
244
+ }
245
+
246
+ if( peer->size < 1 )
247
+ {
248
+ peer->size = 1024;
249
+ peer->buf = malloc( peer->size );
250
+ }
251
+ else if( peer->pos >= peer->size )
252
+ {
253
+ peer->size *= 2;
254
+ peer->buf = realloc( peer->buf, peer->size );
255
+ }
256
+ /* Never read more than 1K each time, otherwise the rate
257
+ control is no use */
258
+ ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
259
+ MIN( 1024, peer->size - peer->pos ) );
260
+ if( ret & TR_NET_CLOSE )
261
+ {
262
+ peer_dbg( "connection closed" );
263
+ return 1;
264
+ }
265
+ else if( ret & TR_NET_BLOCK )
266
+ {
267
+ break;
268
+ }
269
+ peer->date = tr_date();
270
+ peer->pos += ret;
271
+ if( NULL != tor )
272
+ {
273
+ tr_rcTransferred( peer->download, ret );
274
+ tr_rcTransferred( tor->download, ret );
275
+ tr_rcTransferred( tor->globalDownload, ret );
276
+ if( parseBuf( tor, peer ) )
277
+ {
278
+ return 1;
279
+ }
280
+ }
281
+ else
282
+ {
283
+ if( parseBufHeader( peer ) )
284
+ {
285
+ return 1;
286
+ }
287
+ }
288
+ }
289
+
290
+ return 0;
291
+ }
292
+
293
+ uint64_t tr_peerDate( tr_peer_t * peer )
294
+ {
295
+ return peer->date;
296
+ }
297
+
298
+ /***********************************************************************
299
+ * tr_peerHash
300
+ ***********************************************************************
301
+ *
302
+ **********************************************************************/
303
+ uint8_t * tr_peerHash( tr_peer_t * peer )
304
+ {
305
+ return parseBufHash( peer );
306
+ }
307
+
308
+ /***********************************************************************
309
+ * tr_peerPulse
310
+ ***********************************************************************
311
+ *
312
+ **********************************************************************/
313
+ void tr_peerPulse( tr_torrent_t * tor )
314
+ {
315
+ int i, ret, size;
316
+ uint8_t * p;
317
+ tr_peer_t * peer;
318
+
319
+ if( tr_date() > tor->date + 1000 )
320
+ {
321
+ tor->date = tr_date();
322
+
323
+ for( i = 0; i < tor->peerCount; )
324
+ {
325
+ if( checkPeer( tor, i ) )
326
+ {
327
+ tr_peerRem( tor, i );
328
+ continue;
329
+ }
330
+ i++;
331
+ }
332
+ }
333
+
334
+ if( tor->status & TR_STATUS_STOPPING )
335
+ {
336
+ return;
337
+ }
338
+
339
+ /* Shuffle peers */
340
+ if( tor->peerCount > 1 )
341
+ {
342
+ peer = tor->peers[0];
343
+ memmove( &tor->peers[0], &tor->peers[1],
344
+ ( tor->peerCount - 1 ) * sizeof( void * ) );
345
+ tor->peers[tor->peerCount - 1] = peer;
346
+ }
347
+
348
+ /* Handle peers */
349
+ for( i = 0; i < tor->peerCount; )
350
+ {
351
+ peer = tor->peers[i];
352
+
353
+ if( peer->status < PEER_STATUS_HANDSHAKE )
354
+ {
355
+ i++;
356
+ continue;
357
+ }
358
+
359
+ if( tr_peerRead( tor, tor->peers[i] ) )
360
+ {
361
+ goto dropPeer;
362
+ }
363
+
364
+ if( peer->status < PEER_STATUS_CONNECTED )
365
+ {
366
+ i++;
367
+ continue;
368
+ }
369
+
370
+ /* Try to write */
371
+ writeBegin:
372
+
373
+ /* Send all smaller messages regardless of the upload cap */
374
+ while( ( p = messagesPending( peer, &size ) ) )
375
+ {
376
+ ret = tr_netSend( peer->socket, p, size );
377
+ if( ret & TR_NET_CLOSE )
378
+ {
379
+ goto dropPeer;
380
+ }
381
+ else if( ret & TR_NET_BLOCK )
382
+ {
383
+ goto writeEnd;
384
+ }
385
+ messagesSent( peer, ret );
386
+ }
387
+
388
+ /* Send pieces if we can */
389
+ while( ( p = blockPending( tor, peer, &size ) ) )
390
+ {
391
+ if( !tr_rcCanTransfer( tor->globalUpload ) )
392
+ {
393
+ break;
394
+ }
395
+
396
+ ret = tr_netSend( peer->socket, p, size );
397
+ if( ret & TR_NET_CLOSE )
398
+ {
399
+ goto dropPeer;
400
+ }
401
+ else if( ret & TR_NET_BLOCK )
402
+ {
403
+ break;
404
+ }
405
+
406
+ blockSent( peer, ret );
407
+ tr_rcTransferred( tor->upload, ret );
408
+ tr_rcTransferred( tor->globalUpload, ret );
409
+
410
+ tor->uploaded += ret;
411
+ peer->outTotal += ret;
412
+ peer->outDate = tr_date();
413
+
414
+ /* In case this block is done, you may have messages
415
+ pending. Send them before we start the next block */
416
+ goto writeBegin;
417
+ }
418
+ writeEnd:
419
+
420
+ /* Ask for a block whenever possible */
421
+ if( !tr_cpIsSeeding( tor->completion ) &&
422
+ !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
423
+ {
424
+ /* This peer is no use to us, and it seems there are
425
+ more */
426
+ peer_dbg( "not interesting" );
427
+ tr_peerRem( tor, i );
428
+ continue;
429
+ }
430
+
431
+ if( peer->amInterested && !peer->peerChoking && !peer->banned )
432
+ {
433
+ int block;
434
+ while( peer->inRequestCount < OUR_REQUEST_COUNT )
435
+ {
436
+ block = chooseBlock( tor, peer );
437
+ if( block < 0 )
438
+ {
439
+ break;
440
+ }
441
+ sendRequest( tor, peer, block );
442
+ }
443
+ }
444
+
445
+ i++;
446
+ continue;
447
+
448
+ dropPeer:
449
+ tr_peerRem( tor, i );
450
+ }
451
+ }
452
+
453
+ /***********************************************************************
454
+ * tr_peerIsConnected
455
+ ***********************************************************************
456
+ *
457
+ **********************************************************************/
458
+ int tr_peerIsConnected( tr_peer_t * peer )
459
+ {
460
+ return peer->status & PEER_STATUS_CONNECTED;
461
+ }
462
+
463
+ /***********************************************************************
464
+ * tr_peerIsUploading
465
+ ***********************************************************************
466
+ *
467
+ **********************************************************************/
468
+ int tr_peerIsUploading( tr_peer_t * peer )
469
+ {
470
+ return ( peer->inRequestCount > 0 );
471
+ }
472
+
473
+ /***********************************************************************
474
+ * tr_peerIsDownloading
475
+ ***********************************************************************
476
+ *
477
+ **********************************************************************/
478
+ int tr_peerIsDownloading( tr_peer_t * peer )
479
+ {
480
+ return peer->outBlockSending;
481
+ }
482
+
483
+ /***********************************************************************
484
+ * tr_peerBitfield
485
+ ***********************************************************************
486
+ *
487
+ **********************************************************************/
488
+ uint8_t * tr_peerBitfield( tr_peer_t * peer )
489
+ {
490
+ return peer->bitfield;
491
+ }
492
+
493
+ float tr_peerDownloadRate( tr_peer_t * peer )
494
+ {
495
+ return tr_rcRate( peer->download );
496
+ }
497
+
498
+ int tr_peerIsUnchoked( tr_peer_t * peer )
499
+ {
500
+ return !peer->amChoking;
501
+ }
502
+
503
+ int tr_peerIsInterested ( tr_peer_t * peer )
504
+ {
505
+ return peer->peerInterested;
506
+ }
507
+
508
+ void tr_peerChoke( tr_peer_t * peer )
509
+ {
510
+ sendChoke( peer, 1 );
511
+ peer->lastChoke = tr_date();
512
+ }
513
+
514
+ void tr_peerUnchoke( tr_peer_t * peer )
515
+ {
516
+ sendChoke( peer, 0 );
517
+ peer->lastChoke = tr_date();
518
+ }
519
+
520
+ uint64_t tr_peerLastChoke( tr_peer_t * peer )
521
+ {
522
+ return peer->lastChoke;
523
+ }
524
+
525
+ void tr_peerSetOptimistic( tr_peer_t * peer, int o )
526
+ {
527
+ peer->optimistic = o;
528
+ }
529
+
530
+ int tr_peerIsOptimistic( tr_peer_t * peer )
531
+ {
532
+ return peer->optimistic;
533
+ }
534
+
535
+ static inline int peerIsBad( tr_peer_t * peer )
536
+ {
537
+ return ( peer->badPcs > 4 + 2 * peer->goodPcs );
538
+ }
539
+
540
+ static inline int peerIsGood( tr_peer_t * peer )
541
+ {
542
+ return ( peer->goodPcs > 3 * peer->badPcs );
543
+ }
544
+
545
+ void tr_peerBlame( tr_torrent_t * tor, tr_peer_t * peer,
546
+ int piece, int success )
547
+ {
548
+ if( !peer->blamefield || !tr_bitfieldHas( peer->blamefield, piece ) )
549
+ {
550
+ return;
551
+ }
552
+
553
+ if( success )
554
+ {
555
+ peer->goodPcs++;
556
+
557
+ if( peer->banfield && peerIsGood( peer ) )
558
+ {
559
+ /* Assume the peer wasn't responsible for the bad pieces
560
+ we was banned for */
561
+ memset( peer->banfield, 0x00, ( tor->info.pieceCount + 7 ) / 8 );
562
+ }
563
+ }
564
+ else
565
+ {
566
+ peer->badPcs++;
567
+
568
+ /* Ban the peer for this piece */
569
+ if( !peer->banfield )
570
+ {
571
+ peer->banfield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
572
+ }
573
+ tr_bitfieldAdd( peer->banfield, piece );
574
+
575
+ if( peerIsBad( peer ) )
576
+ {
577
+ /* Full ban */
578
+ peer_dbg( "banned (%d / %d)", peer->goodPcs, peer->badPcs );
579
+ peer->banned = 1;
580
+ peer->peerInterested = 0;
581
+ }
582
+ }
583
+ tr_bitfieldRem( peer->blamefield, piece );
584
+ }