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

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

The OpenBSD build fix caused a Darwin build error. :) Thanks to pea for reporting.

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