static void didWriteWrapper( tr_peerIo * io, unsigned int bytes_transferred ) { while( bytes_transferred && tr_isPeerIo( io ) ) { struct tr_datatype * next = io->outbuf_datatypes->data; const unsigned int payload = MIN( next->length, bytes_transferred ); const unsigned int overhead = guessPacketOverhead( payload ); const uint64_t now = tr_time_msec( ); tr_bandwidthUsed( &io->bandwidth, TR_UP, payload, next->isPieceData, now ); if( overhead > 0 ) tr_bandwidthUsed( &io->bandwidth, TR_UP, overhead, FALSE, now ); if( io->didWrite ) io->didWrite( io, payload, next->isPieceData, io->userData ); if( tr_isPeerIo( io ) ) { bytes_transferred -= payload; next->length -= payload; if( !next->length ) { tr_list_pop_front( &io->outbuf_datatypes ); tr_free( next ); } } } }
void tr_peerIoReadBytes( tr_peerIo * io, struct evbuffer * inbuf, void * bytes, size_t byteCount ) { assert( tr_isPeerIo( io ) ); /* FIXME(libevent2): use evbuffer_get_length() */ assert( EVBUFFER_LENGTH( inbuf ) >= byteCount ); switch( io->encryptionMode ) { case PEER_ENCRYPTION_NONE: evbuffer_remove( inbuf, bytes, byteCount ); break; case PEER_ENCRYPTION_RC4: /* FIXME(libevent2): loop through calls to evbuffer_get_contiguous_space() + evbuffer_drain() */ tr_cryptoDecrypt( io->crypto, byteCount, EVBUFFER_DATA(inbuf), bytes ); evbuffer_drain(inbuf, byteCount ); break; default: assert( 0 ); } }
int tr_peerIoReconnect( tr_peerIo * io ) { short int pendingEvents; tr_session * session; assert( tr_isPeerIo( io ) ); assert( !tr_peerIoIsIncoming( io ) ); session = tr_peerIoGetSession( io ); pendingEvents = io->pendingEvents; event_disable( io, EV_READ | EV_WRITE ); if( io->socket >= 0 ) tr_netClose( session, io->socket ); io->socket = tr_netOpenPeerSocket( session, &io->addr, io->port, io->isSeed ); event_set( &io->event_read, io->socket, EV_READ, event_read_cb, io ); event_set( &io->event_write, io->socket, EV_WRITE, event_write_cb, io ); if( io->socket >= 0 ) { event_enable( io, pendingEvents ); tr_netSetTOS( io->socket, session->peerSocketTOS ); maybeSetCongestionAlgorithm( io->socket, session->peer_congestion_algorithm ); return 0; } return -1; }
void tr_peerIoSetTorrentHash( tr_peerIo * io, const uint8_t * hash ) { assert( tr_isPeerIo( io ) ); tr_cryptoSetTorrentHash( io->crypto, hash ); }
void tr_bandwidthSetPeer( tr_bandwidth * b, tr_peerIo * peer ) { assert( tr_isBandwidth( b ) ); assert( ( peer == NULL ) || tr_isPeerIo( peer ) ); b->peer = peer; }
int tr_peerIoHasTorrentHash( const tr_peerIo * io ) { assert( tr_isPeerIo( io ) ); assert( io->crypto ); return tr_cryptoHasTorrentHash( io->crypto ); }
const uint8_t* tr_peerIoGetTorrentHash( tr_peerIo * io ) { assert( tr_isPeerIo( io ) ); assert( io->crypto ); return tr_cryptoGetTorrentHash( io->crypto ); }
void tr_bandwidthRemovePeer( tr_bandwidth * b, tr_peerIo * peerIo ) { assert( tr_isBandwidth( b ) ); assert( tr_isPeerIo( peerIo ) ); tr_ptrArrayRemoveSorted( b->peers, peerIo, comparePointers ); }
void tr_peerIoSetEncryption( tr_peerIo * io, uint32_t encryptionMode ) { assert( tr_isPeerIo( io ) ); assert( encryptionMode == PEER_ENCRYPTION_NONE || encryptionMode == PEER_ENCRYPTION_RC4 ); io->encryptionMode = encryptionMode; }
const tr_address* tr_peerIoGetAddress( const tr_peerIo * io, tr_port * port ) { assert( tr_isPeerIo( io ) ); if( port ) *port = io->port; return &io->addr; }
void tr_peerIoRefImpl( const char * file, int line, tr_peerIo * io ) { assert( tr_isPeerIo( io ) ); dbgmsg( io, "%s:%d is incrementing the IO's refcount from %d to %d", file, line, io->refCount, io->refCount+1 ); ++io->refCount; }
void tr_peerIoSetPeersId( tr_peerIo * io, const uint8_t * peer_id ) { assert( tr_isPeerIo( io ) ); if( ( io->peerIdIsSet = peer_id != NULL ) ) memcpy( io->peerId, peer_id, 20 ); else memset( io->peerId, 0, 20 ); }
void tr_peerIoUnrefImpl( const char * file, int line, tr_peerIo * io ) { assert( tr_isPeerIo( io ) ); dbgmsg( io, "%s:%d is decrementing the IO's refcount from %d to %d", file, line, io->refCount, io->refCount-1 ); if( !--io->refCount ) tr_peerIoFree( io ); }
void tr_peerIoSetEnabled( tr_peerIo * io, tr_direction dir, tr_bool isEnabled ) { const short event = dir == TR_UP ? EV_WRITE : EV_READ; assert( tr_isPeerIo( io ) ); assert( tr_isDirection( dir ) ); assert( tr_amInEventThread( io->session ) ); assert( io->session->events != NULL ); if( isEnabled ) event_enable( io, event ); else event_disable( io, event ); }
int tr_peerIoFlush( tr_peerIo * io, tr_direction dir, size_t limit ) { int bytesUsed = 0; assert( tr_isPeerIo( io ) ); assert( tr_isDirection( dir ) ); if( io->hasFinishedConnecting ) { if( dir == TR_DOWN ) bytesUsed = tr_peerIoTryRead( io, limit ); else bytesUsed = tr_peerIoTryWrite( io, limit ); } dbgmsg( io, "flushing peer-io, hasFinishedConnecting %d, direction %d, limit %zu, bytesUsed %d", (int)io->hasFinishedConnecting, (int)dir, limit, bytesUsed ); return bytesUsed; }
static void io_dtor( void * vio ) { tr_peerIo * io = vio; assert( tr_isPeerIo( io ) ); assert( tr_amInEventThread( io->session ) ); assert( io->session->events != NULL ); dbgmsg( io, "in tr_peerIo destructor" ); event_disable( io, EV_READ | EV_WRITE ); tr_bandwidthDestruct( &io->bandwidth ); evbuffer_free( io->outbuf ); evbuffer_free( io->inbuf ); tr_netClose( io->session, io->socket ); tr_cryptoFree( io->crypto ); tr_list_free( &io->outbuf_datatypes, tr_free ); memset( io, ~0, sizeof( tr_peerIo ) ); tr_free( io ); }
static void canReadWrapper( tr_peerIo * io ) { tr_bool err = 0; tr_bool done = 0; tr_session * session; dbgmsg( io, "canRead" ); assert( tr_isPeerIo( io ) ); assert( tr_isSession( io->session ) ); tr_peerIoRef( io ); session = io->session; /* try to consume the input buffer */ if( io->canRead ) { tr_sessionLock( session ); while( !done && !err ) { size_t piece = 0; const size_t oldLen = EVBUFFER_LENGTH( io->inbuf ); const int ret = io->canRead( io, io->userData, &piece ); const size_t used = oldLen - EVBUFFER_LENGTH( io->inbuf ); assert( tr_isPeerIo( io ) ); if( piece || (piece!=used) ) { const uint64_t now = tr_time_msec( ); if( piece ) tr_bandwidthUsed( &io->bandwidth, TR_DOWN, piece, TRUE, now ); if( used != piece ) tr_bandwidthUsed( &io->bandwidth, TR_DOWN, used - piece, FALSE, now ); } switch( ret ) { case READ_NOW: if( EVBUFFER_LENGTH( io->inbuf ) ) continue; done = 1; break; case READ_LATER: done = 1; break; case READ_ERR: err = 1; break; } assert( tr_isPeerIo( io ) ); } tr_sessionUnlock( session ); } /* keep the iobuf's excess capacity from growing too large */ if( EVBUFFER_LENGTH( io->inbuf ) == 0 ) { evbuffer_free( io->inbuf ); io->inbuf = evbuffer_new( ); } assert( tr_isPeerIo( io ) ); tr_peerIoUnref( io ); }
static ReadState canRead( struct tr_peerIo * io, void * arg, size_t * piece ) { tr_handshake * handshake = arg; struct evbuffer * inbuf = tr_peerIoGetReadBuffer( io ); ReadState ret; tr_bool readyForMore = TRUE; assert( tr_isPeerIo( io ) ); /* no piece data in handshake */ *piece = 0; dbgmsg( handshake, "handling canRead; state is [%s]", getStateName( handshake->state ) ); while( readyForMore ) { switch( handshake->state ) { case AWAITING_HANDSHAKE: ret = readHandshake ( handshake, inbuf ); break; case AWAITING_PEER_ID: ret = readPeerId ( handshake, inbuf ); break; case AWAITING_YA: ret = readYa ( handshake, inbuf ); break; case AWAITING_PAD_A: ret = readPadA ( handshake, inbuf ); break; case AWAITING_CRYPTO_PROVIDE: ret = readCryptoProvide( handshake, inbuf ); break; case AWAITING_PAD_C: ret = readPadC ( handshake, inbuf ); break; case AWAITING_IA: ret = readIA ( handshake, inbuf ); break; case AWAITING_PAYLOAD_STREAM: ret = readPayloadStream( handshake, inbuf ); break; case AWAITING_YB: ret = readYb ( handshake, inbuf ); break; case AWAITING_VC: ret = readVC ( handshake, inbuf ); break; case AWAITING_CRYPTO_SELECT: ret = readCryptoSelect ( handshake, inbuf ); break; case AWAITING_PAD_D: ret = readPadD ( handshake, inbuf ); break; default: assert( 0 ); } if( ret != READ_NOW ) readyForMore = FALSE; else if( handshake->state == AWAITING_PAD_C ) readyForMore = evbuffer_get_length( inbuf ) >= handshake->pad_c_len; else if( handshake->state == AWAITING_PAD_D ) readyForMore = evbuffer_get_length( inbuf ) >= handshake->pad_d_len; else if( handshake->state == AWAITING_IA ) readyForMore = evbuffer_get_length( inbuf ) >= handshake->ia_len; } return ret; }
static void event_write_cb( int fd, short event UNUSED, void * vio ) { int res = 0; int e; short what = EVBUFFER_WRITE; tr_peerIo * io = vio; size_t howmuch; const tr_direction dir = TR_UP; assert( tr_isPeerIo( io ) ); io->hasFinishedConnecting = TRUE; io->pendingEvents &= ~EV_WRITE; dbgmsg( io, "libevent says this peer is ready to write" ); /* Write as much as possible, since the socket is non-blocking, write() will * return if it can't write any more data without blocking */ howmuch = tr_bandwidthClamp( &io->bandwidth, dir, EVBUFFER_LENGTH( io->outbuf ) ); /* if we don't have any bandwidth left, stop writing */ if( howmuch < 1 ) { tr_peerIoSetEnabled( io, dir, FALSE ); return; } EVUTIL_SET_SOCKET_ERROR( 0 ); res = tr_evbuffer_write( io, fd, howmuch ); e = EVUTIL_SOCKET_ERROR( ); if (res == -1) { if (e == EAGAIN || e == EINTR || e == EINPROGRESS) goto reschedule; /* error case */ what |= EVBUFFER_ERROR; } else if (res == 0) { /* eof case */ what |= EVBUFFER_EOF; } if (res <= 0) goto error; if( EVBUFFER_LENGTH( io->outbuf ) ) tr_peerIoSetEnabled( io, dir, TRUE ); didWriteWrapper( io, res ); return; reschedule: if( EVBUFFER_LENGTH( io->outbuf ) ) tr_peerIoSetEnabled( io, dir, TRUE ); return; error: dbgmsg( io, "event_write_cb got an error. res is %d, what is %hd, errno is %d (%s)", res, what, e, strerror( e ) ); if( io->gotError != NULL ) io->gotError( io, what, io->userData ); }
static void event_read_cb( int fd, short event UNUSED, void * vio ) { int res; int e; tr_peerIo * io = vio; /* Limit the input buffer to 256K, so it doesn't grow too large */ unsigned int howmuch; unsigned int curlen; const tr_direction dir = TR_DOWN; const unsigned int max = 256 * 1024; assert( tr_isPeerIo( io ) ); io->hasFinishedConnecting = TRUE; io->pendingEvents &= ~EV_READ; curlen = EVBUFFER_LENGTH( io->inbuf ); howmuch = curlen >= max ? 0 : max - curlen; howmuch = tr_bandwidthClamp( &io->bandwidth, TR_DOWN, howmuch ); dbgmsg( io, "libevent says this peer is ready to read" ); /* if we don't have any bandwidth left, stop reading */ if( howmuch < 1 ) { tr_peerIoSetEnabled( io, dir, FALSE ); return; } EVUTIL_SET_SOCKET_ERROR( 0 ); res = evbuffer_read( io->inbuf, fd, (int)howmuch ); e = EVUTIL_SOCKET_ERROR( ); if( res > 0 ) { tr_peerIoSetEnabled( io, dir, TRUE ); /* Invoke the user callback - must always be called last */ canReadWrapper( io ); } else { char errstr[512]; short what = EVBUFFER_READ; if( res == 0 ) /* EOF */ what |= EVBUFFER_EOF; else if( res == -1 ) { if( e == EAGAIN || e == EINTR ) { tr_peerIoSetEnabled( io, dir, TRUE ); return; } what |= EVBUFFER_ERROR; } tr_net_strerror( errstr, sizeof( errstr ), e ); dbgmsg( io, "event_read_cb got an error. res is %d, what is %hd, errno is %d (%s)", res, what, e, errstr ); if( io->gotError != NULL ) io->gotError( io, what, io->userData ); } }
const char* tr_peerIoGetAddrStr( const tr_peerIo * io ) { return tr_isPeerIo( io ) ? tr_peerIoAddrStr( &io->addr, io->port ) : "error"; }