source: trunk/libtransmission/peer.c @ 1

Last change on this file since 1 was 1, checked in by root, 15 years ago

Import from 2005-10-26

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    uint8_t        id[20];
57
58    uint8_t      * bitfield;
59
60    uint8_t      * buf;
61    int            size;
62    int            pos;
63
64    uint8_t      * outMessages;
65    int            outMessagesSize;
66    int            outMessagesPos;
67    uint8_t        outBlock[13+16384];
68    int            outBlockSize;
69    int            outBlockLoaded;
70    int            outBlockSending;
71
72    int            inRequestCount;
73    tr_request_t   inRequests[OUR_REQUEST_COUNT];
74    int            inIndex;
75    int            inBegin;
76    int            inLength;
77    uint64_t       inTotal;
78
79    int            outRequestCount;
80    tr_request_t   outRequests[MAX_REQUEST_COUNT];
81    uint64_t       outTotal;
82    uint64_t       outDate;
83    int            outSlow;
84};
85
86#define peer_dbg( a... ) __peer_dbg( peer, ## a )
87static void __peer_dbg( tr_peer_t * peer, char * msg, ... )
88{
89    char    string[256];
90    va_list args;
91
92    va_start( args, msg );
93    sprintf( string, "%08x:%04x ",
94             (uint32_t) peer->addr.s_addr, peer->port );
95    vsnprintf( &string[14], sizeof( string ) - 14, msg, args );
96    va_end( args ); 
97
98    tr_dbg( "%s", string );
99}
100
101#include "peermessages.h"
102#include "peerutils.h"
103
104/***********************************************************************
105 * tr_peerAddOld
106 ***********************************************************************
107 * Tries to add a peer given its IP and port (received from a tracker
108 * which doesn't support the "compact" extension).
109 **********************************************************************/
110void tr_peerAddOld( tr_torrent_t * tor, char * ip, int port )
111{
112    struct in_addr addr;
113
114    if( tr_netResolve( ip, &addr ) )
115    {
116        return;
117    }
118
119    addWithAddr( tor, addr, htons( port ) );
120}
121
122/***********************************************************************
123 * tr_peerAddCompact
124 ***********************************************************************
125 * Tries to add a peer. If 's' is a negative value, will use 'addr' and
126 * 'port' to connect to the peer. Otherwise, use the already connected
127 * socket 's'.
128 **********************************************************************/
129void tr_peerAddCompact( tr_torrent_t * tor, struct in_addr addr,
130                        in_port_t port, int s )
131{
132    tr_peer_t * peer;
133
134    if( s < 0 )
135    {
136        addWithAddr( tor, addr, port );
137        return;
138    }
139
140    if( !( peer = peerInit( tor ) ) )
141    {
142        tr_netClose( s );
143        tr_fdSocketClosed( tor->fdlimit, 0 );
144        return;
145    }
146
147    peer->socket = s;
148    peer->addr   = addr;
149    peer->port   = port;
150    peer->status = PEER_STATUS_CONNECTING;
151}
152
153/***********************************************************************
154 * tr_peerRem
155 ***********************************************************************
156 * Frees and closes everything related to the peer at index 'i', and
157 * removes it from the peers list.
158 **********************************************************************/
159void tr_peerRem( tr_torrent_t * tor, int i )
160{
161    tr_peer_t * peer = tor->peers[i];
162    int j;
163
164    for( j = 0; j < peer->inRequestCount; j++ )
165    {
166        tr_request_t * r;
167        int            block;
168
169        r     = &peer->inRequests[j];
170        block = tr_block( r->index,r->begin );
171        if( tor->blockHave[block] > 0 )
172        {
173          (tor->blockHave[block])--;
174        }
175    }
176    if( !peer->amChoking )
177    {
178        tr_uploadChoked( tor->upload );
179    }
180    if( peer->bitfield )
181    {
182        free( peer->bitfield );
183    }
184    if( peer->buf )
185    {
186        free( peer->buf );
187    }
188    if( peer->outMessages )
189    {
190        free( peer->outMessages );
191    }
192    if( peer->status > PEER_STATUS_IDLE )
193    {
194        tr_netClose( peer->socket );
195        tr_fdSocketClosed( tor->fdlimit, 0 );
196    }
197    free( peer );
198    tor->peerCount--;
199    memmove( &tor->peers[i], &tor->peers[i+1],
200             ( tor->peerCount - i ) * sizeof( tr_peer_t * ) );
201}
202
203/***********************************************************************
204 * tr_peerPulse
205 ***********************************************************************
206 *
207 **********************************************************************/
208void tr_peerPulse( tr_torrent_t * tor )
209{
210    int i, ret, size;
211    uint8_t * p;
212    tr_peer_t * peer;
213
214    tor->dates[9] = tr_date();
215    if( tor->dates[9] > tor->dates[8] + 1000 )
216    {
217        memmove( &tor->downloaded[0], &tor->downloaded[1],
218                 9 * sizeof( uint64_t ) );
219        memmove( &tor->uploaded[0], &tor->uploaded[1],
220                 9 * sizeof( uint64_t ) );
221        memmove( &tor->dates[0], &tor->dates[1],
222                 9 * sizeof( uint64_t ) );
223
224        for( i = 0; i < tor->peerCount; )
225        {
226            if( checkPeer( tor, i ) )
227            {
228                tr_peerRem( tor, i );
229                continue;
230            }
231            i++;
232        }
233    }
234
235    /* Check for incoming connections */
236    if( tor->bindSocket > -1 &&
237        tor->peerCount < TR_MAX_PEER_COUNT &&
238        !tr_fdSocketWillCreate( tor->fdlimit, 0 ) )
239    {
240        int            s;
241        struct in_addr addr;
242        in_port_t      port;
243        s = tr_netAccept( tor->bindSocket, &addr, &port );
244        if( s > -1 )
245        {
246            tr_peerAddCompact( tor, addr, port, s );
247        }
248        else
249        {
250            tr_fdSocketClosed( tor->fdlimit, 0 );
251        }
252    }
253   
254    /* Shuffle peers */
255    if( tor->peerCount > 1 )
256    {
257        peer = tor->peers[0];
258        memmove( &tor->peers[0], &tor->peers[1],
259                 ( tor->peerCount - 1 ) * sizeof( void * ) );
260        tor->peers[tor->peerCount - 1] = peer;
261    }
262
263    /* Handle peers */
264    for( i = 0; i < tor->peerCount; )
265    {
266        peer = tor->peers[i];
267
268        /* Connect */
269        if( ( peer->status & PEER_STATUS_IDLE ) &&
270            !tr_fdSocketWillCreate( tor->fdlimit, 0 ) )
271        {
272            peer->socket = tr_netOpen( peer->addr, peer->port );
273            if( peer->socket < 0 )
274            {
275                peer_dbg( "connection failed" );
276                goto dropPeer;
277            }
278            peer->status = PEER_STATUS_CONNECTING;
279        }
280
281        /* Try to send handshake */
282        if( peer->status & PEER_STATUS_CONNECTING )
283        {
284            uint8_t buf[68];
285            tr_info_t * inf = &tor->info;
286
287            buf[0] = 19;
288            memcpy( &buf[1], "BitTorrent protocol", 19 );
289            memset( &buf[20], 0, 8 );
290            memcpy( &buf[28], inf->hash, 20 );
291            memcpy( &buf[48], tor->id, 20 );
292
293            ret = tr_netSend( peer->socket, buf, 68 );
294            if( ret & TR_NET_CLOSE )
295            {
296                peer_dbg( "connection closed" );
297                goto dropPeer;
298            }
299            else if( !( ret & TR_NET_BLOCK ) )
300            {
301                peer_dbg( "SEND handshake" );
302                peer->status = PEER_STATUS_HANDSHAKE;
303            }
304        }
305
306        /* Try to read */
307        if( peer->status >= PEER_STATUS_HANDSHAKE )
308        {
309            for( ;; )
310            {
311                if( peer->size < 1 )
312                {
313                    peer->size = 1024;
314                    peer->buf  = malloc( peer->size );
315                }
316                else if( peer->pos >= peer->size )
317                {
318                    peer->size *= 2;
319                    peer->buf   = realloc( peer->buf, peer->size );
320                }
321                ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
322                                  peer->size - peer->pos );
323                if( ret & TR_NET_CLOSE )
324                {
325                    peer_dbg( "connection closed" );
326                    goto dropPeer;
327                }
328                else if( ret & TR_NET_BLOCK )
329                {
330                    break;
331                }
332                peer->date  = tr_date();
333                peer->pos  += ret;
334                if( parseMessage( tor, peer, ret ) )
335                {
336                    goto dropPeer;
337                }
338            }
339        }
340
341        /* Try to write */
342writeBegin:
343
344        /* Send all smaller messages regardless of the upload cap */
345        while( ( p = messagesPending( peer, &size ) ) )
346        {
347            ret = tr_netSend( peer->socket, p, size );
348            if( ret & TR_NET_CLOSE )
349            {
350                goto dropPeer;
351            }
352            else if( ret & TR_NET_BLOCK )
353            {
354                goto writeEnd;
355            }
356            messagesSent( peer, ret );
357        }
358
359        /* Send pieces if we can */
360        while( ( p = blockPending( tor, peer, &size ) ) )
361        {
362            if( !tr_uploadCanUpload( tor->upload ) )
363            {
364                break;
365            }
366
367            ret = tr_netSend( peer->socket, p, size );
368            if( ret & TR_NET_CLOSE )
369            {
370                goto dropPeer;
371            }
372            else if( ret & TR_NET_BLOCK )
373            {
374                break;
375            }
376
377            blockSent( peer, ret );
378            tr_uploadUploaded( tor->upload, ret );
379
380            tor->uploaded[9] += ret;
381            peer->outTotal   += ret;
382            peer->outDate     = tr_date();
383
384            /* In case this block is done, you may have messages
385               pending. Send them before we start the next block */
386            goto writeBegin;
387        }
388writeEnd:
389
390        /* Connected peers: ask for a block whenever possible */
391        if( peer->status & PEER_STATUS_CONNECTED )
392        {
393            if( tor->blockHaveCount < tor->blockCount &&
394                !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
395            {
396                /* This peer is no use to us, and it seems there are
397                   more */
398                peer_dbg( "not interesting" );
399                tr_peerRem( tor, i );
400                continue;
401            }
402
403            if( peer->amInterested && !peer->peerChoking )
404            {
405                int block;
406                while( peer->inRequestCount < OUR_REQUEST_COUNT )
407                {
408                    block = chooseBlock( tor, peer );
409                    if( block < 0 )
410                    {
411                        break;
412                    }
413                    sendRequest( tor, peer, block );
414                }
415            }
416        }
417       
418        i++;
419        continue;
420
421dropPeer:
422        tr_peerRem( tor, i );
423    }
424}
425
426/***********************************************************************
427 * tr_peerIsConnected
428 ***********************************************************************
429 *
430 **********************************************************************/
431int tr_peerIsConnected( tr_peer_t * peer )
432{
433    return peer->status & PEER_STATUS_CONNECTED;
434}
435
436/***********************************************************************
437 * tr_peerIsUploading
438 ***********************************************************************
439 *
440 **********************************************************************/
441int tr_peerIsUploading( tr_peer_t * peer )
442{
443    return ( peer->inRequestCount > 0 );
444}
445
446/***********************************************************************
447 * tr_peerIsDownloading
448 ***********************************************************************
449 *
450 **********************************************************************/
451int tr_peerIsDownloading( tr_peer_t * peer )
452{
453    return peer->outBlockSending;
454}
455
456/***********************************************************************
457 * tr_peerBitfield
458 ***********************************************************************
459 *
460 **********************************************************************/
461uint8_t * tr_peerBitfield( tr_peer_t * peer )
462{
463    return peer->bitfield;
464}
Note: See TracBrowser for help on using the repository browser.