source: branches/pex/libtransmission/peer.c @ 1540

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

Implement Azureus peer protocol, including PEX message.
Implement extended messages, including uTorrent PEX.

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