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

Last change on this file since 7419 was 7419, checked in by charles, 12 years ago

(trunk libT) really fuck up the peer i/o code. also this breaks the mac build until someone removes iobuf.c from libtransmission's list of files.

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