source: trunk/libtransmission/peer.c @ 774

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

Retrieve peer address as a string via tr_torrentPeers().

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