source: trunk/libtransmission/peer.c @ 1594

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

Don't save or load cached peers for private torrents.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.6 KB
Line 
1/******************************************************************************
2 * $Id: peer.c 1594 2007-03-26 19:19:33Z joshe $
3 *
4 * Copyright (c) 2005-2007 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#include "peertree.h"
27
28#define MAX_REQUEST_COUNT       32
29#define OUR_REQUEST_COUNT       8  /* TODO: we should detect if we are on a
30                                      high-speed network and adapt */
31#define PEX_PEER_CUTOFF         50 /* only try to add new peers from pex if
32                                      we have fewer existing peers than this */
33#define PEX_INTERVAL            60 /* don't send pex messages more frequently
34                                      than PEX_INTERVAL +
35                                      rand( PEX_INTERVAL / 10 ) seconds */
36#define PEER_SUPPORTS_EXTENDED_MESSAGES( bits ) ( (bits)[5] & 0x10 )
37#define PEER_SUPPORTS_AZUREUS_PROTOCOL( bits )  ( (bits)[0] & 0x80 )
38
39#define PEER_MSG_CHOKE          0
40#define PEER_MSG_UNCHOKE        1
41#define PEER_MSG_INTERESTED     2
42#define PEER_MSG_UNINTERESTED   3
43#define PEER_MSG_HAVE           4
44#define PEER_MSG_BITFIELD       5
45#define PEER_MSG_REQUEST        6
46#define PEER_MSG_PIECE          7
47#define PEER_MSG_CANCEL         8
48#define PEER_MSG_PORT           9
49#define PEER_MSG_EXTENDED       20
50
51typedef struct tr_request_s
52{
53    int index;
54    int begin;
55    int length;
56
57} tr_request_t;
58
59struct tr_peer_s
60{
61    tr_torrent_t      * tor;
62
63    struct in_addr      addr;
64    in_port_t           port;  /* peer's listening port, 0 if not known */
65
66#define PEER_STATUS_IDLE        1 /* Need to connect */
67#define PEER_STATUS_CONNECTING  2 /* Trying to send handshake */
68#define PEER_STATUS_HANDSHAKE   3 /* Waiting for peer's handshake */
69#define PEER_STATUS_AZ_GIVER    4 /* Sending Azureus handshake */
70#define PEER_STATUS_AZ_RECEIVER 5 /* Receiving Azureus handshake */
71#define PEER_STATUS_CONNECTED   6 /* Got peer's handshake */
72    int                 status;
73    int                 socket;
74    char                from;
75    char                private;
76    char                azproto;  /* azureus peer protocol is being used */
77    uint64_t            date;
78    uint64_t            keepAlive;
79
80#define EXTENDED_NOT_SUPPORTED   0 /* extended messages not supported */
81#define EXTENDED_SUPPORTED       1 /* waiting to send extended handshake */
82#define EXTENDED_HANDSHAKE       2 /* sent extended handshake */
83    uint8_t             extStatus;
84    uint8_t             pexStatus;   /* peer's ut_pex id, 0 if not supported */
85    uint64_t            lastPex;     /* time when last pex packet was sent */
86    int                 advertisedPort; /* listening port we last told peer */
87    tr_peertree_t       sentPeers;
88
89    char                amChoking;
90    char                amInterested;
91    char                peerChoking;
92    char                peerInterested;
93
94    int                 optimistic;
95    uint64_t            lastChoke;
96
97    uint8_t             id[20];
98
99    /* The pieces that the peer has */
100    tr_bitfield_t     * bitfield;
101    int                 pieceCount;
102    float               progress;
103
104    int                 goodPcs;
105    int                 badPcs;
106    int                 banned;
107    /* The pieces that the peer is contributing to */
108    tr_bitfield_t     * blamefield;
109    /* The bad pieces that the peer has contributed to */
110    tr_bitfield_t     * banfield;
111
112    uint8_t           * buf;
113    int                 size;
114    int                 pos;
115
116    uint8_t           * outMessages;
117    int                 outMessagesSize;
118    int                 outMessagesPos;
119    uint8_t             outBlock[13+16384];
120    int                 outBlockSize;
121    int                 outBlockLoaded;
122    int                 outBlockSending;
123
124    int                 inRequestCount;
125    tr_request_t        inRequests[OUR_REQUEST_COUNT];
126    int                 inIndex;
127    int                 inBegin;
128    int                 inLength;
129    uint64_t            inTotal;
130
131    int                 outRequestCount;
132    tr_request_t        outRequests[MAX_REQUEST_COUNT];
133    uint64_t            outTotal;
134    uint64_t            outDate;
135
136    tr_ratecontrol_t  * download;
137    tr_ratecontrol_t  * upload;
138};
139
140#define peer_dbg( a... ) __peer_dbg( peer, ## a )
141static void __peer_dbg( tr_peer_t * peer, char * msg, ... ) PRINTF( 2, 3 );
142
143static void __peer_dbg( tr_peer_t * peer, char * msg, ... )
144{
145    char    string[256];
146    va_list args;
147
148    va_start( args, msg );
149    snprintf( string, sizeof string, "%08x:%04x ",
150             (uint32_t) peer->addr.s_addr, peer->port );
151    vsnprintf( &string[14], sizeof( string ) - 14, msg, args );
152    va_end( args ); 
153
154    tr_dbg( "%s", string );
155}
156
157#include "peerext.h"
158#include "peeraz.h"
159#include "peermessages.h"
160#include "peerutils.h"
161#include "peerparse.h"
162
163/***********************************************************************
164 * tr_peerInit
165 ***********************************************************************
166 * Initializes a new peer.
167 **********************************************************************/
168tr_peer_t * tr_peerInit( struct in_addr addr, in_port_t port, int s, int from )
169{
170    tr_peer_t * peer = peerInit();
171
172    assert( 0 <= from && TR_PEER_FROM__MAX > from );
173
174    peer->socket = s;
175    peer->addr = addr;
176    peer->port = port;
177    peer->from = from;
178    if( s >= 0 )
179    {
180        assert( TR_PEER_FROM_INCOMING == from );
181        peer->status = PEER_STATUS_CONNECTING;
182    }
183    else
184    {
185        peer->status = PEER_STATUS_IDLE;
186    }
187
188    return peer;
189}
190
191void tr_peerDestroy( tr_peer_t * peer )
192{
193    tr_torrent_t * tor = peer->tor;
194    tr_request_t * r;
195    int i, block;
196
197    peertreeFree( &peer->sentPeers );
198    for( i = 0; i < peer->inRequestCount; i++ )
199    {
200        r = &peer->inRequests[i];
201        block = tr_block( r->index, r->begin );
202        tr_cpDownloaderRem( tor->completion, block );
203    }
204    tr_bitfieldFree( peer->bitfield );
205    tr_bitfieldFree( peer->blamefield );
206    tr_bitfieldFree( peer->banfield );
207    if( peer->buf )
208    {
209        free( peer->buf );
210    }
211    if( peer->outMessages )
212    {
213        free( peer->outMessages );
214    }
215    if( peer->status > PEER_STATUS_IDLE )
216    {
217        tr_netClose( peer->socket );
218    }
219    tr_rcClose( peer->download );
220    tr_rcClose( peer->upload );
221    free( peer );
222}
223
224void tr_peerSetPrivate( tr_peer_t * peer, int private )
225{
226    if( peer->private == private )
227    {
228        return;
229    }
230
231    peer->private = private;
232
233    if( !private )
234    {
235        peer->lastPex = 0;
236    }
237
238    if( EXTENDED_HANDSHAKE == peer->extStatus )
239    {
240        sendExtended( peer->tor, peer, EXTENDED_HANDSHAKE_ID );
241    }
242}
243
244void tr_peerSetTorrent( tr_peer_t * peer, tr_torrent_t * tor )
245{
246    peer->tor = tor;
247}
248
249/***********************************************************************
250 * tr_peerRead
251 ***********************************************************************
252 *
253 **********************************************************************/
254int tr_peerRead( tr_peer_t * peer )
255{
256    tr_torrent_t * tor = peer->tor;
257    int ret;
258
259    /* Try to read */
260    for( ;; )
261    {
262        if( tor )
263        {
264            if( tor->customDownloadLimit )
265            {
266                if( !tr_rcCanTransfer( tor->download ) )
267                {
268                    break;
269                }
270            }
271            else
272            {
273                tr_lockUnlock( &tor->lock );
274                if( !tr_rcCanGlobalTransfer( tor->handle, 0 ) )
275                {
276                    tr_lockLock( &tor->lock );
277                    break;
278                }
279                tr_lockLock( &tor->lock );
280            }
281        }
282
283        if( peer->size < 1 )
284        {
285            peer->size = 1024;
286            peer->buf  = malloc( peer->size );
287        }
288        else if( peer->pos >= peer->size )
289        {
290            peer->size *= 2;
291            peer->buf   = realloc( peer->buf, peer->size );
292        }
293        /* Never read more than 1K each time, otherwise the rate
294           control is no use */
295        ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
296                          MIN( 1024, peer->size - peer->pos ) );
297        if( ret & TR_NET_CLOSE )
298        {
299            peer_dbg( "connection closed" );
300            return TR_ERROR;
301        }
302        else if( ret & TR_NET_BLOCK )
303        {
304            break;
305        }
306        peer->date  = tr_date();
307        peer->pos  += ret;
308        if( NULL != tor )
309        {
310            tr_rcTransferred( peer->download, ret );
311            tr_rcTransferred( tor->download, ret );
312            if( ( ret = parseBuf( tor, peer ) ) )
313            {
314                return ret;
315            }
316        }
317        else
318        {
319            if( ( ret = parseBufHeader( peer ) ) )
320            {
321                return ret;
322            }
323        }
324    }
325
326    return TR_OK;
327}
328
329uint64_t tr_peerDate( tr_peer_t * peer )
330{
331    return peer->date;
332}
333
334/***********************************************************************
335 * tr_peerId
336 ***********************************************************************
337 *
338 **********************************************************************/
339uint8_t * tr_peerId( tr_peer_t * peer )
340{
341    return & peer->id[0];
342}
343
344/***********************************************************************
345 * tr_peerAddress
346 ***********************************************************************
347 *
348 **********************************************************************/
349struct in_addr * tr_peerAddress( tr_peer_t * peer )
350{
351    return &peer->addr;
352}
353
354/***********************************************************************
355 * tr_peerHash
356 ***********************************************************************
357 *
358 **********************************************************************/
359uint8_t * tr_peerHash( tr_peer_t * peer )
360{
361    return parseBufHash( peer );
362}
363
364/***********************************************************************
365 * tr_peerPulse
366 ***********************************************************************
367 *
368 **********************************************************************/
369int tr_peerPulse( tr_peer_t * peer )
370{
371    tr_torrent_t * tor = peer->tor;
372    int ret, size;
373    uint8_t * p;
374
375    if( ( ret = checkPeer( peer ) ) )
376    {
377        return ret;
378    }
379
380    /* Connect */
381    if( PEER_STATUS_IDLE == peer->status )
382    {
383        peer->socket = tr_netOpenTCP( peer->addr, peer->port, 0 );
384        if( peer->socket < 0 )
385        {
386            return TR_ERROR;
387        }
388        peer->status = PEER_STATUS_CONNECTING;
389    }
390
391    /* Try to send handshake */
392    if( PEER_STATUS_CONNECTING == peer->status )
393    {
394        uint8_t buf[68];
395        tr_info_t * inf = &tor->info;
396
397        buf[0] = 19;
398        memcpy( &buf[1], "BitTorrent protocol", 19 );
399        memset( &buf[20], 0, 8 );
400        buf[20] = 0x80;         /* azureus protocol */
401        buf[25] = 0x10;         /* extended messages */
402        memcpy( &buf[28], inf->hash, 20 );
403        memcpy( &buf[48], tor->id, 20 );
404
405        switch( tr_netSend( peer->socket, buf, 68 ) )
406        {
407            case 68:
408                peer_dbg( "SEND handshake" );
409                peer->status = PEER_STATUS_HANDSHAKE;
410                break;
411            case TR_NET_BLOCK:
412                break;
413            default:
414                peer_dbg( "connection closed" );
415                return TR_ERROR;
416        }
417    }
418    if( peer->status < PEER_STATUS_HANDSHAKE )
419    {
420        /* Nothing more we can do till we sent the handshake */
421        return TR_OK;
422    }
423
424    /* Read incoming messages */
425    if( ( ret = tr_peerRead( peer ) ) )
426    {
427        return ret;
428    }
429
430    /* Try to send Azureus handshake */
431    if( PEER_STATUS_AZ_GIVER == peer->status )
432    {
433        switch( sendAZHandshake( tor, peer ) )
434        {
435            case TR_NET_BLOCK:
436                break;
437            case TR_NET_CLOSE:
438                peer_dbg( "connection closed" );
439                return TR_ERROR;
440            default:
441                peer->status = PEER_STATUS_AZ_RECEIVER;
442                break;
443        }
444    }
445
446    if( peer->status < PEER_STATUS_CONNECTED )
447    {
448        /* Nothing more we can do till we got the other guy's handshake */
449        return TR_OK;
450    }
451
452    /* Try to write */
453writeBegin:
454
455    /* Send all smaller messages regardless of the upload cap */
456    while( ( p = messagesPending( peer, &size ) ) )
457    {
458        ret = tr_netSend( peer->socket, p, size );
459        if( ret & TR_NET_CLOSE )
460        {
461            return TR_ERROR;
462        }
463        else if( ret & TR_NET_BLOCK )
464        {
465            goto writeEnd;
466        }
467        messagesSent( peer, ret );
468    }
469
470    /* Send pieces if we can */
471    while( ( p = blockPending( tor, peer, &size ) ) )
472    {
473        if( tor->customUploadLimit )
474        {
475            if( !tr_rcCanTransfer( tor->upload ) )
476            {
477                break;
478            }
479        }
480        else
481        {
482            tr_lockUnlock( &tor->lock );
483            if( !tr_rcCanGlobalTransfer( tor->handle, 1 ) )
484            {
485                tr_lockLock( &tor->lock );
486                break;
487            }
488            tr_lockLock( &tor->lock );
489        }
490
491        ret = tr_netSend( peer->socket, p, size );
492        if( ret & TR_NET_CLOSE )
493        {
494            return TR_ERROR;
495        }
496        else if( ret & TR_NET_BLOCK )
497        {
498            break;
499        }
500
501        blockSent( peer, ret );
502        tr_rcTransferred( peer->upload, ret );
503        tr_rcTransferred( tor->upload, ret );
504
505        tor->uploadedCur += ret;
506        peer->outTotal   += ret;
507        peer->outDate     = tr_date();
508
509        /* In case this block is done, you may have messages
510           pending. Send them before we start the next block */
511        goto writeBegin;
512    }
513writeEnd:
514
515    /* Ask for a block whenever possible */
516    if( !tr_cpIsSeeding( tor->completion ) &&
517        !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
518    {
519        /* This peer is no use to us, and it seems there are
520           more */
521        peer_dbg( "not interesting" );
522        return TR_ERROR;
523    }
524
525    if( peer->amInterested && !peer->peerChoking && !peer->banned )
526    {
527        int block;
528        while( peer->inRequestCount < OUR_REQUEST_COUNT )
529        {
530            block = chooseBlock( tor, peer );
531            if( block < 0 )
532            {
533                break;
534            }
535            sendRequest( tor, peer, block );
536        }
537    }
538
539    return TR_OK;
540}
541
542/***********************************************************************
543 * tr_peerIsConnected
544 ***********************************************************************
545 *
546 **********************************************************************/
547int tr_peerIsConnected( tr_peer_t * peer )
548{
549    return PEER_STATUS_CONNECTED == peer->status;
550}
551
552/***********************************************************************
553 * tr_peerIsIncoming
554 ***********************************************************************
555 *
556 **********************************************************************/
557int tr_peerIsFrom( tr_peer_t * peer )
558{
559    return peer->from;
560}
561
562int tr_peerAmChoking( tr_peer_t * peer )
563{
564    return peer->amChoking;
565}
566int tr_peerAmInterested( tr_peer_t * peer )
567{
568    return peer->amInterested;
569}
570int tr_peerIsChoking( tr_peer_t * peer )
571{
572    return peer->peerChoking;
573}
574int tr_peerIsInterested( tr_peer_t * peer )
575{
576    return peer->peerInterested;
577}
578
579/***********************************************************************
580 * tr_peerProgress
581 ***********************************************************************
582 *
583 **********************************************************************/
584float tr_peerProgress( tr_peer_t * peer )
585{
586    return peer->progress;
587}
588
589/***********************************************************************
590 * tr_peerPort
591 ***********************************************************************
592 * Returns peer's listening port in host byte order
593 **********************************************************************/
594int tr_peerPort( tr_peer_t * peer )
595{
596    return ntohs( peer->port );
597}
598
599/***********************************************************************
600 * tr_peerBitfield
601 ***********************************************************************
602 *
603 **********************************************************************/
604tr_bitfield_t * tr_peerBitfield( tr_peer_t * peer )
605{
606    return peer->bitfield;
607}
608
609float tr_peerDownloadRate( tr_peer_t * peer )
610{
611    return tr_rcRate( peer->download );
612}
613
614float tr_peerUploadRate( tr_peer_t * peer )
615{
616    return tr_rcRate( peer->upload );
617}
618
619void tr_peerChoke( tr_peer_t * peer )
620{
621    sendChoke( peer, 1 );
622    peer->lastChoke = tr_date();
623}
624
625void tr_peerUnchoke( tr_peer_t * peer )
626{
627    sendChoke( peer, 0 );
628    peer->lastChoke = tr_date();
629}
630
631uint64_t tr_peerLastChoke( tr_peer_t * peer )
632{
633    return peer->lastChoke;
634}
635
636void tr_peerSetOptimistic( tr_peer_t * peer, int o )
637{
638    peer->optimistic = o;
639}
640
641int tr_peerIsOptimistic( tr_peer_t * peer )
642{
643    return peer->optimistic;
644}
645
646static inline int peerIsBad( tr_peer_t * peer )
647{
648    return ( peer->badPcs > 4 + 2 * peer->goodPcs );
649}
650
651static inline int peerIsGood( tr_peer_t * peer )
652{
653    return ( peer->goodPcs > 3 * peer->badPcs );
654}
655
656void tr_peerBlame( tr_peer_t * peer, int piece, int success )
657{
658    tr_torrent_t * tor = peer->tor;
659
660    if( !peer->blamefield || !tr_bitfieldHas( peer->blamefield, piece ) )
661    {
662        return;
663    }
664
665    if( success )
666    {
667        peer->goodPcs++;
668
669        if( peer->banfield && peerIsGood( peer ) )
670        {
671            /* Assume the peer wasn't responsible for the bad pieces
672               we was banned for */
673            tr_bitfieldClear( peer->banfield );
674        }
675    }
676    else
677    {
678        peer->badPcs++;
679
680        /* Ban the peer for this piece */
681        if( !peer->banfield )
682        {
683            peer->banfield = tr_bitfieldNew( tor->info.pieceCount );
684        }
685        tr_bitfieldAdd( peer->banfield, piece );
686
687        if( peerIsBad( peer ) )
688        {
689            /* Full ban */
690            peer_dbg( "banned (%d / %d)", peer->goodPcs, peer->badPcs );
691            peer->banned = 1;
692            peer->peerInterested = 0;
693        }
694    }
695    tr_bitfieldRem( peer->blamefield, piece );
696}
697
698int tr_peerGetConnectable( tr_torrent_t * tor, uint8_t ** _buf )
699{
700    int count = 0;
701    uint8_t * buf;
702    tr_peer_t * peer;
703    int i;
704
705    if( tor->peerCount < 1 )
706    {
707        *_buf = NULL;
708        return 0;
709    }
710
711    buf = malloc( 6 * tor->peerCount );
712    for( i = 0; i < tor->peerCount; i++ )
713    {
714        peer = tor->peers[i];
715
716        /* Skip peers with no known listening port */
717        if( 0 == peer->port )
718            continue;
719
720        memcpy( &buf[count*6], &peer->addr, 4 );
721        memcpy( &buf[count*6+4], &peer->port, 2 );
722        count++;
723    }
724
725    if( count < 1 )
726    {
727        free( buf ); buf = NULL;
728    }
729    *_buf = buf;
730
731    return count * 6;
732}
Note: See TracBrowser for help on using the repository browser.