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

Last change on this file since 6991 was 6991, checked in by charles, 14 years ago

(libT) fix unlimited-upload-speed bug reported by Gimp_ and Lacrocivious

  • Property svn:keywords set to Date Rev Author Id
File size: 20.0 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 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 6991 2008-10-30 03:41:45Z charles $
11 */
12
13#include <assert.h>
14#include <limits.h> /* INT_MAX */
15#include <string.h>
16#include <stdio.h>
17#include <unistd.h>
18
19#ifdef WIN32
20 #include <winsock2.h>
21#else
22 #include <netinet/in.h> /* struct in_addr */
23 #include <arpa/inet.h> /* inet_ntoa */
24#endif
25
26#include <event.h>
27
28#include "transmission.h"
29#include "crypto.h"
30#include "net.h"
31#include "peer-io.h"
32#include "ratecontrol.h"
33#include "trevent.h"
34#include "utils.h"
35
36#define IO_TIMEOUT_SECS 8
37
38/**
39***
40**/
41
42#define dbgmsg( io, ... ) \
43    do { \
44        if( tr_deepLoggingIsActive( ) ) \
45            tr_deepLog( __FILE__, __LINE__, tr_peerIoGetAddrStr( io ), __VA_ARGS__ ); \
46    } while( 0 )
47
48struct tr_bandwidth
49{
50    unsigned int    isUnlimited : 1;
51    size_t          bytesUsed;
52    size_t          bytesLeft;
53};
54
55struct tr_peerIo
56{
57    unsigned int           isEncrypted               : 1;
58    unsigned int           isIncoming                : 1;
59    unsigned int           peerIdIsSet               : 1;
60    unsigned int           extendedProtocolSupported : 1;
61    unsigned int           fastPeersSupported        : 1;
62
63    uint8_t                encryptionMode;
64    uint8_t                timeout;
65    uint16_t               port;
66    int                    socket;
67
68    uint8_t                peerId[20];
69    time_t                 timeCreated;
70
71    tr_session *           session;
72
73    struct in_addr         in_addr;
74    struct bufferevent *   bufev;
75    struct evbuffer *      output;
76
77    tr_can_read_cb         canRead;
78    tr_did_write_cb        didWrite;
79    tr_net_error_cb        gotError;
80    void *                 userData;
81
82    size_t                 bufferSize[2];
83
84    struct tr_bandwidth    bandwidth[2];
85    tr_ratecontrol *       speedometer[2];
86
87    tr_crypto *            crypto;
88};
89
90/**
91***
92**/
93
94static void
95adjustOutputBuffer( tr_peerIo * io )
96{
97    struct evbuffer * live = EVBUFFER_OUTPUT( io->bufev );
98
99    if( io->bandwidth[TR_UP].isUnlimited )
100    {
101        bufferevent_write_buffer( io->bufev, io->output );
102    }
103    else if( io->bandwidth[TR_UP].bytesLeft > EVBUFFER_LENGTH( live ) )
104    {
105        /* there's free space in bufev's output buffer;
106           try to fill it up */
107        const size_t desiredLength = io->bandwidth[TR_UP].bytesLeft;
108        const size_t under = desiredLength - EVBUFFER_LENGTH( live );
109        const size_t n = MIN( under, EVBUFFER_LENGTH( io->output ) );
110        bufferevent_write( io->bufev, EVBUFFER_DATA( io->output ), n );
111        evbuffer_drain( io->output, n );
112    }
113    else if( io->bandwidth[TR_UP].bytesLeft < EVBUFFER_LENGTH( live ) )
114    {
115        /* bufev's output buffer exceeds our bandwidth allocation;
116           move the excess out of bufev so it can't be sent yet */
117        const size_t      desiredLength = io->bandwidth[TR_UP].bytesLeft;
118        const size_t      over = EVBUFFER_LENGTH( live ) - desiredLength;
119        struct evbuffer * buf = evbuffer_new( );
120        evbuffer_add( buf, EVBUFFER_DATA( live ) + desiredLength, over );
121        evbuffer_add_buffer( buf, io->output );
122        evbuffer_free( io->output );
123        io->output = buf;
124        EVBUFFER_LENGTH( live ) = desiredLength;
125    }
126    else if( EVBUFFER_LENGTH( live ) )
127    {
128        bufferevent_enable( io->bufev, EV_WRITE );
129    }
130
131    io->bufferSize[TR_UP] = EVBUFFER_LENGTH( live );
132
133    dbgmsg( io, "after adjusting the output buffer, its size is now %zu",
134            io->bufferSize[TR_UP] );
135}
136
137static void
138adjustInputBuffer( tr_peerIo * io )
139{
140    if( io->bandwidth[TR_DOWN].isUnlimited )
141    {
142        dbgmsg( io, "unlimited reading..." );
143        bufferevent_setwatermark( io->bufev, EV_READ, 0, 0 );
144        bufferevent_enable( io->bufev, EV_READ );
145    }
146    else
147    {
148        const size_t n = io->bandwidth[TR_DOWN].bytesLeft;
149        if( n == 0 )
150        {
151            dbgmsg( io, "disabling reads because we've hit our limit" );
152            bufferevent_disable( io->bufev, EV_READ );
153        }
154        else
155        {
156            dbgmsg( io, "enabling reading of %zu more bytes", n );
157            bufferevent_setwatermark( io->bufev, EV_READ, 0, n );
158            bufferevent_enable( io->bufev, EV_READ );
159        }
160    }
161}
162
163/***
164****
165***/
166
167static void
168didWriteWrapper( struct bufferevent * e,
169                 void *               vio )
170{
171    tr_peerIo *  io = vio;
172    const size_t len = EVBUFFER_LENGTH( EVBUFFER_OUTPUT( e ) );
173
174    dbgmsg( io, "didWrite... io->outputBufferSize was %zu, is now %zu",
175            io->bufferSize[TR_UP], len );
176
177    if( len < io->bufferSize[TR_UP] )
178    {
179        const size_t          n = io->bufferSize[TR_UP] - len;
180        struct tr_bandwidth * b = &io->bandwidth[TR_UP];
181        b->bytesLeft -= MIN( b->bytesLeft, (size_t)n );
182        b->bytesUsed += n;
183        tr_rcTransferred( io->speedometer[TR_UP], n );
184        dbgmsg( io,
185                "wrote %zu bytes to peer... upload bytesLeft is now %zu",
186                n,
187                b->bytesLeft );
188    }
189
190    adjustOutputBuffer( io );
191
192    if( io->didWrite )
193        io->didWrite( e, io->userData );
194}
195
196static void
197canReadWrapper( struct bufferevent * e,
198                void *               vio )
199{
200    int          done = 0;
201    int          err = 0;
202    tr_peerIo *  io = vio;
203    tr_session * session = io->session;
204    const size_t len = EVBUFFER_LENGTH( EVBUFFER_INPUT( e ) );
205
206    dbgmsg( io, "canRead" );
207
208    /* if the input buffer has grown, record the bytes that were read */
209    if( len > io->bufferSize[TR_DOWN] )
210    {
211        const size_t          n = len - io->bufferSize[TR_DOWN];
212        struct tr_bandwidth * b = io->bandwidth + TR_DOWN;
213        b->bytesLeft -= MIN( b->bytesLeft, (size_t)n );
214        b->bytesUsed += n;
215        tr_rcTransferred( io->speedometer[TR_DOWN], n );
216        dbgmsg( io,
217                "%zu new input bytes. bytesUsed is %zu, bytesLeft is %zu",
218                n, b->bytesUsed,
219                b->bytesLeft );
220
221        adjustInputBuffer( io );
222    }
223
224    /* try to consume the input buffer */
225    if( io->canRead )
226    {
227        tr_globalLock( session );
228
229        while( !done && !err )
230        {
231            const int ret = io->canRead( e, io->userData );
232
233            switch( ret )
234            {
235                case READ_NOW:
236                    if( EVBUFFER_LENGTH( e->input ) )
237                        continue;
238                    done = 1;
239                    break;
240
241                case READ_LATER:
242                    done = 1;
243                    break;
244
245                case READ_ERR:
246                    err = 1;
247                    break;
248            }
249        }
250
251        tr_globalUnlock( session );
252    }
253
254    if( !err )
255        io->bufferSize[TR_DOWN] = EVBUFFER_LENGTH( EVBUFFER_INPUT( e ) );
256}
257
258static void
259gotErrorWrapper( struct bufferevent * e,
260                 short                what,
261                 void *               userData )
262{
263    tr_peerIo * c = userData;
264
265    if( c->gotError )
266        c->gotError( e, what, c->userData );
267}
268
269/**
270***
271**/
272
273static void
274bufevNew( tr_peerIo * io )
275{
276    io->bufev = bufferevent_new( io->socket,
277                                 canReadWrapper,
278                                 didWriteWrapper,
279                                 gotErrorWrapper,
280                                 io );
281
282    /* tell libevent to call didWriteWrapper after every write,
283     * not just when the write buffer is empty */
284    bufferevent_setwatermark( io->bufev, EV_WRITE, INT_MAX, 0 );
285
286    bufferevent_settimeout( io->bufev, io->timeout, io->timeout );
287
288    bufferevent_enable( io->bufev, EV_READ | EV_WRITE );
289}
290
291static tr_peerIo*
292tr_peerIoNew( tr_session *           session,
293              const struct in_addr * in_addr,
294              uint16_t               port,
295              const uint8_t *        torrentHash,
296              int                    isIncoming,
297              int                    socket )
298{
299    tr_peerIo * io;
300
301    if( socket >= 0 )
302        tr_netSetTOS( socket, session->peerSocketTOS );
303
304    io = tr_new0( tr_peerIo, 1 );
305    io->crypto = tr_cryptoNew( torrentHash, isIncoming );
306    io->session = session;
307    io->in_addr = *in_addr;
308    io->port = port;
309    io->socket = socket;
310    io->isIncoming = isIncoming != 0;
311    io->timeout = IO_TIMEOUT_SECS;
312    io->timeCreated = time( NULL );
313    io->output = evbuffer_new( );
314    io->bandwidth[TR_UP].isUnlimited = 1;
315    io->bandwidth[TR_DOWN].isUnlimited = 1;
316    io->speedometer[TR_UP] = tr_rcInit( );
317    io->speedometer[TR_DOWN] = tr_rcInit( );
318    bufevNew( io );
319    return io;
320}
321
322tr_peerIo*
323tr_peerIoNewIncoming( tr_session *           session,
324                      const struct in_addr * in_addr,
325                      uint16_t               port,
326                      int                    socket )
327{
328    assert( session );
329    assert( in_addr );
330    assert( socket >= 0 );
331
332    return tr_peerIoNew( session, in_addr, port,
333                         NULL, 1,
334                         socket );
335}
336
337tr_peerIo*
338tr_peerIoNewOutgoing( tr_session *           session,
339                      const struct in_addr * in_addr,
340                      int                    port,
341                      const uint8_t *        torrentHash )
342{
343    int socket;
344
345    assert( session );
346    assert( in_addr );
347    assert( port >= 0 );
348    assert( torrentHash );
349
350    socket = tr_netOpenTCP( in_addr, port );
351
352    return socket < 0
353           ? NULL
354           : tr_peerIoNew( session, in_addr, port, torrentHash, 0, socket );
355}
356
357static void
358io_dtor( void * vio )
359{
360    tr_peerIo * io = vio;
361
362    tr_rcClose( io->speedometer[TR_DOWN] );
363    tr_rcClose( io->speedometer[TR_UP] );
364    evbuffer_free( io->output );
365    bufferevent_free( io->bufev );
366    tr_netClose( io->socket );
367    tr_cryptoFree( io->crypto );
368    tr_free( io );
369}
370
371void
372tr_peerIoFree( tr_peerIo * io )
373{
374    if( io )
375    {
376        io->canRead = NULL;
377        io->didWrite = NULL;
378        io->gotError = NULL;
379        tr_runInEventThread( io->session, io_dtor, io );
380    }
381}
382
383tr_session*
384tr_peerIoGetSession( tr_peerIo * io )
385{
386    assert( io );
387    assert( io->session );
388
389    return io->session;
390}
391
392const struct in_addr*
393tr_peerIoGetAddress( const tr_peerIo * io,
394                           uint16_t * port )
395{
396    assert( io );
397
398    if( port )
399        *port = io->port;
400
401    return &io->in_addr;
402}
403
404const char*
405tr_peerIoAddrStr( const struct in_addr * addr,
406                  uint16_t               port )
407{
408    static char buf[512];
409
410    tr_snprintf( buf, sizeof( buf ), "%s:%u", inet_ntoa( *addr ),
411                ntohs( port ) );
412    return buf;
413}
414
415const char*
416tr_peerIoGetAddrStr( const tr_peerIo * io )
417{
418    return tr_peerIoAddrStr( &io->in_addr, io->port );
419}
420
421static void
422tr_peerIoTryRead( tr_peerIo * io )
423{
424    if( EVBUFFER_LENGTH( io->bufev->input ) )
425        canReadWrapper( io->bufev, io );
426}
427
428void
429tr_peerIoSetIOFuncs( tr_peerIo *     io,
430                     tr_can_read_cb  readcb,
431                     tr_did_write_cb writecb,
432                     tr_net_error_cb errcb,
433                     void *          userData )
434{
435    io->canRead = readcb;
436    io->didWrite = writecb;
437    io->gotError = errcb;
438    io->userData = userData;
439
440    tr_peerIoTryRead( io );
441}
442
443int
444tr_peerIoIsIncoming( const tr_peerIo * c )
445{
446    return c->isIncoming ? 1 : 0;
447}
448
449int
450tr_peerIoReconnect( tr_peerIo * io )
451{
452    assert( !tr_peerIoIsIncoming( io ) );
453
454    if( io->socket >= 0 )
455        tr_netClose( io->socket );
456
457    io->socket = tr_netOpenTCP( &io->in_addr, io->port );
458
459    if( io->socket >= 0 )
460    {
461        tr_netSetTOS( io->socket, io->session->peerSocketTOS );
462
463        bufferevent_free( io->bufev );
464        bufevNew( io );
465        return 0;
466    }
467
468    return -1;
469}
470
471void
472tr_peerIoSetTimeoutSecs( tr_peerIo * io,
473                         int         secs )
474{
475    io->timeout = secs;
476    bufferevent_settimeout( io->bufev, io->timeout, io->timeout );
477    bufferevent_enable( io->bufev, EV_READ | EV_WRITE );
478}
479
480/**
481***
482**/
483
484void
485tr_peerIoSetTorrentHash( tr_peerIo *     io,
486                         const uint8_t * hash )
487{
488    assert( io );
489
490    tr_cryptoSetTorrentHash( io->crypto, hash );
491}
492
493const uint8_t*
494tr_peerIoGetTorrentHash( tr_peerIo * io )
495{
496    assert( io );
497    assert( io->crypto );
498
499    return tr_cryptoGetTorrentHash( io->crypto );
500}
501
502int
503tr_peerIoHasTorrentHash( const tr_peerIo * io )
504{
505    assert( io );
506    assert( io->crypto );
507
508    return tr_cryptoHasTorrentHash( io->crypto );
509}
510
511/**
512***
513**/
514
515void
516tr_peerIoSetPeersId( tr_peerIo *     io,
517                     const uint8_t * peer_id )
518{
519    assert( io );
520
521    if( ( io->peerIdIsSet = peer_id != NULL ) )
522        memcpy( io->peerId, peer_id, 20 );
523    else
524        memset( io->peerId, 0, 20 );
525}
526
527const uint8_t*
528tr_peerIoGetPeersId( const tr_peerIo * io )
529{
530    assert( io );
531    assert( io->peerIdIsSet );
532
533    return io->peerId;
534}
535
536/**
537***
538**/
539
540void
541tr_peerIoEnableLTEP( tr_peerIo * io,
542                     int         flag )
543{
544    assert( io );
545    assert( flag == 0 || flag == 1 );
546
547    io->extendedProtocolSupported = flag;
548}
549
550void
551tr_peerIoEnableFEXT( tr_peerIo * io,
552                     int         flag )
553{
554    assert( io );
555    assert( flag == 0 || flag == 1 );
556
557    io->fastPeersSupported = flag;
558}
559
560int
561tr_peerIoSupportsLTEP( const tr_peerIo * io )
562{
563    assert( io );
564
565    return io->extendedProtocolSupported;
566}
567
568int
569tr_peerIoSupportsFEXT( const tr_peerIo * io )
570{
571    assert( io );
572
573    return io->fastPeersSupported;
574}
575
576/**
577***
578**/
579
580size_t
581tr_peerIoGetBandwidthUsed( const tr_peerIo * io,
582                           tr_direction      direction )
583{
584    assert( io );
585    assert( direction == TR_UP || direction == TR_DOWN );
586    return io->bandwidth[direction].bytesUsed;
587}
588
589size_t
590tr_peerIoGetWriteBufferSpace( const tr_peerIo * io )
591{
592    const size_t desiredBufferLen = 4096;
593    const size_t currentLiveLen = EVBUFFER_LENGTH( EVBUFFER_OUTPUT( io->bufev ) );
594
595    const size_t currentLbufLen = EVBUFFER_LENGTH( io->output );
596    const size_t desiredLiveLen = io->bandwidth[TR_UP].isUnlimited
597                                ? INT_MAX
598                                : io->bandwidth[TR_UP].bytesLeft;
599
600    const size_t currentLen = currentLiveLen + currentLbufLen;
601    const size_t desiredLen = desiredBufferLen + desiredLiveLen;
602
603    size_t       freeSpace = 0;
604
605    if( desiredLen > currentLen )
606        freeSpace = desiredLen - currentLen;
607    else
608        freeSpace = 0;
609
610    return freeSpace;
611}
612
613void
614tr_peerIoSetBandwidth( tr_peerIo *  io,
615                       tr_direction direction,
616                       size_t       bytesLeft )
617{
618    struct tr_bandwidth * b;
619
620    assert( io );
621    assert( direction == TR_UP || direction == TR_DOWN );
622
623    b = io->bandwidth + direction;
624    b->isUnlimited = 0;
625    b->bytesUsed = 0;
626    b->bytesLeft = bytesLeft;
627
628    adjustOutputBuffer( io );
629    adjustInputBuffer( io );
630}
631
632void
633tr_peerIoSetBandwidthUnlimited( tr_peerIo *  io,
634                                tr_direction direction )
635{
636    struct tr_bandwidth * b;
637
638    assert( io );
639    assert( direction == TR_UP || direction == TR_DOWN );
640
641    b = io->bandwidth + direction;
642    b->isUnlimited = 1;
643    b->bytesUsed = 0;
644    b->bytesLeft = 0;
645
646    adjustInputBuffer( io );
647    adjustOutputBuffer( io );
648}
649
650double
651tr_peerIoGetRateToClient( const tr_peerIo * io )
652{
653    return tr_rcRate( io->speedometer[TR_DOWN] );
654}
655
656double
657tr_peerIoGetRateToPeer( const tr_peerIo * io )
658{
659    return tr_rcRate( io->speedometer[TR_UP] );
660}
661
662/**
663***
664**/
665
666tr_crypto*
667tr_peerIoGetCrypto( tr_peerIo * c )
668{
669    return c->crypto;
670}
671
672void
673tr_peerIoSetEncryption( tr_peerIo * io,
674                        int         encryptionMode )
675{
676    assert( io );
677    assert( encryptionMode == PEER_ENCRYPTION_NONE
678          || encryptionMode == PEER_ENCRYPTION_RC4 );
679
680    io->encryptionMode = encryptionMode;
681}
682
683int
684tr_peerIoIsEncrypted( const tr_peerIo * io )
685{
686    return io != NULL && io->encryptionMode == PEER_ENCRYPTION_RC4;
687}
688
689/**
690***
691**/
692
693int
694tr_peerIoWantsBandwidth( const tr_peerIo * io,
695                         tr_direction      direction )
696{
697    assert( direction == TR_UP || direction == TR_DOWN );
698
699    if( direction == TR_DOWN )
700    {
701        return TRUE; /* FIXME -- is there a good way to test for this? */
702    }
703    else
704    {
705        return EVBUFFER_LENGTH( EVBUFFER_OUTPUT( io->bufev ) )
706               || EVBUFFER_LENGTH( io->output );
707    }
708}
709
710void
711tr_peerIoWrite( tr_peerIo *  io,
712                const void * writeme,
713                size_t       writemeLen )
714{
715    assert( tr_amInEventThread( io->session ) );
716    dbgmsg( io, "adding %zu bytes into io->output", writemeLen );
717
718    if( io->bandwidth[TR_UP].isUnlimited )
719        bufferevent_write( io->bufev, writeme, writemeLen );
720    else
721        evbuffer_add( io->output, writeme, writemeLen );
722
723    adjustOutputBuffer( io );
724}
725
726void
727tr_peerIoWriteBuf( tr_peerIo *       io,
728                   struct evbuffer * buf )
729{
730    const size_t n = EVBUFFER_LENGTH( buf );
731
732    tr_peerIoWrite( io, EVBUFFER_DATA( buf ), n );
733    evbuffer_drain( buf, n );
734}
735
736/**
737***
738**/
739
740void
741tr_peerIoWriteBytes( tr_peerIo *       io,
742                     struct evbuffer * outbuf,
743                     const void *      bytes,
744                     size_t            byteCount )
745{
746    uint8_t * tmp;
747
748    switch( io->encryptionMode )
749    {
750        case PEER_ENCRYPTION_NONE:
751            evbuffer_add( outbuf, bytes, byteCount );
752            break;
753
754        case PEER_ENCRYPTION_RC4:
755            tmp = tr_new( uint8_t, byteCount );
756            tr_cryptoEncrypt( io->crypto, byteCount, bytes, tmp );
757            evbuffer_add( outbuf, tmp, byteCount );
758            tr_free( tmp );
759            break;
760
761        default:
762            assert( 0 );
763    }
764}
765
766void
767tr_peerIoWriteUint8( tr_peerIo *       io,
768                     struct evbuffer * outbuf,
769                     uint8_t           writeme )
770{
771    tr_peerIoWriteBytes( io, outbuf, &writeme, sizeof( uint8_t ) );
772}
773
774void
775tr_peerIoWriteUint16( tr_peerIo *       io,
776                      struct evbuffer * outbuf,
777                      uint16_t          writeme )
778{
779    uint16_t tmp = htons( writeme );
780
781    tr_peerIoWriteBytes( io, outbuf, &tmp, sizeof( uint16_t ) );
782}
783
784void
785tr_peerIoWriteUint32( tr_peerIo *       io,
786                      struct evbuffer * outbuf,
787                      uint32_t          writeme )
788{
789    uint32_t tmp = htonl( writeme );
790
791    tr_peerIoWriteBytes( io, outbuf, &tmp, sizeof( uint32_t ) );
792}
793
794/***
795****
796***/
797
798void
799tr_peerIoReadBytes( tr_peerIo *       io,
800                    struct evbuffer * inbuf,
801                    void *            bytes,
802                    size_t            byteCount )
803{
804    assert( EVBUFFER_LENGTH( inbuf ) >= byteCount );
805
806    switch( io->encryptionMode )
807    {
808        case PEER_ENCRYPTION_NONE:
809            evbuffer_remove( inbuf, bytes, byteCount );
810            break;
811
812        case PEER_ENCRYPTION_RC4:
813            evbuffer_remove( inbuf, bytes, byteCount );
814            tr_cryptoDecrypt( io->crypto, byteCount, bytes, bytes );
815            break;
816
817        default:
818            assert( 0 );
819    }
820}
821
822void
823tr_peerIoReadUint8( tr_peerIo *       io,
824                    struct evbuffer * inbuf,
825                    uint8_t *         setme )
826{
827    tr_peerIoReadBytes( io, inbuf, setme, sizeof( uint8_t ) );
828}
829
830void
831tr_peerIoReadUint16( tr_peerIo *       io,
832                     struct evbuffer * inbuf,
833                     uint16_t *        setme )
834{
835    uint16_t tmp;
836
837    tr_peerIoReadBytes( io, inbuf, &tmp, sizeof( uint16_t ) );
838    *setme = ntohs( tmp );
839}
840
841void
842tr_peerIoReadUint32( tr_peerIo *       io,
843                     struct evbuffer * inbuf,
844                     uint32_t *        setme )
845{
846    uint32_t tmp;
847
848    tr_peerIoReadBytes( io, inbuf, &tmp, sizeof( uint32_t ) );
849    *setme = ntohl( tmp );
850}
851
852void
853tr_peerIoDrain( tr_peerIo *       io,
854                struct evbuffer * inbuf,
855                size_t            byteCount )
856{
857    uint8_t * tmp = tr_new( uint8_t, byteCount );
858
859    tr_peerIoReadBytes( io, inbuf, tmp, byteCount );
860    tr_free( tmp );
861}
862
863int
864tr_peerIoGetAge( const tr_peerIo * io )
865{
866    return time( NULL ) - io->timeCreated;
867}
868
Note: See TracBrowser for help on using the repository browser.