source: trunk/libtransmission/peer-io.c @ 3841

Last change on this file since 3841 was 3841, checked in by charles, 15 years ago

tweak the libevent read buffer size

  • Property svn:keywords set to Date Rev Author Id
File size: 12.3 KB
Line 
1/*
2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: peer-io.c 3841 2007-11-16 21:47:55Z charles $
11 */
12
13#include <assert.h>
14#include <string.h>
15#include <stdio.h>
16#include <unistd.h>
17#include <netinet/in.h> /* struct in_addr */
18#include <arpa/inet.h> /* inet_ntoa */
19
20#include <event.h>
21
22#include "transmission.h"
23#include "crypto.h"
24#include "net.h"
25#include "peer-io.h"
26#include "ratecontrol.h"
27#include "trevent.h"
28#include "utils.h"
29
30#define IO_TIMEOUT_SECS 8
31
32#define TR_RDBUF 2048
33
34/**
35***
36**/
37
38struct tr_extensions
39{
40    unsigned int extendedProtocolSupported : 1;
41    unsigned int fastPeersSupported : 1;
42};
43
44struct tr_peerIo
45{
46    struct tr_handle * handle;
47
48    struct in_addr in_addr;
49    int port;
50    int socket;
51    int encryptionMode;
52    struct bufferevent * bufev;
53    uint8_t peerId[20];
54
55    tr_extensions extensions;
56
57    unsigned int isEncrypted : 1;
58    unsigned int isIncoming : 1;
59    unsigned int peerIdIsSet : 1;
60
61    tr_can_read_cb     canRead;
62    tr_did_write_cb    didWrite;
63    tr_net_error_cb    gotError;
64    void             * userData;
65
66    tr_crypto * crypto;
67};
68
69/**
70***
71**/
72
73static void
74didWriteWrapper( struct bufferevent * e, void * userData )
75{
76    tr_peerIo * c = userData;
77    if( c->didWrite != NULL )
78        (*c->didWrite)( e, c->userData );
79}
80
81static void
82canReadWrapper( struct bufferevent * e, void * userData )
83{
84    int done = 0;
85    tr_peerIo * c = userData;
86    tr_handle * handle = c->handle;
87
88    if( c->canRead == NULL )
89        return;
90
91    tr_globalLock( handle );
92
93    while( !done )
94    {
95        const int ret = (*c->canRead)( e, c->userData );
96
97        switch( ret )
98        {
99            case READ_AGAIN:
100                if( EVBUFFER_LENGTH( e->input ) )
101                    continue;
102            case READ_MORE:
103            case READ_DONE:
104                done = 1;
105        }
106    }
107
108    tr_globalUnlock( handle );
109}
110
111static void
112gotErrorWrapper( struct bufferevent * e, short what, void * userData )
113{
114    tr_peerIo * c = userData;
115    if( c->gotError != NULL )
116        (*c->gotError)( e, what, c->userData );
117}
118
119/**
120***
121**/
122
123void bufferevent_setwatermark(struct bufferevent *, short, size_t, size_t);
124
125static tr_peerIo*
126tr_peerIoNew( struct tr_handle     * handle,
127              const struct in_addr * in_addr,
128              uint16_t               port,
129              const uint8_t        * torrentHash,
130              int                    isIncoming,
131              int                    socket )
132{
133    tr_peerIo * c;
134    c = tr_new0( tr_peerIo, 1 );
135    c->crypto = tr_cryptoNew( torrentHash, isIncoming );
136    c->handle = handle;
137    c->in_addr = *in_addr;
138    c->port = port;
139    c->socket = socket;
140    c->isIncoming = isIncoming ? 1 : 0;
141    c->bufev = bufferevent_new( c->socket,
142                                canReadWrapper,
143                                didWriteWrapper,
144                                gotErrorWrapper,
145                                c );
146    bufferevent_settimeout( c->bufev, IO_TIMEOUT_SECS, IO_TIMEOUT_SECS );
147    bufferevent_enable( c->bufev, EV_READ|EV_WRITE );
148    bufferevent_setwatermark( c->bufev, EV_READ, 0, TR_RDBUF );
149
150    return c;
151}
152
153tr_peerIo*
154tr_peerIoNewIncoming( struct tr_handle      * handle,
155                      const struct in_addr  * in_addr,
156                      uint16_t                port,
157                      int                     socket )
158{
159    assert( handle != NULL );
160    assert( in_addr != NULL );
161    assert( socket >= 0 );
162
163    return tr_peerIoNew( handle, in_addr, port,
164                         NULL, 1,
165                         socket );
166}
167
168tr_peerIo*
169tr_peerIoNewOutgoing( struct tr_handle      * handle,
170                      const struct in_addr  * in_addr,
171                      int                     port,
172                      const uint8_t         * torrentHash )
173{
174    assert( handle != NULL );
175    assert( in_addr != NULL );
176    assert( port >= 0 );
177    assert( torrentHash != NULL );
178
179    return tr_peerIoNew( handle, in_addr, port,
180                         torrentHash, 0,
181                         tr_netOpenTCP( in_addr, port, 0 ) );
182}
183
184static void
185io_dtor( void * vio )
186{
187    tr_peerIo * io = vio;
188
189    bufferevent_free( io->bufev );
190    tr_netClose( io->socket );
191    tr_cryptoFree( io->crypto );
192    tr_free( io );
193}
194
195void
196tr_peerIoFree( tr_peerIo * io )
197{
198    if( io != NULL )
199    {
200        io->canRead = NULL;
201        io->didWrite = NULL;
202        io->gotError = NULL;
203        tr_runInEventThread( io->handle, io_dtor, io );
204    }
205}
206
207tr_handle*
208tr_peerIoGetHandle( tr_peerIo * io )
209{
210    assert( io != NULL );
211    assert( io->handle != NULL );
212
213    return io->handle;
214}
215
216const struct in_addr*
217tr_peerIoGetAddress( const tr_peerIo * io, uint16_t * port )
218{
219    assert( io != NULL );
220
221    if( port != NULL )
222       *port = io->port;
223
224    return &io->in_addr;
225}
226
227const char*
228tr_peerIoAddrStr( const struct in_addr * addr, uint16_t port )
229{
230    static char buf[512];
231    snprintf( buf, sizeof(buf), "%s:%u", inet_ntoa( *addr ), (unsigned int)port );
232    return buf;
233}
234
235const char*
236tr_peerIoGetAddrStr( const tr_peerIo * io )
237{
238    return tr_peerIoAddrStr( &io->in_addr, io->port );
239}
240
241void
242tr_peerIoTryRead( tr_peerIo * io )
243{
244    if( EVBUFFER_LENGTH( io->bufev->input ) )
245        canReadWrapper( io->bufev, io );
246}
247
248void 
249tr_peerIoSetIOFuncs( tr_peerIo          * io,
250                     tr_can_read_cb       readcb,
251                     tr_did_write_cb      writecb,
252                     tr_net_error_cb      errcb,
253                     void               * userData )
254{
255    io->canRead = readcb;
256    io->didWrite = writecb;
257    io->gotError = errcb;
258    io->userData = userData;
259
260    tr_peerIoTryRead( io );
261}
262
263int
264tr_peerIoIsIncoming( const tr_peerIo * c )
265{
266    return c->isIncoming ? 1 : 0;
267}
268
269int
270tr_peerIoReconnect( tr_peerIo * io )
271{
272    assert( !tr_peerIoIsIncoming( io ) );
273
274    if( io->socket >= 0 )
275        tr_netClose( io->socket );
276
277    io->socket = tr_netOpenTCP( &io->in_addr, io->port, 0 );
278 
279    if( io->socket >= 0 )
280    {
281        bufferevent_free( io->bufev );
282
283        io->bufev = bufferevent_new( io->socket,
284                                     canReadWrapper,
285                                     didWriteWrapper,
286                                     gotErrorWrapper,
287                                     io );
288        bufferevent_settimeout( io->bufev, IO_TIMEOUT_SECS, IO_TIMEOUT_SECS );
289        bufferevent_enable( io->bufev, EV_READ|EV_WRITE );
290        bufferevent_setwatermark( io->bufev, EV_READ, 0, TR_RDBUF );
291
292        return 0;
293    }
294 
295    return -1;
296}
297
298void
299tr_peerIoSetTimeoutSecs( tr_peerIo * io, int secs )
300{
301    bufferevent_settimeout( io->bufev, secs, secs );
302}
303
304/**
305***
306**/
307
308void
309tr_peerIoSetTorrentHash( tr_peerIo     * io,
310                         const uint8_t * hash )
311{
312    assert( io != NULL );
313
314    tr_cryptoSetTorrentHash( io->crypto, hash );
315}
316
317const uint8_t*
318tr_peerIoGetTorrentHash( tr_peerIo * io )
319{
320    assert( io != NULL );
321    assert( io->crypto != NULL );
322
323    return tr_cryptoGetTorrentHash( io->crypto );
324}
325
326int
327tr_peerIoHasTorrentHash( const tr_peerIo * io )
328{
329    assert( io != NULL );
330    assert( io->crypto != NULL );
331
332    return tr_cryptoHasTorrentHash( io->crypto );
333}
334
335/**
336***
337**/
338
339void
340tr_peerIoSetPeersId( tr_peerIo     * io,
341                     const uint8_t * peer_id )
342{
343    assert( io != NULL );
344
345    if(( io->peerIdIsSet = peer_id != NULL ))
346        memcpy( io->peerId, peer_id, 20 );
347    else
348        memset( io->peerId, 0, 20 );
349}
350
351const uint8_t* 
352tr_peerIoGetPeersId( const tr_peerIo * io )
353{
354    assert( io != NULL );
355    assert( io->peerIdIsSet );
356
357    return io->peerId;
358}
359
360/**
361***
362**/
363
364void
365tr_peerIoEnableLTEP( tr_peerIo * io, int flag )
366{
367    assert( io != NULL );
368    assert( flag==0 || flag==1 );
369   
370    io->extensions.extendedProtocolSupported = flag;
371}
372
373void
374tr_peerIoEnableFEXT( tr_peerIo * io, int flag )
375{
376    assert( io != NULL );
377    assert( flag==0 || flag==1 );
378   
379    io->extensions.fastPeersSupported = flag;
380}
381
382int
383tr_peerIoSupportsLTEP( const tr_peerIo * io )
384{
385    assert( io != NULL );
386   
387    return io->extensions.extendedProtocolSupported;
388}
389
390int
391tr_peerIoSupportsFEXT( const tr_peerIo * io )
392{
393    assert( io != NULL );
394   
395    return io->extensions.fastPeersSupported;
396}
397/**
398***
399**/
400
401size_t
402tr_peerIoWriteBytesWaiting( const tr_peerIo * io )
403{
404    return EVBUFFER_LENGTH( EVBUFFER_OUTPUT( io->bufev ) );
405}
406 
407void
408tr_peerIoWrite( tr_peerIo   * io,
409                const void  * writeme,
410                int           writeme_len )
411{
412    assert( tr_amInEventThread( io->handle ) );
413    bufferevent_write( io->bufev, (void*)writeme, writeme_len );
414}
415
416void
417tr_peerIoWriteBuf( tr_peerIo       * io,
418                   struct evbuffer * buf )
419{
420    tr_peerIoWrite( io, EVBUFFER_DATA(buf), EVBUFFER_LENGTH(buf) );
421    evbuffer_drain( buf, ~0 );
422}
423
424/**
425***
426**/
427
428tr_crypto* 
429tr_peerIoGetCrypto( tr_peerIo * c )
430{
431    return c->crypto;
432}
433
434void 
435tr_peerIoSetEncryption( tr_peerIo * io,
436                        int         encryptionMode )
437{
438    assert( io != NULL );
439    assert( encryptionMode==PEER_ENCRYPTION_NONE || encryptionMode==PEER_ENCRYPTION_RC4 );
440
441    io->encryptionMode = encryptionMode;
442}
443
444int
445tr_peerIoIsEncrypted( const tr_peerIo * io )
446{
447    return io!=NULL && io->encryptionMode==PEER_ENCRYPTION_RC4;
448}
449
450void
451tr_peerIoWriteBytes( tr_peerIo        * io,
452                     struct evbuffer  * outbuf,
453                     const void       * bytes,
454                     int                byteCount )
455{
456    uint8_t * tmp;
457
458    switch( io->encryptionMode )
459    {
460        case PEER_ENCRYPTION_NONE:
461            evbuffer_add( outbuf, bytes, byteCount );
462            break;
463
464        case PEER_ENCRYPTION_RC4:
465            tmp = tr_new( uint8_t, byteCount );
466            tr_cryptoEncrypt( io->crypto, byteCount, bytes, tmp );
467            evbuffer_add( outbuf, tmp, byteCount );
468            tr_free( tmp );
469            break;
470
471        default:
472            assert( 0 );
473    }
474}
475
476void
477tr_peerIoWriteUint16( tr_peerIo        * io,
478                      struct evbuffer  * outbuf,
479                      uint16_t           writeme )
480{
481    uint16_t tmp = htons( writeme );
482    tr_peerIoWriteBytes( io, outbuf, &tmp, sizeof(uint16_t) );
483}
484
485void
486tr_peerIoWriteUint8( tr_peerIo        * io,
487                     struct evbuffer  * outbuf,
488                     uint8_t            writeme )
489{
490    tr_peerIoWriteBytes( io, outbuf, &writeme, sizeof(uint8_t) );
491}
492
493void
494tr_peerIoWriteUint32( tr_peerIo        * io,
495                      struct evbuffer  * outbuf,
496                      uint32_t           writeme )
497{
498    uint32_t tmp = htonl( writeme );
499    tr_peerIoWriteBytes( io, outbuf, &tmp, sizeof(uint32_t) );
500}
501
502void
503tr_peerIoReadBytes( tr_peerIo        * io,
504                    struct evbuffer  * inbuf,
505                    void             * bytes,
506                    int                byteCount )
507{
508    assert( (int)EVBUFFER_LENGTH( inbuf ) >= byteCount );
509
510    switch( io->encryptionMode )
511    {
512        case PEER_ENCRYPTION_NONE:
513            evbuffer_remove( inbuf, bytes, byteCount );
514            break;
515
516        case PEER_ENCRYPTION_RC4:
517            evbuffer_remove( inbuf, bytes, byteCount );
518            tr_cryptoDecrypt( io->crypto, byteCount, bytes, bytes );
519            break;
520
521        default:
522            assert( 0 );
523    }
524}
525
526void
527tr_peerIoReadUint16( tr_peerIo         * io,
528                     struct evbuffer   * inbuf,
529                     uint16_t          * setme )
530{
531    uint16_t tmp;
532    tr_peerIoReadBytes( io, inbuf, &tmp, sizeof(uint16_t) );
533    *setme = ntohs( tmp );
534}
535
536void
537tr_peerIoReadUint8( tr_peerIo         * io,
538                    struct evbuffer   * inbuf,
539                    uint8_t           * setme )
540{
541    tr_peerIoReadBytes( io, inbuf, setme, sizeof(uint8_t) );
542}
543
544void
545tr_peerIoReadUint32( tr_peerIo         * io,
546                     struct evbuffer   * inbuf,
547                     uint32_t          * setme )
548{
549    uint32_t tmp;
550    tr_peerIoReadBytes( io, inbuf, &tmp, sizeof(uint32_t) );
551    *setme = ntohl( tmp );
552}
553
554void
555tr_peerIoDrain( tr_peerIo        * io,
556                struct evbuffer  * inbuf,
557                int                byteCount )
558{
559    uint8_t * tmp = tr_new( uint8_t, byteCount );
560    tr_peerIoReadBytes( io, inbuf, tmp, byteCount );
561    tr_free( tmp );
562}
Note: See TracBrowser for help on using the repository browser.