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.
- data/CHANGELOG +0 -0
- data/LICENSE +23 -0
- data/README +50 -0
- data/Rakefile +41 -0
- data/ext/bencode.c +335 -0
- data/ext/bencode.h +64 -0
- data/ext/choking.c +335 -0
- data/ext/choking.h +30 -0
- data/ext/clients.c +90 -0
- data/ext/clients.h +25 -0
- data/ext/completion.c +266 -0
- data/ext/completion.h +67 -0
- data/ext/extconf.rb +3 -0
- data/ext/fastresume.h +375 -0
- data/ext/fdlimit.c +297 -0
- data/ext/fdlimit.h +36 -0
- data/ext/inout.c +620 -0
- data/ext/inout.h +38 -0
- data/ext/internal.h +202 -0
- data/ext/metainfo.c +339 -0
- data/ext/metainfo.h +44 -0
- data/ext/net.c +405 -0
- data/ext/net.h +54 -0
- data/ext/peer.c +584 -0
- data/ext/peer.h +55 -0
- data/ext/peermessages.h +326 -0
- data/ext/peerparse.h +564 -0
- data/ext/peerutils.h +394 -0
- data/ext/platform.c +196 -0
- data/ext/platform.h +63 -0
- data/ext/r_transmission.c +1135 -0
- data/ext/ratecontrol.c +170 -0
- data/ext/ratecontrol.h +33 -0
- data/ext/sha1.c +235 -0
- data/ext/sha1.h +68 -0
- data/ext/tracker.c +767 -0
- data/ext/tracker.h +53 -0
- data/ext/transmission.c +776 -0
- data/ext/transmission.h +317 -0
- data/ext/utils.c +142 -0
- data/ext/utils.h +164 -0
- data/lib/transmission.rb +75 -0
- metadata +98 -0
data/ext/peer.h
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
/******************************************************************************
|
2
|
+
* $Id: peer.h 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
|
+
#ifndef TR_PEER_H
|
26
|
+
#define TR_PEER_H 1
|
27
|
+
|
28
|
+
typedef struct tr_peer_s tr_peer_t;
|
29
|
+
|
30
|
+
void tr_peerAddOld ( tr_torrent_t *, char *, int );
|
31
|
+
void tr_peerAddCompact ( tr_torrent_t *, struct in_addr, in_port_t );
|
32
|
+
tr_peer_t * tr_peerInit ( struct in_addr, in_port_t, int );
|
33
|
+
void tr_peerAttach ( tr_torrent_t *, tr_peer_t * );
|
34
|
+
void tr_peerDestroy ( tr_fd_t *, tr_peer_t * );
|
35
|
+
void tr_peerRem ( tr_torrent_t *, int );
|
36
|
+
int tr_peerRead ( tr_torrent_t *, tr_peer_t * );
|
37
|
+
uint64_t tr_peerDate ( tr_peer_t * );
|
38
|
+
uint8_t * tr_peerHash ( tr_peer_t * );
|
39
|
+
void tr_peerPulse ( tr_torrent_t * );
|
40
|
+
int tr_peerIsConnected ( tr_peer_t * );
|
41
|
+
int tr_peerIsUploading ( tr_peer_t * );
|
42
|
+
int tr_peerIsDownloading ( tr_peer_t * );
|
43
|
+
uint8_t * tr_peerBitfield ( tr_peer_t * );
|
44
|
+
float tr_peerDownloadRate ( tr_peer_t * );
|
45
|
+
int tr_peerIsUnchoked ( tr_peer_t * );
|
46
|
+
int tr_peerIsInterested ( tr_peer_t * );
|
47
|
+
void tr_peerChoke ( tr_peer_t * );
|
48
|
+
void tr_peerUnchoke ( tr_peer_t * );
|
49
|
+
uint64_t tr_peerLastChoke ( tr_peer_t * );
|
50
|
+
void tr_peerSetOptimistic ( tr_peer_t *, int );
|
51
|
+
int tr_peerIsOptimistic ( tr_peer_t * );
|
52
|
+
void tr_peerBlame ( tr_torrent_t *, tr_peer_t *,
|
53
|
+
int piece, int success );
|
54
|
+
|
55
|
+
#endif
|
data/ext/peermessages.h
ADDED
@@ -0,0 +1,326 @@
|
|
1
|
+
/******************************************************************************
|
2
|
+
* $Id: peermessages.h 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
|
+
/***********************************************************************
|
26
|
+
* This file handles all outgoing messages
|
27
|
+
**********************************************************************/
|
28
|
+
|
29
|
+
static uint8_t * messagesPending( tr_peer_t * peer, int * size )
|
30
|
+
{
|
31
|
+
if( peer->outBlockSending || peer->outMessagesPos < 1 )
|
32
|
+
{
|
33
|
+
return NULL;
|
34
|
+
}
|
35
|
+
|
36
|
+
*size = MIN( peer->outMessagesPos, 1024 );
|
37
|
+
|
38
|
+
return peer->outMessages;
|
39
|
+
}
|
40
|
+
|
41
|
+
static void messagesSent( tr_peer_t * peer, int size )
|
42
|
+
{
|
43
|
+
peer->outMessagesPos -= size;
|
44
|
+
memmove( peer->outMessages, &peer->outMessages[size],
|
45
|
+
peer->outMessagesPos );
|
46
|
+
}
|
47
|
+
|
48
|
+
static uint8_t * blockPending( tr_torrent_t * tor, tr_peer_t * peer,
|
49
|
+
int * size )
|
50
|
+
{
|
51
|
+
if( !peer->outBlockLoaded )
|
52
|
+
{
|
53
|
+
uint8_t * p;
|
54
|
+
tr_request_t * r;
|
55
|
+
|
56
|
+
if( peer->amChoking || peer->outRequestCount < 1 )
|
57
|
+
{
|
58
|
+
/* No piece to send */
|
59
|
+
return NULL;
|
60
|
+
}
|
61
|
+
|
62
|
+
/* We need to load the block for the next request */
|
63
|
+
r = &peer->outRequests[0];
|
64
|
+
|
65
|
+
/* Sanity check */
|
66
|
+
if( !tr_cpPieceIsComplete( tor->completion, r->index ) )
|
67
|
+
{
|
68
|
+
/* We have been asked for something we don't have, buggy client?
|
69
|
+
Let's just drop this request */
|
70
|
+
tr_inf( "Block %d/%d/%d was requested but we don't have it",
|
71
|
+
r->index, r->begin, r->length );
|
72
|
+
(peer->outRequestCount)--;
|
73
|
+
memmove( &peer->outRequests[0], &peer->outRequests[1],
|
74
|
+
peer->outRequestCount * sizeof( tr_request_t ) );
|
75
|
+
return NULL;
|
76
|
+
}
|
77
|
+
|
78
|
+
p = (uint8_t *) peer->outBlock;
|
79
|
+
|
80
|
+
TR_HTONL( 9 + r->length, p );
|
81
|
+
p[4] = 7;
|
82
|
+
TR_HTONL( r->index, p + 5 );
|
83
|
+
TR_HTONL( r->begin, p + 9 );
|
84
|
+
|
85
|
+
tr_ioRead( tor->io, r->index, r->begin, r->length, &p[13] );
|
86
|
+
|
87
|
+
peer_dbg( "SEND piece %d/%d (%d bytes)",
|
88
|
+
r->index, r->begin, r->length );
|
89
|
+
|
90
|
+
peer->outBlockSize = 13 + r->length;
|
91
|
+
peer->outBlockLoaded = 1;
|
92
|
+
|
93
|
+
(peer->outRequestCount)--;
|
94
|
+
memmove( &peer->outRequests[0], &peer->outRequests[1],
|
95
|
+
peer->outRequestCount * sizeof( tr_request_t ) );
|
96
|
+
}
|
97
|
+
|
98
|
+
*size = MIN( 1024, peer->outBlockSize );
|
99
|
+
|
100
|
+
return (uint8_t *) peer->outBlock;
|
101
|
+
}
|
102
|
+
|
103
|
+
static void blockSent( tr_peer_t * peer, int size )
|
104
|
+
{
|
105
|
+
peer->outBlockSize -= size;
|
106
|
+
memmove( peer->outBlock, &peer->outBlock[size], peer->outBlockSize );
|
107
|
+
|
108
|
+
if( peer->outBlockSize > 0 )
|
109
|
+
{
|
110
|
+
/* We can't send messages until we are done sending the block */
|
111
|
+
peer->outBlockSending = 1;
|
112
|
+
}
|
113
|
+
else
|
114
|
+
{
|
115
|
+
/* Block fully sent */
|
116
|
+
peer->outBlockSending = 0;
|
117
|
+
peer->outBlockLoaded = 0;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
static uint8_t * getPointerForSize( tr_peer_t * peer, int size )
|
122
|
+
{
|
123
|
+
uint8_t * p;
|
124
|
+
|
125
|
+
if( peer->outMessagesPos + size > peer->outMessagesSize )
|
126
|
+
{
|
127
|
+
peer->outMessagesSize = peer->outMessagesPos + size;
|
128
|
+
peer->outMessages = realloc( peer->outMessages,
|
129
|
+
peer->outMessagesSize );
|
130
|
+
}
|
131
|
+
|
132
|
+
p = &peer->outMessages[peer->outMessagesPos];
|
133
|
+
peer->outMessagesPos += size;
|
134
|
+
|
135
|
+
return p;
|
136
|
+
}
|
137
|
+
|
138
|
+
/***********************************************************************
|
139
|
+
* sendKeepAlive
|
140
|
+
***********************************************************************
|
141
|
+
*
|
142
|
+
**********************************************************************/
|
143
|
+
static void sendKeepAlive( tr_peer_t * peer )
|
144
|
+
{
|
145
|
+
uint8_t * p;
|
146
|
+
|
147
|
+
p = getPointerForSize( peer, 4 );
|
148
|
+
|
149
|
+
TR_HTONL( 0, p );
|
150
|
+
|
151
|
+
peer_dbg( "SEND keep-alive" );
|
152
|
+
}
|
153
|
+
|
154
|
+
|
155
|
+
/***********************************************************************
|
156
|
+
* sendChoke
|
157
|
+
***********************************************************************
|
158
|
+
*
|
159
|
+
**********************************************************************/
|
160
|
+
static void sendChoke( tr_peer_t * peer, int yes )
|
161
|
+
{
|
162
|
+
uint8_t * p;
|
163
|
+
|
164
|
+
p = getPointerForSize( peer, 5 );
|
165
|
+
|
166
|
+
TR_HTONL( 1, p );
|
167
|
+
p[4] = yes ? 0 : 1;
|
168
|
+
|
169
|
+
peer->amChoking = yes;
|
170
|
+
|
171
|
+
if( !yes )
|
172
|
+
{
|
173
|
+
/* Drop older requests from the last time it was unchoked,
|
174
|
+
if any */
|
175
|
+
peer->outRequestCount = 0;
|
176
|
+
}
|
177
|
+
|
178
|
+
peer_dbg( "SEND %schoke", yes ? "" : "un" );
|
179
|
+
}
|
180
|
+
|
181
|
+
/***********************************************************************
|
182
|
+
* sendInterest
|
183
|
+
***********************************************************************
|
184
|
+
*
|
185
|
+
**********************************************************************/
|
186
|
+
static void sendInterest( tr_peer_t * peer, int yes )
|
187
|
+
{
|
188
|
+
uint8_t * p;
|
189
|
+
|
190
|
+
p = getPointerForSize( peer, 5 );
|
191
|
+
|
192
|
+
TR_HTONL( 1, p );
|
193
|
+
p[4] = yes ? 2 : 3;
|
194
|
+
|
195
|
+
peer->amInterested = yes;
|
196
|
+
|
197
|
+
peer_dbg( "SEND %sinterested", yes ? "" : "un" );
|
198
|
+
}
|
199
|
+
|
200
|
+
/***********************************************************************
|
201
|
+
* sendHave
|
202
|
+
***********************************************************************
|
203
|
+
*
|
204
|
+
**********************************************************************/
|
205
|
+
static void sendHave( tr_peer_t * peer, int piece )
|
206
|
+
{
|
207
|
+
uint8_t * p;
|
208
|
+
|
209
|
+
p = getPointerForSize( peer, 9 );
|
210
|
+
|
211
|
+
TR_HTONL( 5, &p[0] );
|
212
|
+
p[4] = 4;
|
213
|
+
TR_HTONL( piece, &p[5] );
|
214
|
+
|
215
|
+
peer_dbg( "SEND have %d", piece );
|
216
|
+
}
|
217
|
+
|
218
|
+
/***********************************************************************
|
219
|
+
* sendBitfield
|
220
|
+
***********************************************************************
|
221
|
+
* Builds a 'bitfield' message:
|
222
|
+
* - size = 5 + X (4 bytes)
|
223
|
+
* - id = 5 (1 byte)
|
224
|
+
* - bitfield (X bytes)
|
225
|
+
**********************************************************************/
|
226
|
+
static void sendBitfield( tr_torrent_t * tor, tr_peer_t * peer )
|
227
|
+
{
|
228
|
+
uint8_t * p;
|
229
|
+
int bitfieldSize = ( tor->info.pieceCount + 7 ) / 8;
|
230
|
+
|
231
|
+
p = getPointerForSize( peer, 5 + bitfieldSize );
|
232
|
+
|
233
|
+
TR_HTONL( 1 + bitfieldSize, p );
|
234
|
+
p[4] = 5;
|
235
|
+
memcpy( &p[5], tr_cpPieceBitfield( tor->completion ), bitfieldSize );
|
236
|
+
|
237
|
+
peer_dbg( "SEND bitfield" );
|
238
|
+
}
|
239
|
+
|
240
|
+
/***********************************************************************
|
241
|
+
* sendRequest
|
242
|
+
***********************************************************************
|
243
|
+
*
|
244
|
+
**********************************************************************/
|
245
|
+
static void sendRequest( tr_torrent_t * tor, tr_peer_t * peer, int block )
|
246
|
+
{
|
247
|
+
tr_info_t * inf = &tor->info;
|
248
|
+
tr_request_t * r;
|
249
|
+
uint8_t * p;
|
250
|
+
|
251
|
+
/* Get the piece the block is a part of, its position in the piece
|
252
|
+
and its size */
|
253
|
+
r = &peer->inRequests[peer->inRequestCount];
|
254
|
+
r->index = block / ( inf->pieceSize / tor->blockSize );
|
255
|
+
r->begin = ( block % ( inf->pieceSize / tor->blockSize ) ) *
|
256
|
+
tor->blockSize;
|
257
|
+
r->length = tor->blockSize;
|
258
|
+
if( block == tor->blockCount - 1 )
|
259
|
+
{
|
260
|
+
int lastSize = inf->totalSize % tor->blockSize;
|
261
|
+
if( lastSize )
|
262
|
+
{
|
263
|
+
r->length = lastSize;
|
264
|
+
}
|
265
|
+
}
|
266
|
+
(peer->inRequestCount)++;
|
267
|
+
|
268
|
+
/* Build the "ask" message */
|
269
|
+
p = getPointerForSize( peer, 17 );
|
270
|
+
|
271
|
+
TR_HTONL( 13, p );
|
272
|
+
p[4] = 6;
|
273
|
+
TR_HTONL( r->index, p + 5 );
|
274
|
+
TR_HTONL( r->begin, p + 9 );
|
275
|
+
TR_HTONL( r->length, p + 13 );
|
276
|
+
|
277
|
+
tr_cpDownloaderAdd( tor->completion, block );
|
278
|
+
|
279
|
+
peer_dbg( "SEND request %d/%d (%d bytes)",
|
280
|
+
r->index, r->begin, r->length );
|
281
|
+
}
|
282
|
+
|
283
|
+
/***********************************************************************
|
284
|
+
* sendCancel
|
285
|
+
***********************************************************************
|
286
|
+
*
|
287
|
+
**********************************************************************/
|
288
|
+
static void sendCancel( tr_torrent_t * tor, int block )
|
289
|
+
{
|
290
|
+
int i, j;
|
291
|
+
uint8_t * p;
|
292
|
+
tr_peer_t * peer;
|
293
|
+
tr_request_t * r;
|
294
|
+
|
295
|
+
for( i = 0; i < tor->peerCount; i++ )
|
296
|
+
{
|
297
|
+
peer = tor->peers[i];
|
298
|
+
|
299
|
+
for( j = 1; j < peer->inRequestCount; j++ )
|
300
|
+
{
|
301
|
+
r = &peer->inRequests[j];
|
302
|
+
|
303
|
+
if( block != tr_block( r->index, r->begin ) )
|
304
|
+
{
|
305
|
+
continue;
|
306
|
+
}
|
307
|
+
|
308
|
+
p = getPointerForSize( peer, 17 );
|
309
|
+
|
310
|
+
/* Build the "cancel" message */
|
311
|
+
TR_HTONL( 13, p );
|
312
|
+
p[4] = 8;
|
313
|
+
TR_HTONL( r->index, p + 5 );
|
314
|
+
TR_HTONL( r->begin, p + 9 );
|
315
|
+
TR_HTONL( r->length, p + 13 );
|
316
|
+
|
317
|
+
peer_dbg( "SEND cancel %d/%d (%d bytes)",
|
318
|
+
r->index, r->begin, r->length );
|
319
|
+
|
320
|
+
(peer->inRequestCount)--;
|
321
|
+
memmove( &peer->inRequests[j], &peer->inRequests[j+1],
|
322
|
+
( peer->inRequestCount - j ) * sizeof( tr_request_t ) );
|
323
|
+
break;
|
324
|
+
}
|
325
|
+
}
|
326
|
+
}
|
data/ext/peerparse.h
ADDED
@@ -0,0 +1,564 @@
|
|
1
|
+
/******************************************************************************
|
2
|
+
* $Id: peerparse.h 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
|
+
/***********************************************************************
|
26
|
+
* This file handles all incoming messages
|
27
|
+
**********************************************************************/
|
28
|
+
|
29
|
+
/***********************************************************************
|
30
|
+
* parseChoke
|
31
|
+
***********************************************************************
|
32
|
+
*
|
33
|
+
**********************************************************************/
|
34
|
+
static inline int parseChoke( tr_torrent_t * tor, tr_peer_t * peer,
|
35
|
+
int len, int choking )
|
36
|
+
{
|
37
|
+
tr_request_t * r;
|
38
|
+
int i;
|
39
|
+
|
40
|
+
if( len != 1 )
|
41
|
+
{
|
42
|
+
peer_dbg( "GET %schoke, invalid", choking ? "" : "un" );
|
43
|
+
return 1;
|
44
|
+
}
|
45
|
+
|
46
|
+
peer_dbg( "GET %schoke", choking ? "" : "un" );
|
47
|
+
|
48
|
+
peer->peerChoking = choking;
|
49
|
+
|
50
|
+
if( choking )
|
51
|
+
{
|
52
|
+
/* Discard all pending requests */
|
53
|
+
for( i = 0; i < peer->inRequestCount; i++ )
|
54
|
+
{
|
55
|
+
r = &peer->inRequests[i];
|
56
|
+
tr_cpDownloaderRem( tor->completion, tr_block(r->index,r->begin) );
|
57
|
+
}
|
58
|
+
peer->inRequestCount = 0;
|
59
|
+
}
|
60
|
+
|
61
|
+
return 0;
|
62
|
+
}
|
63
|
+
|
64
|
+
/***********************************************************************
|
65
|
+
* parseInterested
|
66
|
+
***********************************************************************
|
67
|
+
*
|
68
|
+
**********************************************************************/
|
69
|
+
static inline int parseInterested( tr_peer_t * peer, int len,
|
70
|
+
int interested )
|
71
|
+
{
|
72
|
+
if( len != 1 )
|
73
|
+
{
|
74
|
+
peer_dbg( "GET %sinterested, invalid", interested ? "" : "un" );
|
75
|
+
return 1;
|
76
|
+
}
|
77
|
+
|
78
|
+
peer_dbg( "GET %sinterested", interested ? "" : "un" );
|
79
|
+
|
80
|
+
peer->peerInterested = interested;
|
81
|
+
|
82
|
+
return 0;
|
83
|
+
}
|
84
|
+
|
85
|
+
/***********************************************************************
|
86
|
+
* parseHave
|
87
|
+
***********************************************************************
|
88
|
+
*
|
89
|
+
**********************************************************************/
|
90
|
+
static inline int parseHave( tr_torrent_t * tor, tr_peer_t * peer,
|
91
|
+
uint8_t * p, int len )
|
92
|
+
{
|
93
|
+
uint32_t piece;
|
94
|
+
|
95
|
+
if( len != 5 )
|
96
|
+
{
|
97
|
+
peer_dbg( "GET have, invalid" );
|
98
|
+
return 1;
|
99
|
+
}
|
100
|
+
|
101
|
+
TR_NTOHL( p, piece );
|
102
|
+
|
103
|
+
peer_dbg( "GET have %d", piece );
|
104
|
+
|
105
|
+
if( !peer->bitfield )
|
106
|
+
{
|
107
|
+
peer->bitfield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
|
108
|
+
}
|
109
|
+
tr_bitfieldAdd( peer->bitfield, piece );
|
110
|
+
updateInterest( tor, peer );
|
111
|
+
|
112
|
+
return 0;
|
113
|
+
}
|
114
|
+
|
115
|
+
static inline int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer,
|
116
|
+
uint8_t * p, int len )
|
117
|
+
{
|
118
|
+
tr_info_t * inf = &tor->info;
|
119
|
+
int bitfieldSize;
|
120
|
+
|
121
|
+
bitfieldSize = ( inf->pieceCount + 7 ) / 8;
|
122
|
+
|
123
|
+
if( len != 1 + bitfieldSize )
|
124
|
+
{
|
125
|
+
peer_dbg( "GET bitfield, wrong size" );
|
126
|
+
return 1;
|
127
|
+
}
|
128
|
+
|
129
|
+
/* Make sure the spare bits are unset */
|
130
|
+
if( ( inf->pieceCount & 0x7 ) )
|
131
|
+
{
|
132
|
+
uint8_t lastByte;
|
133
|
+
|
134
|
+
lastByte = p[bitfieldSize-1];
|
135
|
+
lastByte <<= inf->pieceCount & 0x7;
|
136
|
+
lastByte &= 0xFF;
|
137
|
+
|
138
|
+
if( lastByte )
|
139
|
+
{
|
140
|
+
peer_dbg( "GET bitfield, spare bits set" );
|
141
|
+
return 1;
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
peer_dbg( "GET bitfield, ok" );
|
146
|
+
|
147
|
+
if( !peer->bitfield )
|
148
|
+
{
|
149
|
+
peer->bitfield = malloc( bitfieldSize );
|
150
|
+
}
|
151
|
+
memcpy( peer->bitfield, p, bitfieldSize );
|
152
|
+
updateInterest( tor, peer );
|
153
|
+
|
154
|
+
return 0;
|
155
|
+
}
|
156
|
+
|
157
|
+
static inline int parseRequest( tr_peer_t * peer, uint8_t * p, int len )
|
158
|
+
{
|
159
|
+
int index, begin, length;
|
160
|
+
tr_request_t * r;
|
161
|
+
|
162
|
+
if( len != 13 )
|
163
|
+
{
|
164
|
+
peer_dbg( "GET request, invalid" );
|
165
|
+
return 1;
|
166
|
+
}
|
167
|
+
|
168
|
+
if( peer->amChoking )
|
169
|
+
{
|
170
|
+
/* Didn't he get it? */
|
171
|
+
sendChoke( peer, 1 );
|
172
|
+
return 0;
|
173
|
+
}
|
174
|
+
|
175
|
+
TR_NTOHL( p, index );
|
176
|
+
TR_NTOHL( &p[4], begin );
|
177
|
+
TR_NTOHL( &p[8], length );
|
178
|
+
|
179
|
+
peer_dbg( "GET request %d/%d (%d bytes)",
|
180
|
+
index, begin, length );
|
181
|
+
|
182
|
+
/* TODO sanity checks (do we have the piece, etc) */
|
183
|
+
|
184
|
+
if( length > 16384 )
|
185
|
+
{
|
186
|
+
/* Sorry mate */
|
187
|
+
return 1;
|
188
|
+
}
|
189
|
+
|
190
|
+
if( peer->outRequestCount >= MAX_REQUEST_COUNT )
|
191
|
+
{
|
192
|
+
tr_err( "Too many requests" );
|
193
|
+
return 1;
|
194
|
+
}
|
195
|
+
|
196
|
+
r = &peer->outRequests[peer->outRequestCount];
|
197
|
+
r->index = index;
|
198
|
+
r->begin = begin;
|
199
|
+
r->length = length;
|
200
|
+
|
201
|
+
(peer->outRequestCount)++;
|
202
|
+
|
203
|
+
return 0;
|
204
|
+
}
|
205
|
+
|
206
|
+
static inline int parsePiece( tr_torrent_t * tor, tr_peer_t * peer,
|
207
|
+
uint8_t * p, int len )
|
208
|
+
{
|
209
|
+
int index, begin, block, i, j;
|
210
|
+
tr_request_t * r;
|
211
|
+
|
212
|
+
TR_NTOHL( p, index );
|
213
|
+
TR_NTOHL( &p[4], begin );
|
214
|
+
|
215
|
+
peer_dbg( "GET piece %d/%d (%d bytes)",
|
216
|
+
index, begin, len - 9 );
|
217
|
+
|
218
|
+
if( peer->inRequestCount < 1 )
|
219
|
+
{
|
220
|
+
/* Our "cancel" was probably late */
|
221
|
+
peer_dbg( "not expecting a block" );
|
222
|
+
return 0;
|
223
|
+
}
|
224
|
+
|
225
|
+
r = &peer->inRequests[0];
|
226
|
+
if( index != r->index || begin != r->begin )
|
227
|
+
{
|
228
|
+
int suckyClient;
|
229
|
+
|
230
|
+
/* Either our "cancel" was late, or this is a sucky
|
231
|
+
client that cannot deal with multiple requests */
|
232
|
+
suckyClient = 0;
|
233
|
+
for( i = 0; i < peer->inRequestCount; i++ )
|
234
|
+
{
|
235
|
+
r = &peer->inRequests[i];
|
236
|
+
|
237
|
+
if( index != r->index || begin != r->begin )
|
238
|
+
{
|
239
|
+
continue;
|
240
|
+
}
|
241
|
+
|
242
|
+
/* Sucky client, he dropped the previous requests */
|
243
|
+
peer_dbg( "block was expected later" );
|
244
|
+
for( j = 0; j < i; j++ )
|
245
|
+
{
|
246
|
+
r = &peer->inRequests[j];
|
247
|
+
tr_cpDownloaderRem( tor->completion,
|
248
|
+
tr_block(r->index,r->begin) );
|
249
|
+
}
|
250
|
+
suckyClient = 1;
|
251
|
+
peer->inRequestCount -= i;
|
252
|
+
memmove( &peer->inRequests[0], &peer->inRequests[i],
|
253
|
+
peer->inRequestCount * sizeof( tr_request_t ) );
|
254
|
+
r = &peer->inRequests[0];
|
255
|
+
break;
|
256
|
+
}
|
257
|
+
|
258
|
+
if( !suckyClient )
|
259
|
+
{
|
260
|
+
r = &peer->inRequests[0];
|
261
|
+
peer_dbg( "wrong block (expecting %d/%d)",
|
262
|
+
r->index, r->begin );
|
263
|
+
return 0;
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
if( len - 9 != r->length )
|
268
|
+
{
|
269
|
+
peer_dbg( "wrong size (expecting %d)", r->length );
|
270
|
+
return 1;
|
271
|
+
}
|
272
|
+
|
273
|
+
tor->downloaded += r->length;
|
274
|
+
|
275
|
+
block = tr_block( r->index, r->begin );
|
276
|
+
if( tr_cpBlockIsComplete( tor->completion, block ) )
|
277
|
+
{
|
278
|
+
peer_dbg( "have this block already" );
|
279
|
+
(peer->inRequestCount)--;
|
280
|
+
memmove( &peer->inRequests[0], &peer->inRequests[1],
|
281
|
+
peer->inRequestCount * sizeof( tr_request_t ) );
|
282
|
+
return 0;
|
283
|
+
}
|
284
|
+
|
285
|
+
/* set blame/credit for this piece */
|
286
|
+
if( !peer->blamefield )
|
287
|
+
{
|
288
|
+
peer->blamefield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
|
289
|
+
}
|
290
|
+
tr_bitfieldAdd( peer->blamefield, index );
|
291
|
+
|
292
|
+
tr_cpBlockAdd( tor->completion, block );
|
293
|
+
tr_ioWrite( tor->io, index, begin, len - 9, &p[8] );
|
294
|
+
tr_cpDownloaderRem( tor->completion, block );
|
295
|
+
|
296
|
+
sendCancel( tor, block );
|
297
|
+
|
298
|
+
if( tr_cpPieceIsComplete( tor->completion, index ) )
|
299
|
+
{
|
300
|
+
tr_peer_t * otherPeer;
|
301
|
+
|
302
|
+
for( i = 0; i < tor->peerCount; i++ )
|
303
|
+
{
|
304
|
+
otherPeer = tor->peers[i];
|
305
|
+
|
306
|
+
if( otherPeer->status < PEER_STATUS_CONNECTED )
|
307
|
+
{
|
308
|
+
continue;
|
309
|
+
}
|
310
|
+
|
311
|
+
sendHave( otherPeer, index );
|
312
|
+
updateInterest( tor, otherPeer );
|
313
|
+
}
|
314
|
+
}
|
315
|
+
|
316
|
+
(peer->inRequestCount)--;
|
317
|
+
memmove( &peer->inRequests[0], &peer->inRequests[1],
|
318
|
+
peer->inRequestCount * sizeof( tr_request_t ) );
|
319
|
+
|
320
|
+
return 0;
|
321
|
+
}
|
322
|
+
|
323
|
+
static inline int parseCancel( tr_peer_t * peer, uint8_t * p, int len )
|
324
|
+
{
|
325
|
+
int index, begin, length;
|
326
|
+
int i;
|
327
|
+
tr_request_t * r;
|
328
|
+
|
329
|
+
if( len != 13 )
|
330
|
+
{
|
331
|
+
peer_dbg( "GET cancel, invalid" );
|
332
|
+
return 1;
|
333
|
+
}
|
334
|
+
|
335
|
+
TR_NTOHL( p, index );
|
336
|
+
TR_NTOHL( &p[4], begin );
|
337
|
+
TR_NTOHL( &p[8], length );
|
338
|
+
|
339
|
+
peer_dbg( "GET cancel %d/%d (%d bytes)",
|
340
|
+
index, begin, length );
|
341
|
+
|
342
|
+
for( i = 0; i < peer->outRequestCount; i++ )
|
343
|
+
{
|
344
|
+
r = &peer->outRequests[i];
|
345
|
+
if( r->index == index && r->begin == begin &&
|
346
|
+
r->length == length )
|
347
|
+
{
|
348
|
+
(peer->outRequestCount)--;
|
349
|
+
memmove( &r[0], &r[1], sizeof( tr_request_t ) *
|
350
|
+
( peer->outRequestCount - i ) );
|
351
|
+
break;
|
352
|
+
}
|
353
|
+
}
|
354
|
+
|
355
|
+
return 0;
|
356
|
+
}
|
357
|
+
|
358
|
+
static inline int parsePort( tr_peer_t * peer, uint8_t * p, int len )
|
359
|
+
{
|
360
|
+
in_port_t port;
|
361
|
+
|
362
|
+
if( len != 3 )
|
363
|
+
{
|
364
|
+
peer_dbg( "GET port, invalid" );
|
365
|
+
return 1;
|
366
|
+
}
|
367
|
+
|
368
|
+
port = *( (in_port_t *) p );
|
369
|
+
peer_dbg( "GET port %d", ntohs( port ) );
|
370
|
+
|
371
|
+
return 0;
|
372
|
+
}
|
373
|
+
|
374
|
+
static inline int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
|
375
|
+
uint8_t * p, int len )
|
376
|
+
{
|
377
|
+
char id;
|
378
|
+
|
379
|
+
/* Type of the message */
|
380
|
+
id = *(p++);
|
381
|
+
|
382
|
+
switch( id )
|
383
|
+
{
|
384
|
+
case 0:
|
385
|
+
return parseChoke( tor, peer, len, 1 );
|
386
|
+
case 1:
|
387
|
+
return parseChoke( tor, peer, len, 0 );
|
388
|
+
case 2:
|
389
|
+
return parseInterested( peer, len, 1 );
|
390
|
+
case 3:
|
391
|
+
return parseInterested( peer, len, 0 );
|
392
|
+
case 4:
|
393
|
+
return parseHave( tor, peer, p, len );
|
394
|
+
case 5:
|
395
|
+
return parseBitfield( tor, peer, p, len );
|
396
|
+
case 6:
|
397
|
+
return parseRequest( peer, p, len );
|
398
|
+
case 7:
|
399
|
+
return parsePiece( tor, peer, p, len );
|
400
|
+
case 8:
|
401
|
+
return parseCancel( peer, p, len );
|
402
|
+
case 9:
|
403
|
+
return parsePort( peer, p, len );
|
404
|
+
}
|
405
|
+
|
406
|
+
peer_dbg( "Unknown message '%d'", id );
|
407
|
+
return 1;
|
408
|
+
}
|
409
|
+
|
410
|
+
static inline int parseBufHeader( tr_peer_t * peer )
|
411
|
+
{
|
412
|
+
uint8_t * p = peer->buf;
|
413
|
+
|
414
|
+
if( 4 > peer->pos )
|
415
|
+
{
|
416
|
+
return 0;
|
417
|
+
}
|
418
|
+
|
419
|
+
if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
|
420
|
+
{
|
421
|
+
/* Don't wait until we get 68 bytes, this is wrong
|
422
|
+
already */
|
423
|
+
peer_dbg( "GET handshake, invalid" );
|
424
|
+
tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
|
425
|
+
return 1;
|
426
|
+
}
|
427
|
+
if( peer->pos < 68 )
|
428
|
+
{
|
429
|
+
return 0;
|
430
|
+
}
|
431
|
+
if( memcmp( &p[4], "Torrent protocol", 16 ) )
|
432
|
+
{
|
433
|
+
peer_dbg( "GET handshake, invalid" );
|
434
|
+
return 1;
|
435
|
+
}
|
436
|
+
|
437
|
+
return 0;
|
438
|
+
}
|
439
|
+
|
440
|
+
static uint8_t * parseBufHash( tr_peer_t * peer )
|
441
|
+
{
|
442
|
+
if( 48 > peer->pos )
|
443
|
+
{
|
444
|
+
return NULL;
|
445
|
+
}
|
446
|
+
else
|
447
|
+
{
|
448
|
+
return peer->buf + 28;
|
449
|
+
}
|
450
|
+
}
|
451
|
+
|
452
|
+
static inline int parseBuf( tr_torrent_t * tor, tr_peer_t * peer )
|
453
|
+
{
|
454
|
+
tr_info_t * inf = &tor->info;
|
455
|
+
|
456
|
+
int i;
|
457
|
+
int len;
|
458
|
+
uint8_t * p = peer->buf;
|
459
|
+
uint8_t * end = &p[peer->pos];
|
460
|
+
|
461
|
+
if( peer->banned )
|
462
|
+
{
|
463
|
+
/* Don't even parse, we only stay connected */
|
464
|
+
peer->pos = 0;
|
465
|
+
return 0;
|
466
|
+
}
|
467
|
+
|
468
|
+
while( peer->pos >= 4 )
|
469
|
+
{
|
470
|
+
if( peer->status & PEER_STATUS_HANDSHAKE )
|
471
|
+
{
|
472
|
+
char * client;
|
473
|
+
|
474
|
+
if( parseBufHeader( peer ) )
|
475
|
+
{
|
476
|
+
return 1;
|
477
|
+
}
|
478
|
+
|
479
|
+
if( peer->pos < 68 )
|
480
|
+
{
|
481
|
+
break;
|
482
|
+
}
|
483
|
+
|
484
|
+
if( memcmp( &p[28], inf->hash, 20 ) )
|
485
|
+
{
|
486
|
+
peer_dbg( "GET handshake, wrong torrent hash" );
|
487
|
+
return 1;
|
488
|
+
}
|
489
|
+
|
490
|
+
if( !memcmp( &p[48], tor->id, 20 ) )
|
491
|
+
{
|
492
|
+
/* We are connected to ourselves... */
|
493
|
+
peer_dbg( "GET handshake, that is us" );
|
494
|
+
return 1;
|
495
|
+
}
|
496
|
+
|
497
|
+
peer->status = PEER_STATUS_CONNECTED;
|
498
|
+
memcpy( peer->id, &p[48], 20 );
|
499
|
+
p += 68;
|
500
|
+
peer->pos -= 68;
|
501
|
+
|
502
|
+
for( i = 0; i < tor->peerCount; i++ )
|
503
|
+
{
|
504
|
+
if( tor->peers[i] == peer )
|
505
|
+
{
|
506
|
+
continue;
|
507
|
+
}
|
508
|
+
if( !peerCmp( peer, tor->peers[i] ) )
|
509
|
+
{
|
510
|
+
peer_dbg( "GET handshake, duplicate" );
|
511
|
+
return 1;
|
512
|
+
}
|
513
|
+
}
|
514
|
+
|
515
|
+
client = tr_clientForId( (uint8_t *) peer->id );
|
516
|
+
peer_dbg( "GET handshake, ok (%s)", client );
|
517
|
+
free( client );
|
518
|
+
|
519
|
+
sendBitfield( tor, peer );
|
520
|
+
|
521
|
+
continue;
|
522
|
+
}
|
523
|
+
|
524
|
+
/* Get payload size */
|
525
|
+
TR_NTOHL( p, len );
|
526
|
+
p += 4;
|
527
|
+
|
528
|
+
if( len > 9 + tor->blockSize )
|
529
|
+
{
|
530
|
+
/* This should never happen. Drop that peer */
|
531
|
+
peer_dbg( "message too large (%d bytes)", len );
|
532
|
+
return 1;
|
533
|
+
}
|
534
|
+
|
535
|
+
if( !len )
|
536
|
+
{
|
537
|
+
/* keep-alive */
|
538
|
+
peer_dbg( "GET keep-alive" );
|
539
|
+
peer->pos -= 4;
|
540
|
+
continue;
|
541
|
+
}
|
542
|
+
|
543
|
+
if( &p[len] > end )
|
544
|
+
{
|
545
|
+
/* We do not have the entire message */
|
546
|
+
p -= 4;
|
547
|
+
break;
|
548
|
+
}
|
549
|
+
|
550
|
+
/* Remaining data after this message */
|
551
|
+
peer->pos -= 4 + len;
|
552
|
+
|
553
|
+
if( parseMessage( tor, peer, p, len ) )
|
554
|
+
{
|
555
|
+
return 1;
|
556
|
+
}
|
557
|
+
|
558
|
+
p += len;
|
559
|
+
}
|
560
|
+
|
561
|
+
memmove( peer->buf, p, peer->pos );
|
562
|
+
|
563
|
+
return 0;
|
564
|
+
}
|