source: trunk/libtransmission/peerparse.h @ 1442

Last change on this file since 1442 was 1442, checked in by joshe, 16 years ago

Clean up the peer message code a little.

  • Property svn:keywords set to Date Rev Author Id
File size: 14.6 KB
Line 
1/******************************************************************************
2 * $Id: peerparse.h 1442 2007-01-28 02:23:09Z joshe $
3 *
4 * Copyright (c) 2005-2006 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    uint32_t piece;
94
95    if( len != 4 )
96    {
97        peer_dbg( "GET  have, invalid" );
98        return TR_ERROR_ASSERT;
99    }
100
101    TR_NTOHL( p, piece );
102
103    peer_dbg( "GET  have %d", piece );
104
105    if( !peer->bitfield )
106    {
107        peer->bitfield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
108    }
109    if( !tr_bitfieldHas( peer->bitfield, piece ) )
110    {
111        peer->pieceCount++;
112        peer->progress = (float) peer->pieceCount / tor->info.pieceCount;
113    }
114    tr_bitfieldAdd( peer->bitfield, piece );
115    updateInterest( tor, peer );
116
117    tr_rcTransferred( tor->swarmspeed, tor->info.pieceSize );
118
119    return TR_OK;
120}
121
122static inline int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer,
123                                 uint8_t * p, int len )
124{
125    tr_info_t * inf = &tor->info;
126    int bitfieldSize;
127    int i;
128
129    bitfieldSize = ( inf->pieceCount + 7 ) / 8;
130   
131    if( len != bitfieldSize )
132    {
133        peer_dbg( "GET  bitfield, wrong size" );
134        return TR_ERROR_ASSERT;
135    }
136
137    /* Make sure the spare bits are unset */
138    if( ( inf->pieceCount & 0x7 ) )
139    {
140        uint8_t lastByte;
141       
142        lastByte   = p[bitfieldSize-1];
143        lastByte <<= inf->pieceCount & 0x7;
144        lastByte  &= 0xFF;
145
146        if( lastByte )
147        {
148            peer_dbg( "GET  bitfield, spare bits set" );
149            return TR_ERROR_ASSERT;
150        }
151    }
152
153    peer_dbg( "GET  bitfield, ok" );
154
155    if( !peer->bitfield )
156    {
157        peer->bitfield = malloc( bitfieldSize );
158    }
159    memcpy( peer->bitfield, p, bitfieldSize );
160
161    peer->pieceCount = 0;
162    for( i = 0; i < inf->pieceCount; i++ )
163    {
164        if( tr_bitfieldHas( peer->bitfield, i ) )
165        {
166            peer->pieceCount++;
167        }
168    }
169    peer->progress = (float) peer->pieceCount / inf->pieceCount;
170
171    updateInterest( tor, peer );
172
173    return TR_OK;
174}
175
176static inline int parseRequest( tr_peer_t * peer, uint8_t * p, int len )
177{
178    int index, begin, length;
179    tr_request_t * r;
180
181    if( len != 12 )
182    {
183        peer_dbg( "GET  request, invalid" );
184        return TR_ERROR_ASSERT;
185    }
186
187    if( peer->amChoking )
188    {
189        /* Didn't he get it? */
190        sendChoke( peer, 1 );
191        return TR_OK;
192    }
193   
194    TR_NTOHL( p,     index );
195    TR_NTOHL( &p[4], begin );
196    TR_NTOHL( &p[8], length );
197
198    peer_dbg( "GET  request %d/%d (%d bytes)",
199              index, begin, length );
200
201    /* TODO sanity checks (do we have the piece, etc) */
202
203    if( length > 16384 )
204    {
205        /* Sorry mate */
206        return TR_ERROR;
207    }
208
209    if( peer->outRequestCount >= MAX_REQUEST_COUNT )
210    {
211        tr_err( "Too many requests" );
212        return TR_ERROR;
213    }
214
215    r         = &peer->outRequests[peer->outRequestCount];
216    r->index  = index;
217    r->begin  = begin;
218    r->length = length;
219
220    (peer->outRequestCount)++;
221
222    return TR_OK;
223}
224
225static inline void updateRequests( tr_torrent_t * tor, tr_peer_t * peer,
226                                   int index, int begin )
227{
228    tr_request_t * r;
229    int i, j;
230
231    /* Find this block in the requests list */
232    for( i = 0; i < peer->inRequestCount; i++ )
233    {
234        r = &peer->inRequests[i];
235        if( index == r->index && begin == r->begin )
236        {
237            break;
238        }
239    }
240
241    /* Usually i should be 0, but some clients don't handle multiple
242       request well and drop previous requests */
243    if( i < peer->inRequestCount )
244    {
245        if( i > 0 )
246        {
247            peer_dbg( "not expecting this block yet (%d requests dropped)", i );
248        }
249        i++;
250        for( j = 0; j < i; j++ )
251        {
252            r = &peer->inRequests[j];
253            tr_cpDownloaderRem( tor->completion,
254                                tr_block( r->index, r->begin ) );
255        }
256        peer->inRequestCount -= i;
257        memmove( &peer->inRequests[0], &peer->inRequests[i],
258                 peer->inRequestCount * sizeof( tr_request_t ) );
259    }
260    else
261    {
262        /* Not in the list. Probably because of a cancel that arrived
263           too late */
264    }
265}
266
267static inline int parsePiece( tr_torrent_t * tor, tr_peer_t * peer,
268                              uint8_t * p, int len )
269{
270    int index, begin, block, i, ret;
271
272    TR_NTOHL( p,     index );
273    TR_NTOHL( &p[4], begin );
274    block = tr_block( index, begin );
275
276    peer_dbg( "GET  piece %d/%d (%d bytes)",
277              index, begin, len - 8 );
278
279    updateRequests( tor, peer, index, begin );
280    tor->downloadedCur += len;
281
282    /* Sanity checks */
283    if( len - 8 != tr_blockSize( block ) )
284    {
285        peer_dbg( "wrong size (expecting %d)", tr_blockSize( block ) );
286        return TR_ERROR_ASSERT;
287    }
288    if( tr_cpBlockIsComplete( tor->completion, block ) )
289    {
290        peer_dbg( "have this block already" );
291        return TR_OK;
292    }
293
294    /* Set blame/credit for this piece */
295    if( !peer->blamefield )
296    {
297        peer->blamefield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
298    }
299    tr_bitfieldAdd( peer->blamefield, index );
300
301    /* Write to disk */
302    if( ( ret = tr_ioWrite( tor->io, index, begin, len - 8, &p[8] ) ) )
303    {
304        return ret;
305    }
306    tr_cpBlockAdd( tor->completion, block );
307    sendCancel( tor, block );
308
309    if( !tr_cpPieceHasAllBlocks( tor->completion, index ) )
310    {
311        return TR_OK;
312    }
313
314    /* Piece is complete, check it */
315    if( ( ret = tr_ioHash( tor->io, index ) ) )
316    {
317        return ret;
318    }
319    if( !tr_cpPieceIsComplete( tor->completion, index ) )
320    {
321        return TR_OK;
322    }
323
324    /* Hash OK */
325    for( i = 0; i < tor->peerCount; i++ )
326    {
327        tr_peer_t * otherPeer;
328        otherPeer = tor->peers[i];
329
330        if( otherPeer->status < PEER_STATUS_CONNECTED )
331            continue;
332
333        sendHave( otherPeer, index );
334        updateInterest( tor, otherPeer );
335    }
336
337    return TR_OK;
338}
339
340static inline int parseCancel( tr_peer_t * peer, uint8_t * p, int len )
341{
342    int index, begin, length;
343    int i;
344    tr_request_t * r;
345
346    if( len != 12 )
347    {
348        peer_dbg( "GET  cancel, invalid" );
349        return TR_ERROR_ASSERT;
350    }
351
352    TR_NTOHL( p,     index );
353    TR_NTOHL( &p[4], begin );
354    TR_NTOHL( &p[8], length );
355
356    peer_dbg( "GET  cancel %d/%d (%d bytes)",
357              index, begin, length );
358
359    for( i = 0; i < peer->outRequestCount; i++ )
360    {
361        r = &peer->outRequests[i];
362        if( r->index == index && r->begin == begin &&
363            r->length == length )
364        {
365            (peer->outRequestCount)--;
366            memmove( &r[0], &r[1], sizeof( tr_request_t ) *
367                    ( peer->outRequestCount - i ) );
368            break;
369        }
370    }
371
372    return TR_OK;
373}
374
375static inline int parsePort( tr_peer_t * peer, uint8_t * p, int len )
376{
377    in_port_t port;
378
379    if( len != 2 )
380    {
381        peer_dbg( "GET  port, invalid" );
382        return TR_ERROR_ASSERT;
383    }
384
385    port = *( (in_port_t *) p );
386    peer_dbg( "GET  port %d", ntohs( port ) );
387
388    return TR_OK;
389}
390
391static inline int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
392                                uint8_t * p, int len )
393{
394    char id;
395
396    /* Type of the message */
397    id = *(p++);
398    len--;
399
400    switch( id )
401    {
402        case PEER_MSG_CHOKE:
403            return parseChoke( tor, peer, len, 1 );
404        case PEER_MSG_UNCHOKE:
405            return parseChoke( tor, peer, len, 0 );
406        case PEER_MSG_INTERESTED:
407            return parseInterested( peer, len, 1 );
408        case PEER_MSG_UNINTERESTED:
409            return parseInterested( peer, len, 0 );
410        case PEER_MSG_HAVE:
411            return parseHave( tor, peer, p, len );
412        case PEER_MSG_BITFIELD:
413            return parseBitfield( tor, peer, p, len );
414        case PEER_MSG_REQUEST:
415            return parseRequest( peer, p, len );
416        case PEER_MSG_PIECE:
417            return parsePiece( tor, peer, p, len );
418        case PEER_MSG_CANCEL:
419            return parseCancel( peer, p, len );
420        case PEER_MSG_PORT:
421            return parsePort( peer, p, len );
422    }
423
424    peer_dbg( "Unknown message '%d'", id );
425    return TR_ERROR;
426}
427
428static inline int parseBufHeader( tr_peer_t * peer )
429{
430    uint8_t * p   = peer->buf;
431
432    if( 4 > peer->pos )
433    {
434        return TR_OK;
435    }
436
437    if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
438    {
439        /* Don't wait until we get 68 bytes, this is wrong
440           already */
441        peer_dbg( "GET  handshake, invalid" );
442        tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
443        return TR_ERROR;
444    }
445    if( peer->pos < 68 )
446    {
447        return TR_OK;
448    }
449    if( memcmp( &p[4], "Torrent protocol", 16 ) )
450    {
451        peer_dbg( "GET  handshake, invalid" );
452        return TR_ERROR;
453    }
454
455    return TR_OK;
456}
457
458static uint8_t * parseBufHash( tr_peer_t * peer )
459{
460    if( 48 > peer->pos )
461    {
462        return NULL;
463    }
464    else
465    {
466        return peer->buf + 28;
467    }
468}
469
470static inline int parseBuf( tr_torrent_t * tor, tr_peer_t * peer )
471{
472    tr_info_t * inf = &tor->info;
473
474    int       i;
475    int       len;
476    uint8_t * p   = peer->buf;
477    uint8_t * end = &p[peer->pos];
478    int       ret;
479
480    if( peer->banned )
481    {
482        /* Don't even parse, we only stay connected */
483        peer->pos = 0;
484        return TR_OK;
485    }
486
487    while( peer->pos >= 4 )
488    {
489        if( peer->status & PEER_STATUS_HANDSHAKE )
490        {
491            char * client;
492
493            if( ( ret = parseBufHeader( peer ) ) )
494            {
495                return ret;
496            }
497
498            if( peer->pos < 68 )
499            {
500                break;
501            }
502
503            if( memcmp( &p[28], inf->hash, 20 ) )
504            {
505                peer_dbg( "GET  handshake, wrong torrent hash" );
506                return TR_ERROR;
507            }
508
509            if( !memcmp( &p[48], tor->id, 20 ) )
510            {
511                /* We are connected to ourselves... */
512                peer_dbg( "GET  handshake, that is us" );
513                return TR_ERROR;
514            }
515
516            peer->status  = PEER_STATUS_CONNECTED;
517            memcpy( peer->id, &p[48], 20 );
518            p            += 68;
519            peer->pos    -= 68;
520
521            for( i = 0; i < tor->peerCount; i++ )
522            {
523                if( tor->peers[i] == peer )
524                {
525                    continue;
526                }
527                if( !peerCmp( peer, tor->peers[i] ) )
528                {
529                    peer_dbg( "GET  handshake, duplicate" );
530                    return TR_ERROR;
531                }
532            }
533
534            client = tr_clientForId( (uint8_t *) peer->id );
535            peer_dbg( "GET  handshake, ok (%s)", client );
536            free( client );
537
538            sendBitfield( tor, peer );
539
540            continue;
541        }
542       
543        /* Get payload size */
544        TR_NTOHL( p, len );
545        p += 4;
546
547        if( len > 9 + tor->blockSize )
548        {
549            /* This should never happen. Drop that peer */
550            peer_dbg( "message too large (%d bytes)", len );
551            return TR_ERROR;
552        }
553
554        if( !len )
555        {
556            /* keep-alive */
557            peer_dbg( "GET  keep-alive" );
558            peer->pos -= 4;
559            continue;
560        }
561
562        if( &p[len] > end )
563        {
564            /* We do not have the entire message */
565            p -= 4;
566            break;
567        }
568
569        /* Remaining data after this message */
570        peer->pos -= 4 + len;
571
572        if( ( ret = parseMessage( tor, peer, p, len ) ) )
573        {
574            return ret;
575        }
576
577        p += len;
578    }
579
580    memmove( peer->buf, p, peer->pos );
581
582    return TR_OK;
583}
Note: See TracBrowser for help on using the repository browser.