source: trunk/libtransmission/peerparse.h @ 253

Last change on this file since 253 was 253, checked in by titer, 16 years ago

Adds automatic peer banning.
At first, peers get only banned for the bad pieces they've contributed to, i.e. we continue to ask them for other parts of the torrent. If more bad data keeps coming, the peer gets completely banned.
Based on Jeremiah Morris' patch.

File size: 14.0 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005 Eric Petit
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23/***********************************************************************
24 * This file handles all incoming messages
25 **********************************************************************/
26
27/***********************************************************************
28 * parseChoke
29 ***********************************************************************
30 *
31 **********************************************************************/
32static inline int parseChoke( tr_torrent_t * tor, tr_peer_t * peer,
33                              int len, int choking )
34{
35    tr_request_t * r;
36    int i;
37
38    if( len != 1 )
39    {
40        peer_dbg( "GET  %schoke, invalid", choking ? "" : "un" );
41        return 1;
42    }
43
44    peer_dbg( "GET  %schoke", choking ? "" : "un" );
45
46    peer->peerChoking = choking;
47
48    if( choking )
49    {
50        /* Discard all pending requests */
51        for( i = 0; i < peer->inRequestCount; i++ )
52        {
53            r = &peer->inRequests[i];
54            tr_cpDownloaderRem( tor->completion, tr_block(r->index,r->begin) );
55        }
56        peer->inRequestCount = 0;
57    }
58
59    return 0;
60}
61
62/***********************************************************************
63 * parseInterested
64 ***********************************************************************
65 *
66 **********************************************************************/
67static inline int parseInterested( tr_peer_t * peer, int len,
68                                   int interested )
69{
70    if( len != 1 )
71    {
72        peer_dbg( "GET  %sinterested, invalid", interested ? "" : "un" );
73        return 1;
74    }
75
76    peer_dbg( "GET  %sinterested", interested ? "" : "un" );
77
78    peer->peerInterested = interested;
79
80    return 0;
81}
82
83/***********************************************************************
84 * parseHave
85 ***********************************************************************
86 *
87 **********************************************************************/
88static inline int parseHave( tr_torrent_t * tor, tr_peer_t * peer,
89                             uint8_t * p, int len )
90{
91    uint32_t piece;
92
93    if( len != 5 )
94    {
95        peer_dbg( "GET  have, invalid" );
96        return 1;
97    }
98
99    TR_NTOHL( p, piece );
100
101    peer_dbg( "GET  have %d", piece );
102
103    if( !peer->bitfield )
104    {
105        peer->bitfield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
106    }
107    tr_bitfieldAdd( peer->bitfield, piece );
108    updateInterest( tor, peer );
109
110    return 0;
111}
112
113static inline int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer,
114                                 uint8_t * p, int len )
115{
116    tr_info_t * inf = &tor->info;
117    int bitfieldSize;
118
119    bitfieldSize = ( inf->pieceCount + 7 ) / 8;
120   
121    if( len != 1 + bitfieldSize )
122    {
123        peer_dbg( "GET  bitfield, wrong size" );
124        return 1;
125    }
126
127    /* Make sure the spare bits are unset */
128    if( ( inf->pieceCount & 0x7 ) )
129    {
130        uint8_t lastByte;
131       
132        lastByte   = p[bitfieldSize-1];
133        lastByte <<= inf->pieceCount & 0x7;
134        lastByte  &= 0xFF;
135
136        if( lastByte )
137        {
138            peer_dbg( "GET  bitfield, spare bits set" );
139            return 1;
140        }
141    }
142
143    peer_dbg( "GET  bitfield, ok" );
144
145    if( !peer->bitfield )
146    {
147        peer->bitfield = malloc( bitfieldSize );
148    }
149    memcpy( peer->bitfield, p, bitfieldSize );
150    updateInterest( tor, peer );
151
152    return 0;
153}
154
155static inline int parseRequest( tr_peer_t * peer, uint8_t * p, int len )
156{
157    int index, begin, length;
158    tr_request_t * r;
159
160    if( len != 13 )
161    {
162        peer_dbg( "GET  request, invalid" );
163        return 1;
164    }
165
166    if( peer->amChoking )
167    {
168        /* Didn't he get it? */
169        sendChoke( peer, 1 );
170        return 0;
171    }
172   
173    TR_NTOHL( p,     index );
174    TR_NTOHL( &p[4], begin );
175    TR_NTOHL( &p[8], length );
176
177    peer_dbg( "GET  request %d/%d (%d bytes)",
178              index, begin, length );
179
180    /* TODO sanity checks (do we have the piece, etc) */
181
182    if( length > 16384 )
183    {
184        /* Sorry mate */
185        return 1;
186    }
187
188    if( peer->outRequestCount >= MAX_REQUEST_COUNT )
189    {
190        tr_err( "Too many requests" );
191        return 1;
192    }
193
194    r         = &peer->outRequests[peer->outRequestCount];
195    r->index  = index;
196    r->begin  = begin;
197    r->length = length;
198
199    (peer->outRequestCount)++;
200
201    return 0;
202}
203
204static inline int parsePiece( tr_torrent_t * tor, tr_peer_t * peer,
205                              uint8_t * p, int len )
206{
207    int index, begin, block, i, j;
208    tr_request_t * r;
209
210    TR_NTOHL( p,     index );
211    TR_NTOHL( &p[4], begin );
212
213    peer_dbg( "GET  piece %d/%d (%d bytes)",
214              index, begin, len - 9 );
215
216    if( peer->inRequestCount < 1 )
217    {
218        /* Our "cancel" was probably late */
219        peer_dbg( "not expecting a block" );
220        return 0;
221    }
222   
223    r = &peer->inRequests[0];
224    if( index != r->index || begin != r->begin )
225    {
226        int suckyClient;
227
228        /* Either our "cancel" was late, or this is a sucky
229           client that cannot deal with multiple requests */
230        suckyClient = 0;
231        for( i = 0; i < peer->inRequestCount; i++ )
232        {
233            r = &peer->inRequests[i];
234
235            if( index != r->index || begin != r->begin )
236            {
237                continue;
238            }
239
240            /* Sucky client, he dropped the previous requests */
241            peer_dbg( "block was expected later" );
242            for( j = 0; j < i; j++ )
243            {
244                r = &peer->inRequests[j];
245                tr_cpDownloaderRem( tor->completion,
246                                    tr_block(r->index,r->begin) );
247            }
248            suckyClient = 1;
249            peer->inRequestCount -= i;
250            memmove( &peer->inRequests[0], &peer->inRequests[i],
251                     peer->inRequestCount * sizeof( tr_request_t ) );
252            r = &peer->inRequests[0];
253            break;
254        }
255
256        if( !suckyClient )
257        {
258            r = &peer->inRequests[0];
259            peer_dbg( "wrong block (expecting %d/%d)",
260                      r->index, r->begin );
261            return 0;
262        }
263    }
264
265    if( len - 9 != r->length )
266    {
267        peer_dbg( "wrong size (expecting %d)", r->length );
268        return 1;
269    }
270
271    tor->downloaded += r->length;
272
273    block = tr_block( r->index, r->begin );
274    if( tr_cpBlockIsComplete( tor->completion, block ) )
275    {
276        peer_dbg( "have this block already" );
277        (peer->inRequestCount)--;
278        memmove( &peer->inRequests[0], &peer->inRequests[1],
279                 peer->inRequestCount * sizeof( tr_request_t ) );
280        return 0;
281    }
282
283    /* set blame/credit for this piece */
284    if( !peer->blamefield )
285    {
286        peer->blamefield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
287    }
288    tr_bitfieldAdd( peer->blamefield, index );
289
290    tr_cpBlockAdd( tor->completion, block );
291    tr_ioWrite( tor->io, index, begin, len - 9, &p[8] );
292    tr_cpDownloaderRem( tor->completion, block );
293
294    sendCancel( tor, block );
295
296    if( tr_cpPieceIsComplete( tor->completion, index ) )
297    {
298        tr_peer_t * otherPeer;
299
300        for( i = 0; i < tor->peerCount; i++ )
301        {
302            otherPeer = tor->peers[i];
303
304            if( otherPeer->status < PEER_STATUS_CONNECTED )
305            {
306                continue;
307            }
308
309            sendHave( otherPeer, index );
310            updateInterest( tor, otherPeer );
311        }
312    }
313
314    (peer->inRequestCount)--;
315    memmove( &peer->inRequests[0], &peer->inRequests[1],
316             peer->inRequestCount * sizeof( tr_request_t ) );
317
318    return 0;
319}
320
321static inline int parseCancel( tr_peer_t * peer, uint8_t * p, int len )
322{
323    int index, begin, length;
324    int i;
325    tr_request_t * r;
326
327    if( len != 13 )
328    {
329        peer_dbg( "GET  cancel, invalid" );
330        return 1;
331    }
332
333    TR_NTOHL( p,     index );
334    TR_NTOHL( &p[4], begin );
335    TR_NTOHL( &p[8], length );
336
337    peer_dbg( "GET  cancel %d/%d (%d bytes)",
338              index, begin, length );
339
340    for( i = 0; i < peer->outRequestCount; i++ )
341    {
342        r = &peer->outRequests[i];
343        if( r->index == index && r->begin == begin &&
344            r->length == length )
345        {
346            (peer->outRequestCount)--;
347            memmove( &r[0], &r[1], sizeof( tr_request_t ) *
348                    ( peer->outRequestCount - i ) );
349            break;
350        }
351    }
352
353    return 0;
354}
355
356static inline int parsePort( tr_peer_t * peer, uint8_t * p, int len )
357{
358    in_port_t port;
359
360    if( len != 3 )
361    {
362        peer_dbg( "GET  port, invalid" );
363        return 1;
364    }
365
366    port = *( (in_port_t *) p );
367    peer_dbg( "GET  port %d", ntohs( port ) );
368
369    return 0;
370}
371
372static inline int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
373                                uint8_t * p, int len )
374{
375    char id;
376
377    /* Type of the message */
378    id = *(p++);
379
380    switch( id )
381    {
382        case 0:
383            return parseChoke( tor, peer, len, 1 );
384        case 1:
385            return parseChoke( tor, peer, len, 0 );
386        case 2:
387            return parseInterested( peer, len, 1 );
388        case 3:
389            return parseInterested( peer, len, 0 );
390        case 4:
391            return parseHave( tor, peer, p, len );
392        case 5:
393            return parseBitfield( tor, peer, p, len );
394        case 6:
395            return parseRequest( peer, p, len );
396        case 7:
397            return parsePiece( tor, peer, p, len );
398        case 8:
399            return parseCancel( peer, p, len );
400        case 9:
401            return parsePort( peer, p, len );
402    }
403
404    peer_dbg( "Unknown message '%d'", id );
405    return 1;
406}
407
408static inline int parseBufHeader( tr_peer_t * peer )
409{
410    uint8_t * p   = peer->buf;
411
412    if( 4 > peer->pos )
413    {
414        return 0;
415    }
416
417    if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
418    {
419        /* Don't wait until we get 68 bytes, this is wrong
420           already */
421        peer_dbg( "GET  handshake, invalid" );
422        tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
423        return 1;
424    }
425    if( peer->pos < 68 )
426    {
427        return 0;
428    }
429    if( memcmp( &p[4], "Torrent protocol", 16 ) )
430    {
431        peer_dbg( "GET  handshake, invalid" );
432        return 1;
433    }
434
435    return 0;
436}
437
438static uint8_t * parseBufHash( tr_peer_t * peer )
439{
440    if( 48 > peer->pos )
441    {
442        return NULL;
443    }
444    else
445    {
446        return peer->buf + 28;
447    }
448}
449
450static inline int parseBuf( tr_torrent_t * tor, tr_peer_t * peer )
451{
452    tr_info_t * inf = &tor->info;
453
454    int       i;
455    int       len;
456    uint8_t * p   = peer->buf;
457    uint8_t * end = &p[peer->pos];
458
459    if( peer->banned )
460    {
461        /* Don't even parse, we only stay connected */
462        peer->pos = 0;
463        return 0;
464    }
465
466    while( peer->pos >= 4 )
467    {
468        if( peer->status & PEER_STATUS_HANDSHAKE )
469        {
470            char * client;
471
472            if( parseBufHeader( peer ) )
473            {
474                return 1;
475            }
476
477            if( peer->pos < 68 )
478            {
479                break;
480            }
481
482            if( memcmp( &p[28], inf->hash, 20 ) )
483            {
484                peer_dbg( "GET  handshake, wrong torrent hash" );
485                return 1;
486            }
487
488            if( !memcmp( &p[48], tor->id, 20 ) )
489            {
490                /* We are connected to ourselves... */
491                peer_dbg( "GET  handshake, that is us" );
492                return 1;
493            }
494
495            peer->status  = PEER_STATUS_CONNECTED;
496            memcpy( peer->id, &p[48], 20 );
497            p            += 68;
498            peer->pos    -= 68;
499
500            for( i = 0; i < tor->peerCount; i++ )
501            {
502                if( tor->peers[i] == peer )
503                {
504                    continue;
505                }
506                if( !peerCmp( peer, tor->peers[i] ) )
507                {
508                    peer_dbg( "GET  handshake, duplicate" );
509                    return 1;
510                }
511            }
512
513            client = tr_clientForId( (uint8_t *) peer->id );
514            peer_dbg( "GET  handshake, ok (%s)", client );
515            free( client );
516
517            sendBitfield( tor, peer );
518
519            continue;
520        }
521       
522        /* Get payload size */
523        TR_NTOHL( p, len );
524        p += 4;
525
526        if( len > 9 + tor->blockSize )
527        {
528            /* This should never happen. Drop that peer */
529            peer_dbg( "message too large (%d bytes)", len );
530            return 1;
531        }
532
533        if( !len )
534        {
535            /* keep-alive */
536            peer_dbg( "GET  keep-alive" );
537            peer->pos -= 4;
538            continue;
539        }
540
541        if( &p[len] > end )
542        {
543            /* We do not have the entire message */
544            p -= 4;
545            break;
546        }
547
548        /* Remaining data after this message */
549        peer->pos -= 4 + len;
550
551        if( parseMessage( tor, peer, p, len ) )
552        {
553            return 1;
554        }
555
556        p += len;
557    }
558
559    memmove( peer->buf, p, peer->pos );
560
561    return 0;
562}
Note: See TracBrowser for help on using the repository browser.