source: trunk/libtransmission/peer.c @ 1064

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

Adds progress in tr_peer_stat_t

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