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

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

Implement Azureus peer protocol, including PEX message.
Implement extended messages, including uTorrent PEX.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.4 KB
Line 
1/******************************************************************************
2 * $Id: peerparse.h 1540 2007-03-08 04:06:58Z 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        /* don't try to parse zero-length messages */
735        if( !len )
736        {
737            continue;
738        }
739
740        if( ( ret = parseMessage( tor, peer, msgid, p, len ) ) )
741        {
742            return ret;
743        }
744
745        p         += len;
746        peer->pos -= len;
747        assert( 0 <= peer->pos );
748    }
749
750    memmove( peer->buf, p, peer->pos );
751
752    return TR_OK;
753}
Note: See TracBrowser for help on using the repository browser.