source: trunk/libtransmission/peerparse.h @ 2

Last change on this file since 2 was 2, checked in by root, 16 years ago

Update 2005-11-01

File size: 13.6 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/***********************************************************************
24 * This file handles all incoming messages
25 **********************************************************************/
26
27/***********************************************************************
28 * parseChoke
29 ***********************************************************************
30 *
31 **********************************************************************/
32static inline int parseChoke( tr_torrent_t * tor, tr_peer_t * peer,
33                              int len, int choking )
34{
35    tr_request_t * r;
36    int i;
37
38    if( len != 1 )
39    {
40        peer_dbg( "GET  %schoke, invalid", choking ? "" : "un" );
41        return 1;
42    }
43
44    peer_dbg( "GET  %schoke", choking ? "" : "un" );
45
46    peer->peerChoking = choking;
47
48    if( choking )
49    {
50        /* Discard all pending requests */
51        for( i = 0; i < peer->inRequestCount; i++ )
52        {
53            r = &peer->inRequests[i];
54            if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
55            {
56                tor->blockHave[tr_block(r->index,r->begin)]--;
57            }
58        }
59        peer->inRequestCount = 0;
60    }
61
62    return 0;
63}
64
65/***********************************************************************
66 * parseInterested
67 ***********************************************************************
68 *
69 **********************************************************************/
70static inline int parseInterested( tr_peer_t * peer, int len,
71                                   int interested )
72{
73    if( len != 1 )
74    {
75        peer_dbg( "GET  %sinterested, invalid", interested ? "" : "un" );
76        return 1;
77    }
78
79    peer_dbg( "GET  %sinterested", interested ? "" : "un" );
80
81    peer->peerInterested = interested;
82
83    return 0;
84}
85
86/***********************************************************************
87 * parseHave
88 ***********************************************************************
89 *
90 **********************************************************************/
91static inline int parseHave( tr_torrent_t * tor, tr_peer_t * peer,
92                             uint8_t * p, int len )
93{
94    uint32_t piece;
95
96    if( len != 5 )
97    {
98        peer_dbg( "GET  have, invalid" );
99        return 1;
100    }
101
102    TR_NTOHL( p, piece );
103
104    peer_dbg( "GET  have %d", piece );
105
106    if( !peer->bitfield )
107    {
108        peer->bitfield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
109    }
110    tr_bitfieldAdd( peer->bitfield, piece );
111    updateInterest( tor, peer );
112
113    return 0;
114}
115
116static inline int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer,
117                                 uint8_t * p, int len )
118{
119    tr_info_t * inf = &tor->info;
120    int bitfieldSize;
121
122    bitfieldSize = ( inf->pieceCount + 7 ) / 8;
123   
124    if( len != 1 + bitfieldSize )
125    {
126        peer_dbg( "GET  bitfield, wrong size" );
127        return 1;
128    }
129
130    /* Make sure the spare bits are unset */
131    if( ( inf->pieceCount & 0x7 ) )
132    {
133        uint8_t lastByte;
134       
135        lastByte   = p[bitfieldSize-1];
136        lastByte <<= inf->pieceCount & 0x7;
137        lastByte  &= 0xFF;
138
139        if( lastByte )
140        {
141            peer_dbg( "GET  bitfield, spare bits set" );
142            return 1;
143        }
144    }
145
146    peer_dbg( "GET  bitfield, ok" );
147
148    if( !peer->bitfield )
149    {
150        peer->bitfield = malloc( bitfieldSize );
151    }
152    memcpy( peer->bitfield, p, bitfieldSize );
153    updateInterest( tor, peer );
154
155    return 0;
156}
157
158static inline int parseRequest( tr_peer_t * peer, uint8_t * p, int len )
159{
160    int index, begin, length;
161    tr_request_t * r;
162
163    if( len != 13 )
164    {
165        peer_dbg( "GET  request, invalid" );
166        return 1;
167    }
168
169    if( peer->amChoking )
170    {
171        /* Didn't he get it? */
172        sendChoke( peer, 1 );
173        return 0;
174    }
175   
176    TR_NTOHL( p,     index );
177    TR_NTOHL( &p[4], begin );
178    TR_NTOHL( &p[8], length );
179
180    peer_dbg( "GET  request %d/%d (%d bytes)",
181              index, begin, length );
182
183    /* TODO sanity checks (do we have the piece, etc) */
184
185    if( length > 16384 )
186    {
187        /* Sorry mate */
188        return 1;
189    }
190
191    if( peer->outRequestCount >= MAX_REQUEST_COUNT )
192    {
193        tr_err( "Too many requests" );
194        return 1;
195    }
196
197    r         = &peer->outRequests[peer->outRequestCount];
198    r->index  = index;
199    r->begin  = begin;
200    r->length = length;
201
202    (peer->outRequestCount)++;
203
204    return 0;
205}
206
207static inline int parsePiece( tr_torrent_t * tor, tr_peer_t * peer,
208                              uint8_t * p, int len )
209{
210    int index, begin, block, i, j;
211    tr_request_t * r;
212
213    TR_NTOHL( p,     index );
214    TR_NTOHL( &p[4], begin );
215
216    peer_dbg( "GET  piece %d/%d (%d bytes)",
217              index, begin, len - 9 );
218
219    if( peer->inRequestCount < 1 )
220    {
221        /* Our "cancel" was probably late */
222        peer_dbg( "not expecting a block" );
223        return 0;
224    }
225   
226    r = &peer->inRequests[0];
227    if( index != r->index || begin != r->begin )
228    {
229        int suckyClient;
230
231        /* Either our "cancel" was late, or this is a sucky
232           client that cannot deal with multiple requests */
233        suckyClient = 0;
234        for( i = 0; i < peer->inRequestCount; i++ )
235        {
236            r = &peer->inRequests[i];
237
238            if( index != r->index || begin != r->begin )
239            {
240                continue;
241            }
242
243            /* Sucky client, he dropped the previous requests */
244            peer_dbg( "block was expected later" );
245            for( j = 0; j < i; j++ )
246            {
247                r = &peer->inRequests[j];
248                if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
249                {
250                    tor->blockHave[tr_block(r->index,r->begin)]--;
251                }
252            }
253            suckyClient = 1;
254            peer->inRequestCount -= i;
255            memmove( &peer->inRequests[0], &peer->inRequests[i],
256                     peer->inRequestCount * sizeof( tr_request_t ) );
257            r = &peer->inRequests[0];
258            break;
259        }
260
261        if( !suckyClient )
262        {
263            r = &peer->inRequests[0];
264            peer_dbg( "wrong block (expecting %d/%d)",
265                      r->index, r->begin );
266            return 0;
267        }
268    }
269
270    if( len - 9 != r->length )
271    {
272        peer_dbg( "wrong size (expecting %d)", r->length );
273        return 1;
274    }
275
276    block = tr_block( r->index, r->begin );
277    if( tor->blockHave[block] < 0 )
278    {
279        peer_dbg( "have this block already" );
280        (peer->inRequestCount)--;
281        memmove( &peer->inRequests[0], &peer->inRequests[1],
282                 peer->inRequestCount * sizeof( tr_request_t ) );
283        return 0;
284    }
285
286    tor->blockHave[block]  = -1;
287    tor->blockHaveCount   +=  1;
288    tr_ioWrite( tor->io, index, begin, len - 9, &p[8] );
289
290    sendCancel( tor, block );
291
292    if( tr_bitfieldHas( tor->bitfield, index ) )
293    {
294        tr_peer_t * otherPeer;
295
296        for( i = 0; i < tor->peerCount; i++ )
297        {
298            otherPeer = tor->peers[i];
299
300            if( otherPeer->status < PEER_STATUS_CONNECTED )
301            {
302                continue;
303            }
304
305            sendHave( otherPeer, index );
306            updateInterest( tor, otherPeer );
307        }
308    }
309
310    (peer->inRequestCount)--;
311    memmove( &peer->inRequests[0], &peer->inRequests[1],
312             peer->inRequestCount * sizeof( tr_request_t ) );
313
314    return 0;
315}
316
317static inline int parseCancel( tr_peer_t * peer, uint8_t * p, int len )
318{
319    int index, begin, length;
320    int i;
321    tr_request_t * r;
322
323    if( len != 13 )
324    {
325        peer_dbg( "GET  cancel, invalid" );
326        return 1;
327    }
328
329    TR_NTOHL( p,     index );
330    TR_NTOHL( &p[4], begin );
331    TR_NTOHL( &p[8], length );
332
333    peer_dbg( "GET  cancel %d/%d (%d bytes)",
334              index, begin, length );
335
336    for( i = 0; i < peer->outRequestCount; i++ )
337    {
338        r = &peer->outRequests[i];
339        if( r->index == index && r->begin == begin &&
340            r->length == length )
341        {
342            (peer->outRequestCount)--;
343            memmove( &r[0], &r[1], sizeof( tr_request_t ) *
344                    ( peer->outRequestCount - i ) );
345            break;
346        }
347    }
348
349    return 0;
350}
351
352static inline int parsePort( tr_peer_t * peer, uint8_t * p, int len )
353{
354    in_port_t port;
355
356    if( len != 3 )
357    {
358        peer_dbg( "GET  port, invalid" );
359        return 1;
360    }
361
362    port = *( (in_port_t *) p );
363    peer_dbg( "GET  port %d", ntohs( port ) );
364
365    return 0;
366}
367
368static inline int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
369                                uint8_t * p, int len )
370{
371    char id;
372
373    /* Type of the message */
374    id = *(p++);
375
376    switch( id )
377    {
378        case 0:
379            return parseChoke( tor, peer, len, 1 );
380        case 1:
381            return parseChoke( tor, peer, len, 0 );
382        case 2:
383            return parseInterested( peer, len, 1 );
384        case 3:
385            return parseInterested( peer, len, 0 );
386        case 4:
387            return parseHave( tor, peer, p, len );
388        case 5:
389            return parseBitfield( tor, peer, p, len );
390        case 6:
391            return parseRequest( peer, p, len );
392        case 7:
393            return parsePiece( tor, peer, p, len );
394        case 8:
395            return parseCancel( peer, p, len );
396        case 9:
397            return parsePort( peer, p, len );
398    }
399
400    peer_dbg( "Unknown message '%d'", id );
401    return 1;
402}
403
404static inline int parseBuf( tr_torrent_t * tor, tr_peer_t * peer,
405                            int newBytes )
406{
407    tr_info_t * inf = &tor->info;
408
409    int       i;
410    int       len;
411    uint8_t * p   = peer->buf;
412    uint8_t * end = &p[peer->pos];
413
414    while( peer->pos >= 4 )
415    {
416        if( peer->status & PEER_STATUS_HANDSHAKE )
417        {
418            char * client;
419
420            if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
421            {
422                /* Don't wait until we get 68 bytes, this is wrong
423                   already */
424                peer_dbg( "GET  handshake, invalid" );
425                tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
426                return 1;
427            }
428
429            if( peer->pos < 68 )
430            {
431                break;
432            }
433
434            if( memcmp( &p[4], "Torrent protocol", 16 ) )
435            {
436                peer_dbg( "GET  handshake, invalid" );
437                return 1;
438            }
439
440            if( memcmp( &p[28], inf->hash, 20 ) )
441            {
442                peer_dbg( "GET  handshake, wrong torrent hash" );
443                return 1;
444            }
445
446            if( !memcmp( &p[48], tor->id, 20 ) )
447            {
448                /* We are connected to ourselves... */
449                peer_dbg( "GET  handshake, that is us" );
450                return 1;
451            }
452
453            peer->status  = PEER_STATUS_CONNECTED;
454            memcpy( peer->id, &p[48], 20 );
455            p            += 68;
456            peer->pos    -= 68;
457
458            for( i = 0; i < tor->peerCount; i++ )
459            {
460                if( tor->peers[i] == peer )
461                {
462                    continue;
463                }
464                if( !peerCmp( peer, tor->peers[i] ) )
465                {
466                    peer_dbg( "GET  handshake, duplicate" );
467                    return 1;
468                }
469            }
470
471            client = tr_clientForId( (uint8_t *) peer->id );
472            peer_dbg( "GET  handshake, ok (%s)", client );
473            free( client );
474
475            sendBitfield( tor, peer );
476
477            continue;
478        }
479       
480        /* Get payload size */
481        TR_NTOHL( p, len );
482        p += 4;
483
484        if( len > 9 + tor->blockSize )
485        {
486            /* This should never happen. Drop that peer */
487            peer_dbg( "message too large (%d bytes)", len );
488            return 1;
489        }
490
491        if( !len )
492        {
493            /* keep-alive */
494            peer_dbg( "GET  keep-alive" );
495            peer->pos -= 4;
496            continue;
497        }
498
499        /* That's a piece coming */
500        if( p < end && *p == 7 )
501        {
502            /* XXX */
503            tor->downloaded[9] += newBytes;
504            peer->inTotal      += newBytes;
505            newBytes            = 0;
506        }
507
508        if( &p[len] > end )
509        {
510            /* We do not have the entire message */
511            p -= 4;
512            break;
513        }
514
515        /* Remaining data after this message */
516        peer->pos -= 4 + len;
517
518        if( parseMessage( tor, peer, p, len ) )
519        {
520            return 1;
521        }
522
523        p += len;
524    }
525
526    memmove( peer->buf, p, peer->pos );
527
528    return 0;
529}
Note: See TracBrowser for help on using the repository browser.