source: trunk/libtransmission/peerparse.h @ 1600

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

Unbreak azureus peer protocol.
Add more peer debug messages.
Fix pex interval.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.8 KB
Line 
1/******************************************************************************
2 * $Id: peerparse.h 1600 2007-03-28 06:28:34Z 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( "GET  unknown extended message '%hhu'", extid );
523            }
524            return 1;
525        case AZ_MSG_BT_KEEP_ALIVE:
526            return TR_OK;
527        case AZ_MSG_AZ_PEER_EXCHANGE:
528            if( peer->azproto && peer->pexStatus )
529            {
530                return parseAZPex( tor, peer, p, len );
531            }
532            break;
533        case AZ_MSG_INVALID:
534            return 0;
535    }
536
537    peer_dbg( "GET  unknown message '%d'", id );
538    return TR_ERROR;
539}
540
541static inline int parseBufHeader( tr_peer_t * peer )
542{
543    uint8_t * p   = peer->buf;
544
545    if( 4 > peer->pos )
546    {
547        return TR_OK;
548    }
549
550    if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
551    {
552        /* Don't wait until we get 68 bytes, this is wrong
553           already */
554        peer_dbg( "GET  handshake, invalid" );
555        tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
556        return TR_ERROR;
557    }
558    if( peer->pos < 68 )
559    {
560        return TR_OK;
561    }
562    if( memcmp( &p[4], "Torrent protocol", 16 ) )
563    {
564        peer_dbg( "GET  handshake, invalid" );
565        return TR_ERROR;
566    }
567
568    return TR_OK;
569}
570
571static uint8_t * parseBufHash( tr_peer_t * peer )
572{
573    if( 48 > peer->pos )
574    {
575        return NULL;
576    }
577    else
578    {
579        return peer->buf + 28;
580    }
581}
582
583static inline int parseHandshake( tr_torrent_t * tor, tr_peer_t * peer )
584{
585    tr_info_t * inf = &tor->info;
586    int         ii;
587    char      * client;
588
589    if( memcmp( &peer->buf[28], inf->hash, SHA_DIGEST_LENGTH ) )
590    {
591        peer_dbg( "GET  handshake, wrong torrent hash" );
592        return TR_ERROR;
593    }
594
595    if( !memcmp( &peer->buf[48], tor->id, TR_ID_LEN ) )
596    {
597        /* We are connected to ourselves... */
598        peer_dbg( "GET  handshake, that is us" );
599        return TR_ERROR;
600    }
601
602    memcpy( peer->id, &peer->buf[48], TR_ID_LEN );
603
604    for( ii = 0; ii < tor->peerCount; ii++ )
605    {
606        if( tor->peers[ii] == peer )
607        {
608            continue;
609        }
610        if( !peerCmp( peer, tor->peers[ii] ) )
611        {
612            peer_dbg( "GET  handshake, duplicate" );
613            return TR_ERROR;
614        }
615    }
616
617    client = tr_clientForId( (uint8_t *) peer->id );
618    if( PEER_SUPPORTS_EXTENDED_MESSAGES( &peer->buf[20] ) )
619    {
620        peer->status = PEER_STATUS_CONNECTED;
621        peer->extStatus = EXTENDED_SUPPORTED;
622        peer_dbg( "GET  handshake, ok (%s) extended messaging supported",
623                  client );
624    }
625    else if( PEER_SUPPORTS_AZUREUS_PROTOCOL( &peer->buf[20] ) )
626    {
627        peer->status  = PEER_STATUS_AZ_GIVER;
628        peer->azproto = 1;
629        peer->date    = tr_date();
630        peer_dbg( "GET  handshake, ok (%s) will use azureus protocol",
631                  client );
632    }
633    else
634    {
635        peer->status = PEER_STATUS_CONNECTED;
636        peer_dbg( "GET  handshake, ok (%s)", client );
637    }
638    free( client );
639
640    return TR_OK;
641}
642
643static inline int sendInitial( tr_torrent_t * tor, tr_peer_t * peer )
644{
645    if( PEER_STATUS_CONNECTED != peer->status )
646    {
647        return TR_OK;
648    }
649
650    if( EXTENDED_SUPPORTED == peer->extStatus )
651    {
652        if( sendExtended( tor, peer, EXTENDED_HANDSHAKE_ID ) )
653        {
654            return TR_ERROR;
655        }
656        peer->extStatus = EXTENDED_HANDSHAKE;
657    }
658
659    sendBitfield( tor, peer );
660
661    return TR_OK;
662}
663
664static inline int parseBuf( tr_torrent_t * tor, tr_peer_t * peer )
665{
666    int       len, ret, msgid;
667    uint8_t * buf;
668
669    buf = peer->buf;
670
671    if( peer->banned )
672    {
673        /* Don't even parse, we only stay connected */
674        peer->pos = 0;
675        return TR_OK;
676    }
677
678    while( peer->pos >= 4 )
679    {
680        if( PEER_STATUS_HANDSHAKE == peer->status )
681        {
682            ret = parseBufHeader( peer );
683            if( ret )
684            {
685                return ret;
686            }
687
688            if( peer->pos < 68 )
689            {
690                break;
691            }
692
693            ret = parseHandshake( tor, peer );
694            if( 0 > ret )
695            {
696                return ret;
697            }
698            buf       += 68;
699            peer->pos -= 68;
700
701            ret = sendInitial( tor, peer );
702            if( ret )
703            {
704                return ret;
705            }
706
707            continue;
708        }
709
710        if( PEER_STATUS_AZ_RECEIVER == peer->status )
711        {
712            ret = parseAZMessageHeader( peer, buf, peer->pos, &msgid, &len );
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            buf       += ret;
723            peer->pos -= ret;
724            assert( len <= peer->pos );
725            if( AZ_MSG_AZ_HANDSHAKE != msgid ||
726                parseAZHandshake( peer, buf, len ) )
727            {
728                return TR_ERROR;
729            }
730            buf         += len;
731            peer->pos   -= len;
732            assert( 0 <= peer->pos );
733            peer->status = PEER_STATUS_CONNECTED;
734
735            ret = sendInitial( tor, peer );
736            if( ret )
737            {
738                return ret;
739            }
740
741            continue;
742        }
743
744        if( PEER_STATUS_CONNECTED != peer->status )
745        {
746            break;
747        }
748
749        if( peer->azproto )
750        {
751            ret = parseAZMessageHeader( peer, buf, peer->pos, &msgid, &len );
752        }
753        else
754        {
755            ret = parseMessageHeader( peer, buf, peer->pos, &msgid, &len );
756        }
757        if( TR_NET_BLOCK & ret )
758        {
759            break;
760        }
761        else if( TR_NET_CLOSE & ret )
762        {
763            return TR_ERROR;
764        }
765
766#if 0
767        if( len > 8 + tor->blockSize )
768        {
769            /* This should never happen. Drop that peer */
770            /* XXX could an extended message be longer than this? */
771            peer_dbg( "message too large (%d bytes)", len );
772            return TR_ERROR;
773        }
774#endif
775
776        buf       += ret;
777        peer->pos -= ret;
778        assert( 0 <= peer->pos );
779
780        if( ( ret = parseMessage( tor, peer, msgid, buf, len ) ) )
781        {
782            return ret;
783        }
784
785        buf       += len;
786        peer->pos -= len;
787        assert( 0 <= peer->pos );
788    }
789
790    if( 0 < peer->pos )
791    {
792        memmove( peer->buf, buf, peer->pos );
793    }
794
795    return TR_OK;
796}
Note: See TracBrowser for help on using the repository browser.