source: trunk/libtransmission/peer.c @ 920

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

Merge nat-traversal branch to trunk.

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