source: trunk/libtransmission/peer.c @ 926

Last change on this file since 926 was 926, checked in by titer, 15 years ago

Remember whether a peer comes from an incoming or outcoming connection

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