source: trunk/libtransmission/peer.c @ 1060

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

Saves a list of peers when you pause a torrent, and tries to reconnect to them next time you resume it

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