source: trunk/libtransmission/peer.c @ 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: 15.3 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#include "transmission.h"
24
25#define MAX_REQUEST_COUNT 32
26#define OUR_REQUEST_COUNT 8  /* TODO: we should detect if we are on a
27                                high-speed network and adapt */
28
29typedef struct tr_request_s
30{
31    int index;
32    int begin;
33    int length;
34
35} tr_request_t;
36
37struct tr_peer_s
38{
39    struct in_addr addr;
40    in_port_t      port;
41
42#define PEER_STATUS_IDLE       1 /* Need to connect */
43#define PEER_STATUS_CONNECTING 2 /* Trying to send handshake */
44#define PEER_STATUS_HANDSHAKE  4 /* Waiting for peer's handshake */
45#define PEER_STATUS_CONNECTED  8 /* Got peer's handshake */
46    int            status;
47    int            socket;
48    uint64_t       date;
49    uint64_t       keepAlive;
50
51    char           amChoking;
52    char           amInterested;
53    char           peerChoking;
54    char           peerInterested;
55
56    int            optimistic;
57    uint64_t       lastChoke;
58
59    uint8_t        id[20];
60
61    /* The pieces that the peer has */
62    uint8_t      * bitfield;
63
64    int            goodPcs;
65    int            badPcs;
66    int            banned;
67    /* The pieces that the peer is contributing to */
68    uint8_t      * blamefield;
69    /* The bad pieces that the peer has contributed to */
70    uint8_t      * banfield;
71
72    uint8_t      * buf;
73    int            size;
74    int            pos;
75
76    uint8_t      * outMessages;
77    int            outMessagesSize;
78    int            outMessagesPos;
79    uint8_t        outBlock[13+16384];
80    int            outBlockSize;
81    int            outBlockLoaded;
82    int            outBlockSending;
83
84    int            inRequestCount;
85    tr_request_t   inRequests[OUR_REQUEST_COUNT];
86    int            inIndex;
87    int            inBegin;
88    int            inLength;
89    uint64_t       inTotal;
90
91    int            outRequestCount;
92    tr_request_t   outRequests[MAX_REQUEST_COUNT];
93    uint64_t       outTotal;
94    uint64_t       outDate;
95    int            outSlow;
96
97    tr_ratecontrol_t * download;
98};
99
100#define peer_dbg( a... ) __peer_dbg( peer, ## a )
101static void __peer_dbg( tr_peer_t * peer, char * msg, ... )
102{
103    char    string[256];
104    va_list args;
105
106    va_start( args, msg );
107    sprintf( string, "%08x:%04x ",
108             (uint32_t) peer->addr.s_addr, peer->port );
109    vsnprintf( &string[14], sizeof( string ) - 14, msg, args );
110    va_end( args ); 
111
112    tr_dbg( "%s", string );
113}
114
115#include "peermessages.h"
116#include "peerutils.h"
117#include "peerparse.h"
118
119/***********************************************************************
120 * tr_peerAddOld
121 ***********************************************************************
122 * Tries to add a peer given its IP and port (received from a tracker
123 * which doesn't support the "compact" extension).
124 **********************************************************************/
125void tr_peerAddOld( tr_torrent_t * tor, char * ip, int port )
126{
127    struct in_addr addr;
128
129    if( tr_netResolve( ip, &addr ) )
130    {
131        return;
132    }
133
134    addWithAddr( tor, addr, htons( port ) );
135}
136
137/***********************************************************************
138 * tr_peerAddCompact
139 ***********************************************************************
140 * Tries to add a peer, using 'addr' and 'port' to connect to the peer.
141 **********************************************************************/
142void tr_peerAddCompact( tr_torrent_t * tor, struct in_addr addr,
143                        in_port_t port )
144{
145    addWithAddr( tor, addr, port );
146}
147
148/***********************************************************************
149 * tr_peerInit
150 ***********************************************************************
151 * Initializes a new peer.
152 **********************************************************************/
153tr_peer_t * tr_peerInit( struct in_addr addr, in_port_t port, int s )
154{
155    tr_peer_t * peer = peerInit();
156
157    peer->socket = s;
158    peer->addr   = addr;
159    peer->port   = port;
160    peer->status = PEER_STATUS_CONNECTING;
161
162    return peer;
163}
164
165void tr_peerAttach( tr_torrent_t * tor, tr_peer_t * peer )
166{
167    peerAttach( tor, peer );
168}
169
170void tr_peerDestroy( tr_fd_t * fdlimit, tr_peer_t * peer )
171{
172    if( peer->bitfield )
173    {
174        free( peer->bitfield );
175    }
176    if( peer->blamefield )
177    {
178        free( peer->blamefield );
179    }
180    if( peer->banfield )
181    {
182        free( peer->banfield );
183    }
184    if( peer->buf )
185    {
186        free( peer->buf );
187    }
188    if( peer->outMessages )
189    {
190        free( peer->outMessages );
191    }
192    if( peer->status > PEER_STATUS_IDLE )
193    {
194        tr_netClose( peer->socket );
195        tr_fdSocketClosed( fdlimit, 0 );
196    }
197    tr_rcClose( peer->download );
198    free( peer );
199}
200
201/***********************************************************************
202 * tr_peerRem
203 ***********************************************************************
204 * Frees and closes everything related to the peer at index 'i', and
205 * removes it from the peers list.
206 **********************************************************************/
207void tr_peerRem( tr_torrent_t * tor, int i )
208{
209    tr_peer_t * peer = tor->peers[i];
210    int j;
211
212    for( j = 0; j < peer->inRequestCount; j++ )
213    {
214        tr_request_t * r;
215        int            block;
216
217        r     = &peer->inRequests[j];
218        block = tr_block( r->index,r->begin );
219        tr_cpDownloaderRem( tor->completion, block );
220    }
221    tr_peerDestroy( tor->fdlimit, peer );
222    tor->peerCount--;
223    memmove( &tor->peers[i], &tor->peers[i+1],
224             ( tor->peerCount - i ) * sizeof( tr_peer_t * ) );
225}
226
227/***********************************************************************
228 * tr_peerRead
229 ***********************************************************************
230 *
231 **********************************************************************/
232int tr_peerRead( tr_torrent_t * tor, tr_peer_t * peer )
233{
234    int ret;
235
236    /* Try to read */
237    for( ;; )
238    {
239        if( tor && !tr_rcCanTransfer( tor->globalDownload ) )
240        {
241            break;
242        }
243
244        if( peer->size < 1 )
245        {
246            peer->size = 1024;
247            peer->buf  = malloc( peer->size );
248        }
249        else if( peer->pos >= peer->size )
250        {
251            peer->size *= 2;
252            peer->buf   = realloc( peer->buf, peer->size );
253        }
254        /* Never read more than 1K each time, otherwise the rate
255           control is no use */
256        ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
257                          MIN( 1024, peer->size - peer->pos ) );
258        if( ret & TR_NET_CLOSE )
259        {
260            peer_dbg( "connection closed" );
261            return 1;
262        }
263        else if( ret & TR_NET_BLOCK )
264        {
265            break;
266        }
267        peer->date  = tr_date();
268        peer->pos  += ret;
269        if( NULL != tor )
270        {
271            tr_rcTransferred( peer->download, ret );
272            tr_rcTransferred( tor->download, ret );
273            tr_rcTransferred( tor->globalDownload, ret );
274            if( parseBuf( tor, peer ) )
275            {
276                return 1;
277            }
278        }
279        else
280        {
281            if( parseBufHeader( peer ) )
282            {
283                return 1;
284            }
285        }
286    }
287
288    return 0;
289}
290
291uint64_t tr_peerDate( tr_peer_t * peer )
292{
293    return peer->date;
294}
295
296/***********************************************************************
297 * tr_peerHash
298 ***********************************************************************
299 *
300 **********************************************************************/
301uint8_t * tr_peerHash( tr_peer_t * peer )
302{
303    return parseBufHash( peer );
304}
305
306/***********************************************************************
307 * tr_peerPulse
308 ***********************************************************************
309 *
310 **********************************************************************/
311void tr_peerPulse( tr_torrent_t * tor )
312{
313    int i, ret, size;
314    uint8_t * p;
315    tr_peer_t * peer;
316
317    if( tr_date() > tor->date + 1000 )
318    {
319        tor->date = tr_date();
320
321        for( i = 0; i < tor->peerCount; )
322        {
323            if( checkPeer( tor, i ) )
324            {
325                tr_peerRem( tor, i );
326                continue;
327            }
328            i++;
329        }
330    }
331
332    if( tor->status & TR_STATUS_STOPPING )
333    {
334        return;
335    }
336   
337    /* Shuffle peers */
338    if( tor->peerCount > 1 )
339    {
340        peer = tor->peers[0];
341        memmove( &tor->peers[0], &tor->peers[1],
342                 ( tor->peerCount - 1 ) * sizeof( void * ) );
343        tor->peers[tor->peerCount - 1] = peer;
344    }
345
346    /* Handle peers */
347    for( i = 0; i < tor->peerCount; )
348    {
349        peer = tor->peers[i];
350
351        if( peer->status < PEER_STATUS_HANDSHAKE )
352        {
353            i++;
354            continue;
355        }
356
357        if( tr_peerRead( tor, tor->peers[i] ) )
358        {
359            goto dropPeer;
360        }
361
362        if( peer->status < PEER_STATUS_CONNECTED )
363        {
364            i++;
365            continue;
366        }
367
368        /* Try to write */
369writeBegin:
370
371        /* Send all smaller messages regardless of the upload cap */
372        while( ( p = messagesPending( peer, &size ) ) )
373        {
374            ret = tr_netSend( peer->socket, p, size );
375            if( ret & TR_NET_CLOSE )
376            {
377                goto dropPeer;
378            }
379            else if( ret & TR_NET_BLOCK )
380            {
381                goto writeEnd;
382            }
383            messagesSent( peer, ret );
384        }
385
386        /* Send pieces if we can */
387        while( ( p = blockPending( tor, peer, &size ) ) )
388        {
389            if( !tr_rcCanTransfer( tor->globalUpload ) )
390            {
391                break;
392            }
393
394            ret = tr_netSend( peer->socket, p, size );
395            if( ret & TR_NET_CLOSE )
396            {
397                goto dropPeer;
398            }
399            else if( ret & TR_NET_BLOCK )
400            {
401                break;
402            }
403
404            blockSent( peer, ret );
405            tr_rcTransferred( tor->upload, ret );
406            tr_rcTransferred( tor->globalUpload, ret );
407
408            tor->uploaded  += ret;
409            peer->outTotal += ret;
410            peer->outDate   = tr_date();
411
412            /* In case this block is done, you may have messages
413               pending. Send them before we start the next block */
414            goto writeBegin;
415        }
416writeEnd:
417
418        /* Ask for a block whenever possible */
419        if( !tr_cpIsSeeding( tor->completion ) &&
420            !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
421        {
422            /* This peer is no use to us, and it seems there are
423               more */
424            peer_dbg( "not interesting" );
425            tr_peerRem( tor, i );
426            continue;
427        }
428
429        if( peer->amInterested && !peer->peerChoking && !peer->banned )
430        {
431            int block;
432            while( peer->inRequestCount < OUR_REQUEST_COUNT )
433            {
434                block = chooseBlock( tor, peer );
435                if( block < 0 )
436                {
437                    break;
438                }
439                sendRequest( tor, peer, block );
440            }
441        }
442       
443        i++;
444        continue;
445
446dropPeer:
447        tr_peerRem( tor, i );
448    }
449}
450
451/***********************************************************************
452 * tr_peerIsConnected
453 ***********************************************************************
454 *
455 **********************************************************************/
456int tr_peerIsConnected( tr_peer_t * peer )
457{
458    return peer->status & PEER_STATUS_CONNECTED;
459}
460
461/***********************************************************************
462 * tr_peerIsUploading
463 ***********************************************************************
464 *
465 **********************************************************************/
466int tr_peerIsUploading( tr_peer_t * peer )
467{
468    return ( peer->inRequestCount > 0 );
469}
470
471/***********************************************************************
472 * tr_peerIsDownloading
473 ***********************************************************************
474 *
475 **********************************************************************/
476int tr_peerIsDownloading( tr_peer_t * peer )
477{
478    return peer->outBlockSending;
479}
480
481/***********************************************************************
482 * tr_peerBitfield
483 ***********************************************************************
484 *
485 **********************************************************************/
486uint8_t * tr_peerBitfield( tr_peer_t * peer )
487{
488    return peer->bitfield;
489}
490
491float tr_peerDownloadRate( tr_peer_t * peer )
492{
493    return tr_rcRate( peer->download );
494}
495
496int tr_peerIsUnchoked( tr_peer_t * peer )
497{
498    return !peer->amChoking;
499}
500
501int tr_peerIsInterested  ( tr_peer_t * peer )
502{
503    return peer->peerInterested;
504}
505
506void tr_peerChoke( tr_peer_t * peer )
507{
508    sendChoke( peer, 1 );
509    peer->lastChoke = tr_date();
510}
511
512void tr_peerUnchoke( tr_peer_t * peer )
513{
514    sendChoke( peer, 0 );
515    peer->lastChoke = tr_date();
516}
517
518uint64_t tr_peerLastChoke( tr_peer_t * peer )
519{
520    return peer->lastChoke;
521}
522
523void tr_peerSetOptimistic( tr_peer_t * peer, int o )
524{
525    peer->optimistic = o;
526}
527
528int tr_peerIsOptimistic( tr_peer_t * peer )
529{
530    return peer->optimistic;
531}
532
533static inline int peerIsBad( tr_peer_t * peer )
534{
535    if( peer->goodPcs >= 5 &&
536        peer->badPcs >= ( peer->goodPcs * 3 ) )
537    {
538        /* need poor success rate if we've successfully downloaded before */
539        return 1;
540    }
541    else if( peer->goodPcs < 5 &&
542             peer->badPcs >= ( 10 + peer->goodPcs ) )
543    {
544        /* need 10 more bad pieces than good before we discard peer */
545        return 1;
546    }
547    return 0;
548}
549
550void tr_peerBlame( tr_torrent_t * tor, tr_peer_t * peer,
551                   int piece, int success )
552{
553    if( !peer->blamefield || !tr_bitfieldHas( peer->blamefield, piece ) )
554    {
555        return;
556    }
557
558    if( success )
559    {
560        peer->goodPcs++;
561    }
562    else
563    {
564        peer->badPcs++;
565
566        if( !peer->banfield )
567        {
568            peer->banfield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
569        }
570        tr_bitfieldAdd( peer->banfield, piece );
571
572        if( peerIsBad( peer ) )
573        {
574            /* Full ban */
575            peer_dbg( "banned (%d / %d)", peer->goodPcs, peer->badPcs );
576            peer->banned = 1;
577            peer->peerInterested = 0;
578        }
579    }
580    tr_bitfieldRem( peer->blamefield, piece );
581}
Note: See TracBrowser for help on using the repository browser.