source: trunk/libtransmission/peer.c @ 133

Last change on this file since 133 was 133, checked in by titer, 16 years ago

Quick fix for the 'getting unconnectable' problem (adds a timeout on
incoming connections)

File size: 13.5 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005 Eric Petit
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23#include "transmission.h"
24
25#define MAX_REQUEST_COUNT 32
26#define OUR_REQUEST_COUNT 8  /* TODO: we should detect if we are on a
27                                high-speed network and adapt */
28
29typedef struct tr_request_s
30{
31    int index;
32    int begin;
33    int length;
34
35} tr_request_t;
36
37struct tr_peer_s
38{
39    struct in_addr addr;
40    in_port_t      port;
41
42#define PEER_STATUS_IDLE       1 /* Need to connect */
43#define PEER_STATUS_CONNECTING 2 /* Trying to send handshake */
44#define PEER_STATUS_HANDSHAKE  4 /* Waiting for peer's handshake */
45#define PEER_STATUS_CONNECTED  8 /* Got peer's handshake */
46    int            status;
47    int            socket;
48    uint64_t       date;
49    uint64_t       keepAlive;
50
51    char           amChoking;
52    char           amInterested;
53    char           peerChoking;
54    char           peerInterested;
55
56    int            optimistic;
57    uint64_t       lastChoke;
58
59    uint8_t        id[20];
60
61    uint8_t      * bitfield;
62
63    uint8_t      * buf;
64    int            size;
65    int            pos;
66
67    uint8_t      * outMessages;
68    int            outMessagesSize;
69    int            outMessagesPos;
70    uint8_t        outBlock[13+16384];
71    int            outBlockSize;
72    int            outBlockLoaded;
73    int            outBlockSending;
74
75    int            inRequestCount;
76    tr_request_t   inRequests[OUR_REQUEST_COUNT];
77    int            inIndex;
78    int            inBegin;
79    int            inLength;
80    uint64_t       inTotal;
81
82    int            outRequestCount;
83    tr_request_t   outRequests[MAX_REQUEST_COUNT];
84    uint64_t       outTotal;
85    uint64_t       outDate;
86    int            outSlow;
87
88    tr_ratecontrol_t * download;
89};
90
91#define peer_dbg( a... ) __peer_dbg( peer, ## a )
92static void __peer_dbg( tr_peer_t * peer, char * msg, ... )
93{
94    char    string[256];
95    va_list args;
96
97    va_start( args, msg );
98    sprintf( string, "%08x:%04x ",
99             (uint32_t) peer->addr.s_addr, peer->port );
100    vsnprintf( &string[14], sizeof( string ) - 14, msg, args );
101    va_end( args ); 
102
103    tr_dbg( "%s", string );
104}
105
106#include "peermessages.h"
107#include "peerutils.h"
108#include "peerparse.h"
109
110/***********************************************************************
111 * tr_peerAddOld
112 ***********************************************************************
113 * Tries to add a peer given its IP and port (received from a tracker
114 * which doesn't support the "compact" extension).
115 **********************************************************************/
116void tr_peerAddOld( tr_torrent_t * tor, char * ip, int port )
117{
118    struct in_addr addr;
119
120    if( tr_netResolve( ip, &addr ) )
121    {
122        return;
123    }
124
125    addWithAddr( tor, addr, htons( port ) );
126}
127
128/***********************************************************************
129 * tr_peerAddCompact
130 ***********************************************************************
131 * Tries to add a peer, using 'addr' and 'port' to connect to the peer.
132 **********************************************************************/
133void tr_peerAddCompact( tr_torrent_t * tor, struct in_addr addr,
134                        in_port_t port )
135{
136    addWithAddr( tor, addr, port );
137}
138
139/***********************************************************************
140 * tr_peerInit
141 ***********************************************************************
142 * Initializes a new peer.
143 **********************************************************************/
144tr_peer_t * tr_peerInit( struct in_addr addr, in_port_t port, int s )
145{
146    tr_peer_t * peer = peerInit();
147
148    peer->socket = s;
149    peer->addr   = addr;
150    peer->port   = port;
151    peer->status = PEER_STATUS_CONNECTING;
152
153    return peer;
154}
155
156void tr_peerAttach( tr_torrent_t * tor, tr_peer_t * peer )
157{
158    peerAttach( tor, peer );
159}
160
161void tr_peerDestroy( tr_fd_t * fdlimit, tr_peer_t * peer )
162{
163    if( peer->bitfield )
164    {
165        free( peer->bitfield );
166    }
167    if( peer->buf )
168    {
169        free( peer->buf );
170    }
171    if( peer->outMessages )
172    {
173        free( peer->outMessages );
174    }
175    if( peer->status > PEER_STATUS_IDLE )
176    {
177        tr_netClose( peer->socket );
178        tr_fdSocketClosed( fdlimit, 0 );
179    }
180    tr_rcClose( peer->download );
181    free( peer );
182}
183
184/***********************************************************************
185 * tr_peerRem
186 ***********************************************************************
187 * Frees and closes everything related to the peer at index 'i', and
188 * removes it from the peers list.
189 **********************************************************************/
190void tr_peerRem( tr_torrent_t * tor, int i )
191{
192    tr_peer_t * peer = tor->peers[i];
193    int j;
194
195    for( j = 0; j < peer->inRequestCount; j++ )
196    {
197        tr_request_t * r;
198        int            block;
199
200        r     = &peer->inRequests[j];
201        block = tr_block( r->index,r->begin );
202        tr_cpDownloaderRem( tor->completion, block );
203    }
204    tr_peerDestroy( tor->fdlimit, peer );
205    tor->peerCount--;
206    memmove( &tor->peers[i], &tor->peers[i+1],
207             ( tor->peerCount - i ) * sizeof( tr_peer_t * ) );
208}
209
210/***********************************************************************
211 * tr_peerRead
212 ***********************************************************************
213 *
214 **********************************************************************/
215int tr_peerRead( tr_torrent_t * tor, tr_peer_t * peer )
216{
217    int ret;
218
219    /* Try to read */
220    for( ;; )
221    {
222        if( peer->size < 1 )
223        {
224            peer->size = 1024;
225            peer->buf  = malloc( peer->size );
226        }
227        else if( peer->pos >= peer->size )
228        {
229            peer->size *= 2;
230            peer->buf   = realloc( peer->buf, peer->size );
231        }
232        ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
233                          peer->size - peer->pos );
234        if( ret & TR_NET_CLOSE )
235        {
236            peer_dbg( "connection closed" );
237            return 1;
238        }
239        else if( ret & TR_NET_BLOCK )
240        {
241            break;
242        }
243        peer->date  = tr_date();
244        peer->pos  += ret;
245        if( NULL != tor )
246        {
247            tr_rcTransferred( peer->download, ret );
248            tr_rcTransferred( tor->download, ret );
249            tr_rcTransferred( tor->globalDownload, ret );
250            if( parseBuf( tor, peer ) )
251            {
252                return 1;
253            }
254        }
255        else
256        {
257            if( parseBufHeader( peer ) )
258            {
259                return 1;
260            }
261        }
262    }
263
264    return 0;
265}
266
267uint64_t tr_peerDate( tr_peer_t * peer )
268{
269    return peer->date;
270}
271
272/***********************************************************************
273 * tr_peerHash
274 ***********************************************************************
275 *
276 **********************************************************************/
277uint8_t * tr_peerHash( tr_peer_t * peer )
278{
279    return parseBufHash( peer );
280}
281
282/***********************************************************************
283 * tr_peerPulse
284 ***********************************************************************
285 *
286 **********************************************************************/
287void tr_peerPulse( tr_torrent_t * tor )
288{
289    int i, ret, size;
290    uint8_t * p;
291    tr_peer_t * peer;
292
293    if( tr_date() > tor->date + 1000 )
294    {
295        tor->date = tr_date();
296
297        for( i = 0; i < tor->peerCount; )
298        {
299            if( checkPeer( tor, i ) )
300            {
301                tr_peerRem( tor, i );
302                continue;
303            }
304            i++;
305        }
306    }
307
308    if( tor->status & TR_STATUS_STOPPING )
309    {
310        return;
311    }
312   
313    /* Shuffle peers */
314    if( tor->peerCount > 1 )
315    {
316        peer = tor->peers[0];
317        memmove( &tor->peers[0], &tor->peers[1],
318                 ( tor->peerCount - 1 ) * sizeof( void * ) );
319        tor->peers[tor->peerCount - 1] = peer;
320    }
321
322    /* Handle peers */
323    for( i = 0; i < tor->peerCount; )
324    {
325        peer = tor->peers[i];
326
327        if( peer->status < PEER_STATUS_HANDSHAKE )
328        {
329            i++;
330            continue;
331        }
332
333        if( tr_peerRead( tor, tor->peers[i] ) )
334        {
335            goto dropPeer;
336        }
337
338        if( peer->status < PEER_STATUS_CONNECTED )
339        {
340            i++;
341            continue;
342        }
343
344        /* Try to write */
345writeBegin:
346
347        /* Send all smaller messages regardless of the upload cap */
348        while( ( p = messagesPending( peer, &size ) ) )
349        {
350            ret = tr_netSend( peer->socket, p, size );
351            if( ret & TR_NET_CLOSE )
352            {
353                goto dropPeer;
354            }
355            else if( ret & TR_NET_BLOCK )
356            {
357                goto writeEnd;
358            }
359            messagesSent( peer, ret );
360        }
361
362        /* Send pieces if we can */
363        while( ( p = blockPending( tor, peer, &size ) ) )
364        {
365            if( !tr_rcCanTransfer( tor->globalUpload ) )
366            {
367                break;
368            }
369
370            ret = tr_netSend( peer->socket, p, size );
371            if( ret & TR_NET_CLOSE )
372            {
373                goto dropPeer;
374            }
375            else if( ret & TR_NET_BLOCK )
376            {
377                break;
378            }
379
380            blockSent( peer, ret );
381            tr_rcTransferred( tor->upload, ret );
382            tr_rcTransferred( tor->globalUpload, ret );
383
384            tor->uploaded  += ret;
385            peer->outTotal += ret;
386            peer->outDate   = tr_date();
387
388            /* In case this block is done, you may have messages
389               pending. Send them before we start the next block */
390            goto writeBegin;
391        }
392writeEnd:
393
394        /* Ask for a block whenever possible */
395        if( !tr_cpIsSeeding( tor->completion ) &&
396            !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
397        {
398            /* This peer is no use to us, and it seems there are
399               more */
400            peer_dbg( "not interesting" );
401            tr_peerRem( tor, i );
402            continue;
403        }
404
405        if( peer->amInterested && !peer->peerChoking )
406        {
407            int block;
408            while( peer->inRequestCount < OUR_REQUEST_COUNT )
409            {
410                block = chooseBlock( tor, peer );
411                if( block < 0 )
412                {
413                    break;
414                }
415                sendRequest( tor, peer, block );
416            }
417        }
418       
419        i++;
420        continue;
421
422dropPeer:
423        tr_peerRem( tor, i );
424    }
425}
426
427/***********************************************************************
428 * tr_peerIsConnected
429 ***********************************************************************
430 *
431 **********************************************************************/
432int tr_peerIsConnected( tr_peer_t * peer )
433{
434    return peer->status & PEER_STATUS_CONNECTED;
435}
436
437/***********************************************************************
438 * tr_peerIsUploading
439 ***********************************************************************
440 *
441 **********************************************************************/
442int tr_peerIsUploading( tr_peer_t * peer )
443{
444    return ( peer->inRequestCount > 0 );
445}
446
447/***********************************************************************
448 * tr_peerIsDownloading
449 ***********************************************************************
450 *
451 **********************************************************************/
452int tr_peerIsDownloading( tr_peer_t * peer )
453{
454    return peer->outBlockSending;
455}
456
457/***********************************************************************
458 * tr_peerBitfield
459 ***********************************************************************
460 *
461 **********************************************************************/
462uint8_t * tr_peerBitfield( tr_peer_t * peer )
463{
464    return peer->bitfield;
465}
466
467float tr_peerDownloadRate( tr_peer_t * peer )
468{
469    return tr_rcRate( peer->download );
470}
471
472int tr_peerIsUnchoked( tr_peer_t * peer )
473{
474    return !peer->amChoking;
475}
476
477int tr_peerIsInterested  ( tr_peer_t * peer )
478{
479    return peer->peerInterested;
480}
481
482void tr_peerChoke( tr_peer_t * peer )
483{
484    sendChoke( peer, 1 );
485    peer->lastChoke = tr_date();
486}
487
488void tr_peerUnchoke( tr_peer_t * peer )
489{
490    sendChoke( peer, 0 );
491    peer->lastChoke = tr_date();
492}
493
494uint64_t tr_peerLastChoke( tr_peer_t * peer )
495{
496    return peer->lastChoke;
497}
498
499void tr_peerSetOptimistic( tr_peer_t * peer, int o )
500{
501    peer->optimistic = o;
502}
503
504int tr_peerIsOptimistic( tr_peer_t * peer )
505{
506    return peer->optimistic;
507}
Note: See TracBrowser for help on using the repository browser.