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