source: branches/pex/libtransmission/peerparse.h @ 1554

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

Remove old message length check that was causing some messages to be ignored.
Send the correct bitfield in the bitfield message.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.3 KB
Line 
1/******************************************************************************
2 * $Id: peerparse.h 1554 2007-03-09 01:41:45Z joshe $
3 *
4 * Copyright (c) 2005-2007 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
444parseMessageHeader( tr_peer_t * peer, uint8_t * buf, int buflen,
445                    int * msgid, int * msglen )
446{
447    if( 4 > buflen )
448    {
449        return TR_NET_BLOCK;
450    }
451
452    /* Get payload size */
453    TR_NTOHL( buf, *msglen );
454
455    if( 4 + *msglen > buflen )
456    {
457        /* We do not have the entire message */
458        return TR_NET_BLOCK;
459    }
460
461    if( 0 == *msglen )
462    {
463        /* keep-alive */
464        peer_dbg( "GET  keep-alive" );
465        *msgid = AZ_MSG_BT_KEEP_ALIVE;
466        return 4;
467    }
468    else
469    {
470        /* Type of the message */
471        *msgid = buf[4];
472        (*msglen)--;
473        return 5;
474    }
475}
476
477static inline int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
478                                int id, uint8_t * p, int len )
479{
480    int extid;
481
482    switch( id )
483    {
484        case PEER_MSG_CHOKE:
485            return parseChoke( tor, peer, len, 1 );
486        case PEER_MSG_UNCHOKE:
487            return parseChoke( tor, peer, len, 0 );
488        case PEER_MSG_INTERESTED:
489            return parseInterested( peer, len, 1 );
490        case PEER_MSG_UNINTERESTED:
491            return parseInterested( peer, len, 0 );
492        case PEER_MSG_HAVE:
493            return parseHave( tor, peer, p, len );
494        case PEER_MSG_BITFIELD:
495            return parseBitfield( tor, peer, p, len );
496        case PEER_MSG_REQUEST:
497            return parseRequest( tor, peer, p, len );
498        case PEER_MSG_PIECE:
499            return parsePiece( tor, peer, p, len );
500        case PEER_MSG_CANCEL:
501            return parseCancel( tor, peer, p, len );
502        case PEER_MSG_PORT:
503            return parsePort( peer, p, len );
504        case PEER_MSG_EXTENDED:
505            if( EXTENDED_NOT_SUPPORTED == peer->extStatus )
506            {
507                break;
508            }
509            if( 0 < len )
510            {
511                extid = p[0];
512                p++;
513                len--;
514                if( EXTENDED_HANDSHAKE_ID == extid )
515                {
516                    return parseExtendedHandshake( peer, p, len );
517                }
518                else if( 0 < peer->pexStatus && extid == peer->pexStatus )
519                {
520                    return parseUTPex( tor, peer, p, len );
521                }
522                peer_dbg( "Unknown extended message '%hhu'", extid );
523            }
524            return 1;
525        case AZ_MSG_AZ_PEER_EXCHANGE:
526            if( peer->azproto && peer->pexStatus )
527            {
528                return parseAZPex( tor, peer, p, len );
529            }
530            break;
531        case AZ_MSG_INVALID:
532            return 0;
533    }
534
535    peer_dbg( "Unknown message '%d'", id );
536    return TR_ERROR;
537}
538
539static inline int parseBufHeader( tr_peer_t * peer )
540{
541    uint8_t * p   = peer->buf;
542
543    if( 4 > peer->pos )
544    {
545        return TR_OK;
546    }
547
548    if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
549    {
550        /* Don't wait until we get 68 bytes, this is wrong
551           already */
552        peer_dbg( "GET  handshake, invalid" );
553        tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
554        return TR_ERROR;
555    }
556    if( peer->pos < 68 )
557    {
558        return TR_OK;
559    }
560    if( memcmp( &p[4], "Torrent protocol", 16 ) )
561    {
562        peer_dbg( "GET  handshake, invalid" );
563        return TR_ERROR;
564    }
565
566    return TR_OK;
567}
568
569static uint8_t * parseBufHash( tr_peer_t * peer )
570{
571    if( 48 > peer->pos )
572    {
573        return NULL;
574    }
575    else
576    {
577        return peer->buf + 28;
578    }
579}
580
581static inline int parseBuf( tr_torrent_t * tor, tr_peer_t * peer )
582{
583    tr_info_t * inf = &tor->info;
584
585    int       i;
586    int       len;
587    uint8_t * p   = peer->buf;
588    int       ret;
589    int       msgid;
590
591    if( peer->banned )
592    {
593        /* Don't even parse, we only stay connected */
594        peer->pos = 0;
595        return TR_OK;
596    }
597
598    while( peer->pos >= 4 )
599    {
600        if( PEER_STATUS_HANDSHAKE == peer->status )
601        {
602            char * client;
603
604            if( ( ret = parseBufHeader( peer ) ) )
605            {
606                return ret;
607            }
608
609            if( peer->pos < 68 )
610            {
611                break;
612            }
613
614            if( memcmp( &p[28], inf->hash, 20 ) )
615            {
616                peer_dbg( "GET  handshake, wrong torrent hash" );
617                return TR_ERROR;
618            }
619
620            if( !memcmp( &p[48], tor->id, 20 ) )
621            {
622                /* We are connected to ourselves... */
623                peer_dbg( "GET  handshake, that is us" );
624                return TR_ERROR;
625            }
626
627            peer->status = PEER_STATUS_CONNECTED;
628            if( PEER_SUPPORTS_EXTENDED_MESSAGES( &p[20] ) )
629            {
630                peer_dbg( "extended messages supported" );
631                peer->extStatus = EXTENDED_SUPPORTED;
632            }
633            else if( PEER_SUPPORTS_AZUREUS_PROTOCOL( &p[20] ) )
634            {
635                peer->azproto = 1;
636                peer->status  = PEER_STATUS_AZ_GIVER;
637                peer->date    = tr_date();
638            }
639            memcpy( peer->id, &p[48], 20 );
640            p            += 68;
641            peer->pos    -= 68;
642
643            for( i = 0; i < tor->peerCount; i++ )
644            {
645                if( tor->peers[i] == peer )
646                {
647                    continue;
648                }
649                if( !peerCmp( peer, tor->peers[i] ) )
650                {
651                    peer_dbg( "GET  handshake, duplicate" );
652                    return TR_ERROR;
653                }
654            }
655
656            client = tr_clientForId( (uint8_t *) peer->id );
657            peer_dbg( "GET  handshake, ok (%s)", client );
658            free( client );
659
660          justconnected:
661            if( PEER_STATUS_CONNECTED == peer->status )
662            {
663                if( EXTENDED_SUPPORTED == peer->extStatus )
664                {
665                    if( sendExtended( tor, peer, EXTENDED_HANDSHAKE_ID ) )
666                    {
667                        return TR_ERROR;
668                    }
669                    peer->extStatus = EXTENDED_HANDSHAKE;
670                }
671                sendBitfield( tor, peer );
672            }
673
674            continue;
675        }
676        else if( peer->status < PEER_STATUS_CONNECTED )
677        {
678            ret = parseAZMessageHeader( peer, p, peer->pos, &msgid, &len );
679            if( TR_NET_BLOCK & ret )
680            {
681                break;
682            }
683            else if( TR_NET_CLOSE & ret )
684            {
685                return TR_ERROR;
686            }
687            else
688            {
689                p         += ret;
690                peer->pos -= ret;
691                assert( len <= peer->pos );
692                if( AZ_MSG_AZ_HANDSHAKE != msgid ||
693                    parseAZHandshake( peer, p, len ) )
694                {
695                    return TR_ERROR;
696                }
697                p           += len;
698                peer->pos   -= len;
699                assert( 0 <= peer->pos );
700                peer->status = PEER_STATUS_CONNECTED;
701                goto justconnected;
702            }
703        }
704
705        if( peer->azproto )
706        {
707            ret = parseAZMessageHeader( peer, p, peer->pos, &msgid, &len );
708        }
709        else
710        {
711            ret = parseMessageHeader( peer, p, peer->pos, &msgid, &len );
712        }
713        if( TR_NET_BLOCK & ret )
714        {
715            break;
716        }
717        else if( TR_NET_CLOSE & ret )
718        {
719            return TR_ERROR;
720        }
721
722        if( len > 8 + tor->blockSize )
723        {
724            /* This should never happen. Drop that peer */
725            /* XXX could an extended message be longer than this? */
726            peer_dbg( "message too large (%d bytes)", len );
727            return TR_ERROR;
728        }
729
730        p         += ret;
731        peer->pos -= ret;
732        assert( 0 <= peer->pos );
733
734        if( ( ret = parseMessage( tor, peer, msgid, p, len ) ) )
735        {
736            return ret;
737        }
738
739        p         += len;
740        peer->pos -= len;
741        assert( 0 <= peer->pos );
742    }
743
744    memmove( peer->buf, p, peer->pos );
745
746    return TR_OK;
747}
Note: See TracBrowser for help on using the repository browser.