source: trunk/libtransmission/peerparse.h @ 346

Last change on this file since 346 was 261, checked in by titer, 15 years ago

Updated svn:keywords

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