source: trunk/libtransmission/peerparse.h @ 1534

Last change on this file since 1534 was 1534, checked in by joshe, 15 years ago

Do bounds checking on bitfields.

  • Property svn:keywords set to Date Rev Author Id
File size: 14.7 KB
Line 
1/******************************************************************************
2 * $Id: peerparse.h 1534 2007-03-05 23:03:38Z joshe $
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 **********************************************************************/
34static 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 != 0 )
41    {
42        peer_dbg( "GET  %schoke, invalid", choking ? "" : "un" );
43        return TR_ERROR_ASSERT;
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 TR_OK;
62}
63
64/***********************************************************************
65 * parseInterested
66 ***********************************************************************
67 *
68 **********************************************************************/
69static inline int parseInterested( tr_peer_t * peer, int len,
70                                   int interested )
71{
72    if( len != 0 )
73    {
74        peer_dbg( "GET  %sinterested, invalid", interested ? "" : "un" );
75        return TR_ERROR_ASSERT;
76    }
77
78    peer_dbg( "GET  %sinterested", interested ? "" : "un" );
79
80    peer->peerInterested = interested;
81
82    return TR_OK;
83}
84
85/***********************************************************************
86 * parseHave
87 ***********************************************************************
88 *
89 **********************************************************************/
90static 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 != 4 )
96    {
97        peer_dbg( "GET  have, invalid" );
98        return TR_ERROR_ASSERT;
99    }
100
101    TR_NTOHL( p, piece );
102
103    peer_dbg( "GET  have %d", piece );
104
105    if( !peer->bitfield )
106    {
107        peer->bitfield = tr_bitfieldNew( tor->info.pieceCount );
108    }
109    if( !tr_bitfieldHas( peer->bitfield, piece ) )
110    {
111        peer->pieceCount++;
112        peer->progress = (float) peer->pieceCount / tor->info.pieceCount;
113    }
114    tr_bitfieldAdd( peer->bitfield, piece );
115    updateInterest( tor, peer );
116
117    tr_rcTransferred( tor->swarmspeed, tor->info.pieceSize );
118
119    return TR_OK;
120}
121
122static inline int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer,
123                                 uint8_t * p, int len )
124{
125    tr_info_t * inf = &tor->info;
126    int bitfieldSize;
127    int i;
128
129    bitfieldSize = ( inf->pieceCount + 7 ) / 8;
130   
131    if( len != bitfieldSize )
132    {
133        peer_dbg( "GET  bitfield, wrong size" );
134        return TR_ERROR_ASSERT;
135    }
136
137    /* Make sure the spare bits are unset */
138    if( ( inf->pieceCount & 0x7 ) )
139    {
140        uint8_t lastByte;
141       
142        lastByte   = p[bitfieldSize-1];
143        lastByte <<= inf->pieceCount & 0x7;
144        lastByte  &= 0xFF;
145
146        if( lastByte )
147        {
148            peer_dbg( "GET  bitfield, spare bits set" );
149            return TR_ERROR_ASSERT;
150        }
151    }
152
153    peer_dbg( "GET  bitfield, ok" );
154
155    if( !peer->bitfield )
156    {
157        peer->bitfield = tr_bitfieldNew( inf->pieceCount );
158    }
159    assert( bitfieldSize == peer->bitfield->len );
160    memcpy( peer->bitfield->bits, p, bitfieldSize );
161
162    peer->pieceCount = 0;
163    for( i = 0; i < inf->pieceCount; i++ )
164    {
165        if( tr_bitfieldHas( peer->bitfield, i ) )
166        {
167            peer->pieceCount++;
168        }
169    }
170    peer->progress = (float) peer->pieceCount / inf->pieceCount;
171
172    updateInterest( tor, peer );
173
174    return TR_OK;
175}
176
177static inline int parseRequest( tr_peer_t * peer, uint8_t * p, int len )
178{
179    int index, begin, length;
180    tr_request_t * r;
181
182    if( len != 12 )
183    {
184        peer_dbg( "GET  request, invalid" );
185        return TR_ERROR_ASSERT;
186    }
187
188    if( peer->amChoking )
189    {
190        /* Didn't he get it? */
191        sendChoke( peer, 1 );
192        return TR_OK;
193    }
194   
195    TR_NTOHL( p,     index );
196    TR_NTOHL( &p[4], begin );
197    TR_NTOHL( &p[8], length );
198
199    peer_dbg( "GET  request %d/%d (%d bytes)",
200              index, begin, length );
201
202    /* TODO sanity checks (do we have the piece, etc) */
203
204    if( length > 16384 )
205    {
206        /* Sorry mate */
207        return TR_ERROR;
208    }
209
210    if( peer->outRequestCount >= MAX_REQUEST_COUNT )
211    {
212        tr_err( "Too many requests" );
213        return TR_ERROR;
214    }
215
216    r         = &peer->outRequests[peer->outRequestCount];
217    r->index  = index;
218    r->begin  = begin;
219    r->length = length;
220
221    (peer->outRequestCount)++;
222
223    return TR_OK;
224}
225
226static inline void updateRequests( tr_torrent_t * tor, tr_peer_t * peer,
227                                   int index, int begin )
228{
229    tr_request_t * r;
230    int i, j;
231
232    /* Find this block in the requests list */
233    for( i = 0; i < peer->inRequestCount; i++ )
234    {
235        r = &peer->inRequests[i];
236        if( index == r->index && begin == r->begin )
237        {
238            break;
239        }
240    }
241
242    /* Usually i should be 0, but some clients don't handle multiple
243       request well and drop previous requests */
244    if( i < peer->inRequestCount )
245    {
246        if( i > 0 )
247        {
248            peer_dbg( "not expecting this block yet (%d requests dropped)", i );
249        }
250        i++;
251        for( j = 0; j < i; j++ )
252        {
253            r = &peer->inRequests[j];
254            tr_cpDownloaderRem( tor->completion,
255                                tr_block( r->index, r->begin ) );
256        }
257        peer->inRequestCount -= i;
258        memmove( &peer->inRequests[0], &peer->inRequests[i],
259                 peer->inRequestCount * sizeof( tr_request_t ) );
260    }
261    else
262    {
263        /* Not in the list. Probably because of a cancel that arrived
264           too late */
265    }
266}
267
268static inline int parsePiece( tr_torrent_t * tor, tr_peer_t * peer,
269                              uint8_t * p, int len )
270{
271    int index, begin, block, i, ret;
272
273    TR_NTOHL( p,     index );
274    TR_NTOHL( &p[4], begin );
275    block = tr_block( index, begin );
276
277    peer_dbg( "GET  piece %d/%d (%d bytes)",
278              index, begin, len - 8 );
279
280    updateRequests( tor, peer, index, begin );
281    tor->downloadedCur += len;
282
283    /* Sanity checks */
284    if( len - 8 != tr_blockSize( block ) )
285    {
286        peer_dbg( "wrong size (expecting %d)", tr_blockSize( block ) );
287        return TR_ERROR_ASSERT;
288    }
289    if( tr_cpBlockIsComplete( tor->completion, block ) )
290    {
291        peer_dbg( "have this block already" );
292        return TR_OK;
293    }
294
295    /* Set blame/credit for this piece */
296    if( !peer->blamefield )
297    {
298        peer->blamefield = tr_bitfieldNew( tor->info.pieceCount );
299    }
300    tr_bitfieldAdd( peer->blamefield, index );
301
302    /* Write to disk */
303    if( ( ret = tr_ioWrite( tor->io, index, begin, len - 8, &p[8] ) ) )
304    {
305        return ret;
306    }
307    tr_cpBlockAdd( tor->completion, block );
308    sendCancel( tor, block );
309
310    if( !tr_cpPieceHasAllBlocks( tor->completion, index ) )
311    {
312        return TR_OK;
313    }
314
315    /* Piece is complete, check it */
316    if( ( ret = tr_ioHash( tor->io, index ) ) )
317    {
318        return ret;
319    }
320    if( !tr_cpPieceIsComplete( tor->completion, index ) )
321    {
322        return TR_OK;
323    }
324
325    /* Hash OK */
326    for( i = 0; i < tor->peerCount; i++ )
327    {
328        tr_peer_t * otherPeer;
329        otherPeer = tor->peers[i];
330
331        if( otherPeer->status < PEER_STATUS_CONNECTED )
332            continue;
333
334        sendHave( otherPeer, index );
335        updateInterest( tor, otherPeer );
336    }
337
338    return TR_OK;
339}
340
341static inline int parseCancel( tr_peer_t * peer, uint8_t * p, int len )
342{
343    int index, begin, length;
344    int i;
345    tr_request_t * r;
346
347    if( len != 12 )
348    {
349        peer_dbg( "GET  cancel, invalid" );
350        return TR_ERROR_ASSERT;
351    }
352
353    TR_NTOHL( p,     index );
354    TR_NTOHL( &p[4], begin );
355    TR_NTOHL( &p[8], length );
356
357    peer_dbg( "GET  cancel %d/%d (%d bytes)",
358              index, begin, length );
359
360    for( i = 0; i < peer->outRequestCount; i++ )
361    {
362        r = &peer->outRequests[i];
363        if( r->index == index && r->begin == begin &&
364            r->length == length )
365        {
366            (peer->outRequestCount)--;
367            memmove( &r[0], &r[1], sizeof( tr_request_t ) *
368                    ( peer->outRequestCount - i ) );
369            break;
370        }
371    }
372
373    return TR_OK;
374}
375
376static inline int parsePort( tr_peer_t * peer, uint8_t * p, int len )
377{
378    in_port_t port;
379
380    if( len != 2 )
381    {
382        peer_dbg( "GET  port, invalid" );
383        return TR_ERROR_ASSERT;
384    }
385
386    port = *( (in_port_t *) p );
387    peer_dbg( "GET  port %d", ntohs( port ) );
388
389    return TR_OK;
390}
391
392static inline int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
393                                uint8_t * p, int len )
394{
395    char id;
396
397    /* Type of the message */
398    id = *(p++);
399    len--;
400
401    switch( id )
402    {
403        case PEER_MSG_CHOKE:
404            return parseChoke( tor, peer, len, 1 );
405        case PEER_MSG_UNCHOKE:
406            return parseChoke( tor, peer, len, 0 );
407        case PEER_MSG_INTERESTED:
408            return parseInterested( peer, len, 1 );
409        case PEER_MSG_UNINTERESTED:
410            return parseInterested( peer, len, 0 );
411        case PEER_MSG_HAVE:
412            return parseHave( tor, peer, p, len );
413        case PEER_MSG_BITFIELD:
414            return parseBitfield( tor, peer, p, len );
415        case PEER_MSG_REQUEST:
416            return parseRequest( peer, p, len );
417        case PEER_MSG_PIECE:
418            return parsePiece( tor, peer, p, len );
419        case PEER_MSG_CANCEL:
420            return parseCancel( peer, p, len );
421        case PEER_MSG_PORT:
422            return parsePort( peer, p, len );
423    }
424
425    peer_dbg( "Unknown message '%d'", id );
426    return TR_ERROR;
427}
428
429static inline int parseBufHeader( tr_peer_t * peer )
430{
431    uint8_t * p   = peer->buf;
432
433    if( 4 > peer->pos )
434    {
435        return TR_OK;
436    }
437
438    if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
439    {
440        /* Don't wait until we get 68 bytes, this is wrong
441           already */
442        peer_dbg( "GET  handshake, invalid" );
443        tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
444        return TR_ERROR;
445    }
446    if( peer->pos < 68 )
447    {
448        return TR_OK;
449    }
450    if( memcmp( &p[4], "Torrent protocol", 16 ) )
451    {
452        peer_dbg( "GET  handshake, invalid" );
453        return TR_ERROR;
454    }
455
456    return TR_OK;
457}
458
459static uint8_t * parseBufHash( tr_peer_t * peer )
460{
461    if( 48 > peer->pos )
462    {
463        return NULL;
464    }
465    else
466    {
467        return peer->buf + 28;
468    }
469}
470
471static inline int parseBuf( tr_torrent_t * tor, tr_peer_t * peer )
472{
473    tr_info_t * inf = &tor->info;
474
475    int       i;
476    int       len;
477    uint8_t * p   = peer->buf;
478    uint8_t * end = &p[peer->pos];
479    int       ret;
480
481    if( peer->banned )
482    {
483        /* Don't even parse, we only stay connected */
484        peer->pos = 0;
485        return TR_OK;
486    }
487
488    while( peer->pos >= 4 )
489    {
490        if( peer->status & PEER_STATUS_HANDSHAKE )
491        {
492            char * client;
493
494            if( ( ret = parseBufHeader( peer ) ) )
495            {
496                return ret;
497            }
498
499            if( peer->pos < 68 )
500            {
501                break;
502            }
503
504            if( memcmp( &p[28], inf->hash, 20 ) )
505            {
506                peer_dbg( "GET  handshake, wrong torrent hash" );
507                return TR_ERROR;
508            }
509
510            if( !memcmp( &p[48], tor->id, 20 ) )
511            {
512                /* We are connected to ourselves... */
513                peer_dbg( "GET  handshake, that is us" );
514                return TR_ERROR;
515            }
516
517            peer->status  = PEER_STATUS_CONNECTED;
518            memcpy( peer->id, &p[48], 20 );
519            p            += 68;
520            peer->pos    -= 68;
521
522            for( i = 0; i < tor->peerCount; i++ )
523            {
524                if( tor->peers[i] == peer )
525                {
526                    continue;
527                }
528                if( !peerCmp( peer, tor->peers[i] ) )
529                {
530                    peer_dbg( "GET  handshake, duplicate" );
531                    return TR_ERROR;
532                }
533            }
534
535            client = tr_clientForId( (uint8_t *) peer->id );
536            peer_dbg( "GET  handshake, ok (%s)", client );
537            free( client );
538
539            sendBitfield( tor, peer );
540
541            continue;
542        }
543       
544        /* Get payload size */
545        TR_NTOHL( p, len );
546        p += 4;
547
548        if( len > 9 + tor->blockSize )
549        {
550            /* This should never happen. Drop that peer */
551            peer_dbg( "message too large (%d bytes)", len );
552            return TR_ERROR;
553        }
554
555        if( !len )
556        {
557            /* keep-alive */
558            peer_dbg( "GET  keep-alive" );
559            peer->pos -= 4;
560            continue;
561        }
562
563        if( &p[len] > end )
564        {
565            /* We do not have the entire message */
566            p -= 4;
567            break;
568        }
569
570        /* Remaining data after this message */
571        peer->pos -= 4 + len;
572
573        if( ( ret = parseMessage( tor, peer, p, len ) ) )
574        {
575            return ret;
576        }
577
578        p += len;
579    }
580
581    memmove( peer->buf, p, peer->pos );
582
583    return TR_OK;
584}
Note: See TracBrowser for help on using the repository browser.