source: trunk/libtransmission/peerparse.h @ 931

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

Fix possible uint64_t underflow which could cause insanely huge (16,000,000 TB)

upload and/or download totals to be reported to the tracker.

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