source: trunk/libtransmission/peerparse.h @ 2495

Last change on this file since 2495 was 2495, checked in by charles, 15 years ago

fix compiler warnings reported by wereHamster

  • Property svn:keywords set to Date Rev Author Id
File size: 21.5 KB
Line 
1/******************************************************************************
2 * $Id: peerparse.h 2495 2007-07-25 17:27:19Z charles $
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 **********************************************************************/
34
35static int parseChoke( tr_torrent_t  * tor,
36                       tr_peer_t     * peer,
37                       int             len,
38                       int             choking )
39{
40    tr_request_t * r;
41    int i;
42
43    if( len != 0 )
44    {
45        peer_dbg( "GET  %schoke, invalid", choking ? "" : "un" );
46        return TR_ERROR_ASSERT;
47    }
48
49    peer_dbg( "GET  %schoke", choking ? "" : "un" );
50
51    peer->isChokingUs = choking;
52
53    if( choking )
54    {
55        /* Discard all pending requests */
56        for( i = 0; i < peer->inRequestCount; i++ )
57        {
58            r = &peer->inRequests[i];
59            tr_cpDownloaderRem( tor->completion, tr_block(r->index,r->begin) );
60
61            /* According to the spec, all requests are dropped when you
62               are choked, however some clients seem to remember those
63               the next time they unchoke you. Also, if you get quickly
64               choked and unchoked while you are sending requests, you
65               can't know when the other peer received them and how it
66               handled it.
67               This can cause us to receive blocks multiple times and
68               overdownload, so we send 'cancel' messages to try and
69               reduce that. */
70            sendCancel( peer, r->index, r->begin, r->length );
71        }
72        peer->inRequestCount = 0;
73    }
74
75    return TR_OK;
76}
77
78/***********************************************************************
79 * parseInterested
80 ***********************************************************************
81 *
82 **********************************************************************/
83static int parseInterested( tr_peer_t * peer, int len,
84                                   int interested )
85{
86    if( len != 0 )
87    {
88        peer_dbg( "GET  %sinterested, invalid", interested ? "" : "un" );
89        return TR_ERROR_ASSERT;
90    }
91
92    peer_dbg( "GET  %sinterested", interested ? "" : "un" );
93
94    peer->isInterested = interested;
95
96    return TR_OK;
97}
98
99/***********************************************************************
100 * parseHave
101 ***********************************************************************
102 *
103 **********************************************************************/
104static int parseHave( tr_torrent_t * tor, tr_peer_t * peer,
105                             uint8_t * p, int len )
106{
107    tr_info_t * inf = &tor->info;
108    uint32_t piece;
109
110    if( len != 4 )
111    {
112        peer_dbg( "GET  have, invalid" );
113        return TR_ERROR_ASSERT;
114    }
115
116    piece = tr_ntohl( p );
117    if( ( uint32_t )inf->pieceCount <= piece )
118    {
119        peer_dbg( "GET  have, invalid piece" );
120        return TR_ERROR_ASSERT;
121    }
122
123    peer_dbg( "GET  have %d", piece );
124
125    if( !peer->bitfield )
126    {
127        peer->bitfield = tr_bitfieldNew( inf->pieceCount );
128    }
129    if( !tr_bitfieldHas( peer->bitfield, piece ) )
130    {
131        peer->pieceCount++;
132        peer->progress = (float) peer->pieceCount / inf->pieceCount;
133    }
134    tr_bitfieldAdd( peer->bitfield, piece );
135    updateInterest( tor, peer );
136
137    tr_rcTransferred( tor->swarmspeed, tor->info.pieceSize );
138
139    return TR_OK;
140}
141
142static int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer,
143                                 uint8_t * p, int len )
144{
145    tr_info_t * inf = &tor->info;
146    int bitfieldSize;
147    int i;
148
149    bitfieldSize = ( inf->pieceCount + 7 ) / 8;
150   
151    if( len != bitfieldSize )
152    {
153        peer_dbg( "GET  bitfield, wrong size" );
154        return TR_ERROR_ASSERT;
155    }
156
157    /* Make sure the spare bits are unset */
158    if( ( inf->pieceCount & 0x7 ) )
159    {
160        uint8_t lastByte;
161       
162        lastByte   = p[bitfieldSize-1];
163        lastByte <<= inf->pieceCount & 0x7;
164        lastByte  &= 0xFF;
165
166        if( lastByte )
167        {
168            peer_dbg( "GET  bitfield, spare bits set" );
169            return TR_ERROR_ASSERT;
170        }
171    }
172
173    peer_dbg( "GET  bitfield, ok" );
174
175    if( !peer->bitfield )
176    {
177        peer->bitfield = tr_bitfieldNew( inf->pieceCount );
178    }
179    assert( (unsigned)bitfieldSize == peer->bitfield->len );
180    memcpy( peer->bitfield->bits, p, bitfieldSize );
181
182    peer->pieceCount = 0;
183    for( i = 0; i < inf->pieceCount; i++ )
184    {
185        if( tr_bitfieldHas( peer->bitfield, i ) )
186        {
187            peer->pieceCount++;
188        }
189    }
190    peer->progress = (float) peer->pieceCount / inf->pieceCount;
191
192    updateInterest( tor, peer );
193
194    return TR_OK;
195}
196
197static int parseRequest( tr_torrent_t * tor, tr_peer_t * peer,
198                                uint8_t * p, int len )
199{
200    tr_info_t * inf = &tor->info;
201    int index, begin, length;
202    tr_request_t * r;
203
204    if( len != 12 )
205    {
206        peer_dbg( "GET  request, invalid" );
207        return TR_ERROR_ASSERT;
208    }
209
210    if( peer->isChokedByUs )
211    {
212        /* Didn't he get it? */
213        sendChoke( peer, 1 );
214        return TR_OK;
215    }
216   
217    index = tr_ntohl( p );
218    begin = tr_ntohl( &p[4] );
219    length = tr_ntohl( &p[8] );
220
221    if( inf->pieceCount <= index )
222    {
223        peer_dbg( "GET  request, invalid index" );
224        return TR_ERROR_ASSERT;
225    }
226    if( tr_pieceSize( index ) < begin + length )
227    {
228        peer_dbg( "GET  request, invalid begin/length" );
229        return TR_ERROR_ASSERT;
230    }
231
232    peer_dbg( "GET  request %d/%d (%d bytes)",
233              index, begin, length );
234
235    /* TODO sanity checks (do we have the piece, etc) */
236
237    if( length > 16384 )
238    {
239        /* Sorry mate */
240        return TR_ERROR;
241    }
242
243    r = tr_new0( tr_request_t, 1 );
244    r->index = index;
245    r->begin = begin;
246    r->length = length;
247    peer->outRequests = tr_list_append( peer->outRequests, r );
248
249    return TR_OK;
250}
251
252static void updateRequests( tr_peer_t * peer, int index, int begin )
253{
254    tr_request_t * r;
255    int i;
256
257    /* Find this block in the requests list */
258    for( i = 0; i < peer->inRequestCount; i++ )
259    {
260        r = &peer->inRequests[i];
261        if( index == r->index && begin == r->begin )
262        {
263            break;
264        }
265    }
266
267    /* Usually 'i' would be 0, but some clients don't handle multiple
268       requests and drop previous requests, some other clients don't
269       send blocks in the same order we sent the requests */
270    if( i < peer->inRequestCount )
271    {
272        peer->inRequestCount--;
273        memmove( &peer->inRequests[i], &peer->inRequests[i+1],
274                 ( peer->inRequestCount - i ) * sizeof( tr_request_t ) );
275    }
276    else
277    {
278        /* Not in the list. Probably because of a cancel that arrived
279           too late */
280        peer_dbg( "wasn't expecting this block" );
281    }
282}
283
284static int parsePiece( tr_torrent_t * tor, tr_peer_t * peer,
285                              uint8_t * p, int len )
286{
287    tr_info_t * inf = &tor->info;
288    int index, begin, block, i, ret;
289
290    if( 8 > len )
291    {
292        peer_dbg( "GET  piece, too short (8 > %i)", len );
293        return TR_ERROR_ASSERT;
294    }
295
296    index = tr_ntohl( p );
297    begin = tr_ntohl( &p[4] );
298
299    if( inf->pieceCount <= index )
300    {
301        peer_dbg( "GET  piece, invalid index" );
302        return TR_ERROR_ASSERT;
303    }
304    if( tr_pieceSize( index ) < begin + len - 8 )
305    {
306        peer_dbg( "GET  piece, invalid begin/length" );
307        return TR_ERROR_ASSERT;
308    }
309
310    block = tr_block( index, begin );
311
312    peer_dbg( "GET  piece %d/%d (%d bytes)",
313              index, begin, len - 8 );
314
315    updateRequests( peer, index, begin );
316
317    /* Sanity checks */
318    if( len - 8 != tr_blockSize( block ) )
319    {
320        peer_dbg( "wrong size (expecting %d)", tr_blockSize( block ) );
321        return TR_ERROR_ASSERT;
322    }
323    if( tr_cpBlockIsComplete( tor->completion, block ) )
324    {
325        peer_dbg( "have this block already" );
326        return TR_OK;
327    }
328
329    /* Set blame/credit for this piece */
330    if( !peer->blamefield )
331    {
332        peer->blamefield = tr_bitfieldNew( inf->pieceCount );
333    }
334    tr_bitfieldAdd( peer->blamefield, index );
335
336    /* Write to disk */
337    if( ( ret = tr_ioWrite( tor->io, index, begin, len - 8, &p[8] ) ) )
338    {
339        return ret;
340    }
341    tr_cpBlockAdd( tor->completion, block );
342    tr_peerSentBlockToUs( peer, len-8 );
343    broadcastCancel( tor, index, begin, len - 8 );
344
345    if( !tr_cpPieceHasAllBlocks( tor->completion, index ) )
346    {
347        return TR_OK;
348    }
349
350    /* Piece is complete, check it */
351    if( ( ret = tr_ioHash( tor->io, index ) ) )
352    {
353        /* Follow Azureus' and uTorrent's lead in not counting corrupt
354           pieces in our announce data, as it could give us a misleadingly
355           bad share ratio . (Ticket #263) */
356        const int byteCount = tr_pieceSize( index );
357        if( tor->downloadedCur >= (uint64_t)byteCount )
358            tor->downloadedCur -= byteCount;
359
360        return ret;
361    }
362    if( !tr_cpPieceIsComplete( tor->completion, index ) )
363    {
364        return TR_OK;
365    }
366
367    /* Hash OK */
368    for( i = 0; i < tor->peerCount; i++ )
369    {
370        tr_peer_t * otherPeer;
371        otherPeer = tor->peers[i];
372
373        if( otherPeer->status < PEER_STATUS_CONNECTED )
374            continue;
375
376        sendHave( otherPeer, index );
377        updateInterest( tor, otherPeer );
378    }
379
380    return TR_OK;
381}
382
383static int reqCompare( const void * va, const void * vb )
384{
385    const tr_request_t * a = (const tr_request_t *) va;
386    const tr_request_t * b = (const tr_request_t *) vb;
387    if( a->index != b->index ) return a->index - b->index;
388    if( a->begin != b->begin ) return a->begin - b->begin;
389    return a->length - b->length;
390}
391
392static int parseCancel( tr_torrent_t * tor, tr_peer_t * peer,
393                               uint8_t * p, int len )
394{
395    tr_info_t * inf = &tor->info;
396    int index, begin, length;
397    tr_request_t req;
398    tr_list_t * l;
399
400    if( len != 12 )
401    {
402        peer_dbg( "GET  cancel, invalid" );
403        return TR_ERROR_ASSERT;
404    }
405
406    index = tr_ntohl( p );
407    begin = tr_ntohl( &p[4] );
408    length = tr_ntohl( &p[8] );
409
410    if( inf->pieceCount <= index )
411    {
412        peer_dbg( "GET  cancel, invalid index" );
413        return TR_ERROR_ASSERT;
414    }
415    if( tr_pieceSize( index ) < begin + length )
416    {
417        peer_dbg( "GET  cancel, invalid begin/length" );
418        return TR_ERROR_ASSERT;
419    }
420
421    peer_dbg( "GET  cancel %d/%d (%d bytes)",
422              index, begin, length );
423
424    /* remove it from the outRequests list */
425    req.index = index;
426    req.begin = begin;
427    req.length = length;
428    while(( l = tr_list_find( peer->outRequests, reqCompare, &req ) )) {
429        tr_request_t * r = (tr_request_t *) l->data;
430        peer->outRequests = tr_list_remove_data( peer->outRequests, r );
431        tr_free( r );
432    }
433
434    return TR_OK;
435}
436
437static int parsePort( tr_peer_t * peer, uint8_t * p, int len )
438{
439    in_port_t port;
440
441    if( len != 2 )
442    {
443        peer_dbg( "GET  port, invalid" );
444        return TR_ERROR_ASSERT;
445    }
446
447    port = *( (in_port_t *) p );
448    peer_dbg( "GET  port %d", ntohs( port ) );
449
450    return TR_OK;
451}
452
453static int
454parseMessageHeader( tr_peer_t * peer, uint8_t * buf, int buflen,
455                    int * msgid, int * msglen )
456{
457    if( 4 > buflen )
458    {
459        return TR_NET_BLOCK;
460    }
461
462    /* Get payload size */
463    *msglen = tr_ntohl( buf );
464
465    if( 4 + *msglen > buflen )
466    {
467        /* We do not have the entire message */
468        return TR_NET_BLOCK;
469    }
470
471    if( 0 == *msglen )
472    {
473        /* keep-alive */
474        peer_dbg( "GET  keep-alive" );
475        *msgid = AZ_MSG_BT_KEEP_ALIVE;
476        return 4;
477    }
478    else
479    {
480        /* Type of the message */
481        *msgid = buf[4];
482        (*msglen)--;
483        return 5;
484    }
485}
486
487static int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
488                                int id, uint8_t * p, int len )
489{
490    int extid;
491
492    switch( id )
493    {
494        case PEER_MSG_CHOKE:
495            return parseChoke( tor, peer, len, 1 );
496        case PEER_MSG_UNCHOKE:
497            return parseChoke( tor, peer, len, 0 );
498        case PEER_MSG_INTERESTED:
499            return parseInterested( peer, len, 1 );
500        case PEER_MSG_UNINTERESTED:
501            return parseInterested( peer, len, 0 );
502        case PEER_MSG_HAVE:
503            return parseHave( tor, peer, p, len );
504        case PEER_MSG_BITFIELD:
505            return parseBitfield( tor, peer, p, len );
506        case PEER_MSG_REQUEST:
507            return parseRequest( tor, peer, p, len );
508        case PEER_MSG_PIECE:
509            return parsePiece( tor, peer, p, len );
510        case PEER_MSG_CANCEL:
511            return parseCancel( tor, peer, p, len );
512        case PEER_MSG_PORT:
513            return parsePort( peer, p, len );
514        case PEER_MSG_EXTENDED:
515            if( EXTENDED_NOT_SUPPORTED == peer->extStatus )
516            {
517                break;
518            }
519            if( 0 < len )
520            {
521                extid = p[0];
522                p++;
523                len--;
524                if( EXTENDED_HANDSHAKE_ID == extid )
525                {
526                    return parseExtendedHandshake( peer, p, len );
527                }
528                else if( 0 < peer->pexStatus && extid == peer->pexStatus )
529                {
530                    return parseUTPex( tor, peer, p, len );
531                }
532                peer_dbg( "GET  unknown extended message '%hhu'", extid );
533            }
534            /* ignore the unknown extension */
535            return 0;
536        case AZ_MSG_BT_KEEP_ALIVE:
537            return TR_OK;
538        case AZ_MSG_AZ_PEER_EXCHANGE:
539            if( peer->azproto && peer->pexStatus )
540            {
541                return parseAZPex( tor, peer, p, len );
542            }
543            break;
544        case AZ_MSG_INVALID:
545            return 0;
546    }
547
548    tr_err( "GET  unknown message '%d'", id );
549    return TR_ERROR;
550}
551
552static int parseBufHeader( tr_peer_t * peer )
553{
554    static uint8_t badproto_http[] =
555"HTTP/1.0 400 Nice try...\015\012"
556"Content-type: text/plain\015\012"
557"\015\012";
558    static uint8_t badproto_tinfoil[] =
559"This is not a rootkit or other backdoor, it's a BitTorrent\015\012"
560"client. Really. Why should you be worried, can't you read this\015\012"
561"reassuring message? Now just listen to this social engi, er, I mean,\015\012"
562"completely truthful statement, and go about your business. Your box is\015\012"
563"safe and completely impregnable, the marketing hype for your OS even\015\012"
564"says so. You can believe everything you read. Now move along, nothing\015\012"
565"to see here.";
566    uint8_t * p   = peer->buf;
567
568    if( 4 > peer->pos )
569    {
570        return TR_OK;
571    }
572
573    if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
574    {
575        /* Don't wait until we get 68 bytes, this is wrong
576           already */
577        peer_dbg( "GET  handshake, invalid" );
578        if( 0 == memcmp( p, "GET ", 4 ) || 0 == memcmp( p, "HEAD", 4 ) )
579        {
580            tr_netSend( peer->socket, badproto_http, sizeof badproto_http - 1 );
581        }
582        tr_netSend( peer->socket, badproto_tinfoil, sizeof badproto_tinfoil - 1 );
583        return TR_ERROR;
584    }
585    if( peer->pos < 68 )
586    {
587        return TR_OK;
588    }
589    if( memcmp( &p[4], "Torrent protocol", 16 ) )
590    {
591        peer_dbg( "GET  handshake, invalid" );
592        return TR_ERROR;
593    }
594
595    return TR_OK;
596}
597
598static const uint8_t * parseBufHash( const tr_peer_t * peer )
599{
600    if( 48 > peer->pos )
601    {
602        return NULL;
603    }
604    else
605    {
606        return peer->buf + 28;
607    }
608}
609
610static int parseHandshake( tr_torrent_t * tor, tr_peer_t * peer )
611{
612    tr_info_t * inf = &tor->info;
613    int         ii;
614
615    if( memcmp( &peer->buf[28], inf->hash, SHA_DIGEST_LENGTH ) )
616    {
617        peer_dbg( "GET  handshake, wrong torrent hash" );
618        return TR_ERROR;
619    }
620
621    if( !memcmp( &peer->buf[48], tor->peer_id, TR_ID_LEN ) )
622    {
623        /* We are connected to ourselves... */
624        peer_dbg( "GET  handshake, that is us" );
625        return TR_ERROR;
626    }
627
628    memcpy( peer->id, &peer->buf[48], TR_ID_LEN );
629
630    for( ii = 0; ii < tor->peerCount; ii++ )
631    {
632        if( tor->peers[ii] == peer )
633        {
634            continue;
635        }
636        if( !peerCmp( peer, tor->peers[ii] ) )
637        {
638            peer_dbg( "GET  handshake, duplicate" );
639            return TR_ERROR;
640        }
641    }
642
643    if( PEER_SUPPORTS_EXTENDED_MESSAGES( &peer->buf[20] ) )
644    {
645        peer->status = PEER_STATUS_CONNECTED;
646        peer->extStatus = EXTENDED_SUPPORTED;
647        peer_dbg( "GET  handshake, ok (%s) extended messaging supported",
648                  tr_peerClient( peer ) );
649    }
650    else if( PEER_SUPPORTS_AZUREUS_PROTOCOL( &peer->buf[20] ) )
651    {
652        peer->status  = PEER_STATUS_AZ_GIVER;
653        peer->azproto = 1;
654        peer->date    = tr_date();
655        peer_dbg( "GET  handshake, ok (%s) will use azureus protocol",
656                  tr_peerClient( peer ) );
657    }
658    else
659    {
660        peer->status = PEER_STATUS_CONNECTED;
661        peer_dbg( "GET  handshake, ok (%s)", tr_peerClient( peer ) );
662    }
663
664    return TR_OK;
665}
666
667static int sendInitial( tr_torrent_t * tor, tr_peer_t * peer )
668{
669    if( PEER_STATUS_CONNECTED != peer->status )
670    {
671        return TR_OK;
672    }
673
674    if( EXTENDED_SUPPORTED == peer->extStatus )
675    {
676        if( sendExtended( tor, peer, EXTENDED_HANDSHAKE_ID ) )
677        {
678            return TR_ERROR;
679        }
680        peer->extStatus = EXTENDED_HANDSHAKE;
681    }
682
683    sendBitfield( tor, peer );
684
685    return TR_OK;
686}
687
688static int parseBuf( tr_torrent_t * tor, tr_peer_t * peer )
689{
690    int       len, ret, msgid;
691    uint8_t * buf;
692
693    buf = peer->buf;
694
695    if( peer->banned )
696    {
697        /* Don't even parse, we only stay connected */
698        peer->pos = 0;
699        return TR_OK;
700    }
701
702    while( peer->pos >= 4 )
703    {
704        if( PEER_STATUS_HANDSHAKE == peer->status )
705        {
706            ret = parseBufHeader( peer );
707            if( ret )
708            {
709                return ret;
710            }
711
712            if( peer->pos < 68 )
713            {
714                break;
715            }
716
717            ret = parseHandshake( tor, peer );
718            if( 0 > ret )
719            {
720                return ret;
721            }
722            buf       += 68;
723            peer->pos -= 68;
724
725            ret = sendInitial( tor, peer );
726            if( ret )
727            {
728                return ret;
729            }
730
731            continue;
732        }
733
734        if( PEER_STATUS_AZ_RECEIVER == peer->status )
735        {
736            ret = parseAZMessageHeader( peer, buf, peer->pos, &msgid, &len );
737            if( TR_NET_BLOCK & ret )
738            {
739                break;
740            }
741            else if( TR_NET_CLOSE & ret )
742            {
743                return TR_ERROR;
744            }
745
746            buf       += ret;
747            peer->pos -= ret;
748            assert( len <= peer->pos );
749            if( AZ_MSG_AZ_HANDSHAKE != msgid ||
750                parseAZHandshake( peer, buf, len ) )
751            {
752                return TR_ERROR;
753            }
754            buf         += len;
755            peer->pos   -= len;
756            assert( 0 <= peer->pos );
757            peer->status = PEER_STATUS_CONNECTED;
758
759            ret = sendInitial( tor, peer );
760            if( ret )
761            {
762                return ret;
763            }
764
765            continue;
766        }
767
768        if( PEER_STATUS_CONNECTED != peer->status )
769        {
770            break;
771        }
772
773        if( peer->azproto )
774        {
775            ret = parseAZMessageHeader( peer, buf, peer->pos, &msgid, &len );
776        }
777        else
778        {
779            ret = parseMessageHeader( peer, buf, peer->pos, &msgid, &len );
780        }
781        if( TR_NET_BLOCK & ret )
782        {
783            break;
784        }
785        else if( TR_NET_CLOSE & ret )
786        {
787            return TR_ERROR;
788        }
789
790#if 0
791        if( len > 8 + tor->blockSize )
792        {
793            /* This should never happen. Drop that peer */
794            /* XXX could an extended message be longer than this? */
795            peer_dbg( "message too large (%d bytes)", len );
796            return TR_ERROR;
797        }
798#endif
799
800        buf       += ret;
801        peer->pos -= ret;
802        assert( 0 <= peer->pos );
803
804        if( ( ret = parseMessage( tor, peer, msgid, buf, len ) ) )
805        {
806            return ret;
807        }
808
809        buf       += len;
810        peer->pos -= len;
811        assert( 0 <= peer->pos );
812    }
813
814    if( 0 < peer->pos )
815    {
816        memmove( peer->buf, buf, peer->pos );
817    }
818
819    return TR_OK;
820}
Note: See TracBrowser for help on using the repository browser.