source: trunk/libtransmission/peerparse.h @ 1535

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

Check all offsets and lengths received from the peer before using them.

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