source: trunk/libtransmission/peerparse.h @ 2231

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

lots of performance improvements. fun!

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