source: trunk/libtransmission/peer.c @ 1424

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

clean up

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