(trunk libT) #2035: Transmission causes wakeups by unnecessary polling.

This commit is contained in:
Charles Kerr 2009-05-14 13:42:29 +00:00
parent 3973659162
commit da42fe8a7c
7 changed files with 314 additions and 518 deletions

View File

@ -38,6 +38,7 @@
#include <netdb.h>
#include <fcntl.h>
#endif
#include <unistd.h>
#include <evutil.h>
@ -47,6 +48,7 @@
#include "net.h"
#include "peer-io.h"
#include "platform.h"
#include "session.h"
#include "utils.h"
#ifndef IN_MULTICAST
@ -54,31 +56,30 @@
#endif
const tr_address tr_in6addr_any = { TR_AF_INET6, { IN6ADDR_ANY_INIT } };
const tr_address tr_inaddr_any = { TR_AF_INET,
{ { { { INADDR_ANY, 0x00, 0x00, 0x00 } } } } };
const tr_address tr_inaddr_any = { TR_AF_INET, { { { { INADDR_ANY, 0x00, 0x00, 0x00 } } } } };
#ifdef WIN32
static const char *
inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
inet_ntop( int af, const void *src, char *dst, socklen_t cnt )
{
if (af == AF_INET)
{
struct sockaddr_in in;
memset(&in, 0, sizeof(in));
memset( &in, 0, sizeof( in ) );
in.sin_family = AF_INET;
memcpy(&in.sin_addr, src, sizeof(struct in_addr));
getnameinfo((struct sockaddr *)&in, sizeof(struct
sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
memcpy( &in.sin_addr, src, sizeof( struct in_addr ) );
getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in),
dst, cnt, NULL, 0, NI_NUMERICHOST);
return dst;
}
else if (af == AF_INET6)
{
struct sockaddr_in6 in;
memset(&in, 0, sizeof(in));
memset( &in, 0, sizeof( in ) );
in.sin6_family = AF_INET6;
memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
getnameinfo((struct sockaddr *)&in, sizeof(struct
sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
memcpy( &in.sin6_addr, src, sizeof( struct in_addr6 ) );
getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6),
dst, cnt, NULL, 0, NI_NUMERICHOST);
return dst;
}
return NULL;
@ -196,140 +197,22 @@ tr_compareAddresses( const tr_address * a, const tr_address * b)
tr_bool
tr_net_hasIPv6( tr_port port )
{
static tr_bool result = FALSE;
static tr_bool alreadyDone = FALSE;
static tr_bool result = FALSE;
int s;
if( alreadyDone )
return result;
s = tr_netBindTCP( &tr_in6addr_any, port, TRUE );
if( s >= 0 || -s != EAFNOSUPPORT ) /* we support ipv6 */
if( !alreadyDone )
{
result = TRUE;
tr_netClose( s );
int fd = tr_netBindTCP( &tr_in6addr_any, port, TRUE );
if( fd >= 0 || -fd != EAFNOSUPPORT ) /* we support ipv6 */
result = TRUE;
if( fd >= 0 )
EVUTIL_CLOSESOCKET( fd );
alreadyDone = TRUE;
}
alreadyDone = TRUE;
return result;
}
/***********************************************************************
* Socket list housekeeping
**********************************************************************/
struct tr_socketList
{
int socket;
tr_address addr;
tr_socketList * next;
};
tr_socketList *
tr_socketListAppend( tr_socketList * const head,
const tr_address * const addr )
{
tr_socketList * tmp;
assert( head );
assert( tr_isAddress( addr ) );
for( tmp = head; tmp->next; tmp = tmp->next );
tmp->next = tr_socketListNew( addr );
return tmp->next;
}
tr_socketList *
tr_socketListNew( const tr_address * const addr )
{
tr_socketList * tmp;
assert( tr_isAddress( addr ) );
tmp = tr_new( tr_socketList, 1 );
tmp->socket = -1;
tmp->addr = *addr;
tmp->next = NULL;
return tmp;
}
void
tr_socketListFree( tr_socketList * const head )
{
assert( head );
if( head->next )
tr_socketListFree( head->next );
tr_free( head );
}
void
tr_socketListRemove( tr_socketList * const head,
tr_socketList * const el)
{
tr_socketList * tmp;
assert( head );
assert( el );
for( tmp = head; tmp->next && tmp->next != el; tmp = tmp->next );
tmp->next = el->next;
el->next = NULL;
tr_socketListFree(el);
}
void
tr_socketListTruncate( tr_socketList * const head,
tr_socketList * const start )
{
tr_socketList * tmp;
assert( head );
assert( start );
for( tmp = head; tmp->next && tmp->next != start; tmp = tmp->next );
tr_socketListFree( start );
tmp->next = NULL;
}
#if 0
int
tr_socketListGetSocket( const tr_socketList * const el )
{
assert( el );
return el->socket;
}
const tr_address *
tr_socketListGetAddress( const tr_socketList * const el )
{
assert( el );
return &el->addr;
}
#endif
void
tr_socketListForEach( tr_socketList * const head,
void ( * cb ) ( int * const,
tr_address * const,
void * const),
void * const userData )
{
tr_socketList * tmp;
for( tmp = head; tmp; tmp = tmp->next )
cb( &tmp->socket, &tmp->addr, userData );
}
const tr_address *
tr_socketListGetType( const tr_socketList * const el, tr_address_type type )
{
const tr_socketList * tmp = el;
while( tmp )
{
if( tmp->addr.type == type )
return &tmp->addr;
tmp = tmp->next;
}
return NULL;
}
/***********************************************************************
* TCP sockets
**********************************************************************/
@ -344,57 +227,16 @@ tr_netSetTOS( int s, int tos )
#endif
}
static int
makeSocketNonBlocking( int fd )
{
if( fd >= 0 )
{
if( evutil_make_socket_nonblocking( fd ) )
{
int tmperrno;
tr_err( _( "Couldn't create socket: %s" ),
tr_strerror( sockerrno ) );
tmperrno = sockerrno;
tr_netClose( fd );
fd = -tmperrno;
}
}
return fd;
}
static int
createSocket( int domain, int type )
{
return makeSocketNonBlocking( tr_fdSocketCreate( domain, type ) );
}
static void
setSndBuf( tr_session * session UNUSED, int fd UNUSED )
{
#if 0
if( fd >= 0 )
{
const int sndbuf = session->so_sndbuf;
const int rcvbuf = session->so_rcvbuf;
setsockopt( fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof( sndbuf ) );
setsockopt( fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof( rcvbuf ) );
}
#endif
}
static socklen_t
setup_sockaddr( const tr_address * addr,
tr_port port,
struct sockaddr_storage * sockaddr)
{
struct sockaddr_in sock4;
struct sockaddr_in6 sock6;
assert( tr_isAddress( addr ) );
if( addr->type == TR_AF_INET )
{
struct sockaddr_in sock4;
memset( &sock4, 0, sizeof( sock4 ) );
sock4.sin_family = AF_INET;
sock4.sin_addr.s_addr = addr->addr.addr4.s_addr;
@ -404,11 +246,12 @@ setup_sockaddr( const tr_address * addr,
}
else
{
struct sockaddr_in6 sock6;
memset( &sock6, 0, sizeof( sock6 ) );
sock6.sin6_family = AF_INET6;
sock6.sin6_port = port;
sock6.sin6_family = AF_INET6;
sock6.sin6_port = port;
sock6.sin6_flowinfo = 0;
sock6.sin6_addr = addr->addr.addr6;
sock6.sin6_addr = addr->addr.addr6;
memcpy( sockaddr, &sock6, sizeof( sock6 ) );
return sizeof( struct sockaddr_in6 );
}
@ -460,16 +303,14 @@ tr_isValidPeerAddress( const tr_address * addr, tr_port port )
return TRUE;
}
const tr_socketList * tr_getSessionBindSockets( const tr_session * session );
int
tr_netOpenTCP( tr_session * session,
const tr_address * addr,
tr_port port )
{
static const int domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
int s;
struct sockaddr_storage sock;
const int type = SOCK_STREAM;
socklen_t addrlen;
const tr_address * source_addr;
socklen_t sourcelen;
@ -480,16 +321,19 @@ tr_netOpenTCP( tr_session * session,
if( isMulticastAddress( addr ) || isIPv6LinkLocalAddress( addr ) )
return -EINVAL;
if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ), type ) ) < 0 )
return s;
s = tr_fdSocketCreate( domains[addr->type], SOCK_STREAM );
if( s < 0 )
return -1;
setSndBuf( session, s );
if( evutil_make_socket_nonblocking( s ) < 0 ) {
EVUTIL_CLOSESOCKET( s );
return -1;
}
addrlen = setup_sockaddr( addr, port, &sock );
/* set source address */
source_addr = tr_socketListGetType( tr_getSessionBindSockets( session ),
addr->type );
source_addr = tr_sessionGetPublicAddress( session, addr->type );
assert( source_addr );
sourcelen = setup_sockaddr( source_addr, 0, &source_sock );
if( bind( s, ( struct sockaddr * ) &source_sock, sourcelen ) )
@ -526,64 +370,68 @@ tr_netOpenTCP( tr_session * session,
int
tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs )
{
int s;
static const int domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
struct sockaddr_storage sock;
const int type = SOCK_STREAM;
int addrlen;
#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT ) || defined( IPV6_V6ONLY )
int optval = 1;
#endif
int fd;
int addrlen;
int optval;
assert( tr_isAddress( addr ) );
if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ),
type ) ) < 0 )
return s;
fd = socket( domains[addr->type], SOCK_STREAM, 0 );
if( fd < 0 )
return -1;
#ifdef SO_REUSEADDR
setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof( optval ) );
#endif
if( evutil_make_socket_nonblocking( fd ) < 0 ) {
EVUTIL_CLOSESOCKET( fd );
return -1;
}
optval = 1;
setsockopt( fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval) );
setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) );
#ifdef IPV6_V6ONLY
if( addr->type == TR_AF_INET6 &&
setsockopt( s, IPPROTO_IPV6, IPV6_V6ONLY, &optval,
sizeof( optval ) ) == -1 ) {
/* the kernel may not support this. if not, ignore it */
if( errno != ENOPROTOOPT )
return -errno;
}
if( addr->type == TR_AF_INET6 )
if( setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof( optval ) ) == -1 )
if( EVUTIL_SOCKET_ERROR( ) != ENOPROTOOPT ) /* if the kernel doesn't support it, ignore it */
return -EVUTIL_SOCKET_ERROR( );
#endif
addrlen = setup_sockaddr( addr, htons( port ), &sock );
if( bind( s, (struct sockaddr *) &sock,
addrlen ) )
{
int tmperrno;
if( bind( fd, (struct sockaddr *) &sock, addrlen ) ) {
const int err = EVUTIL_SOCKET_ERROR( );
if( !suppressMsgs )
tr_err( _( "Couldn't bind port %d on %s: %s" ), port,
tr_ntop_non_ts( addr ), tr_strerror( sockerrno ) );
tmperrno = sockerrno;
tr_netClose( s );
return -tmperrno;
tr_err( _( "Couldn't bind port %d on %s: %s" ),
port, tr_ntop_non_ts( addr ), tr_strerror( err ) );
tr_netClose( fd );
return -err;
}
if( !suppressMsgs )
tr_dbg( "Bound socket %d to port %d on %s",
s, port, tr_ntop_non_ts( addr ) );
return s;
tr_dbg( "Bound socket %d to port %d on %s", fd, port, tr_ntop_non_ts( addr ) );
if( listen( fd, 128 ) == -1 ) {
EVUTIL_CLOSESOCKET( fd );
return -EVUTIL_SOCKET_ERROR( );
}
return fd;
}
int
tr_netAccept( tr_session * session,
tr_netAccept( tr_session * session UNUSED,
int b,
tr_address * addr,
tr_port * port )
{
int fd;
int fd = tr_fdSocketAccept( b, addr, port );
if( fd>=0 && evutil_make_socket_nonblocking(fd)<0 ) {
EVUTIL_CLOSESOCKET( fd );
fd = -1;
}
fd = makeSocketNonBlocking( tr_fdSocketAccept( b, addr, port ) );
setSndBuf( session, fd );
return fd;
}

View File

@ -61,8 +61,10 @@ struct tr_session;
typedef enum tr_address_type
{
TR_AF_INET,
TR_AF_INET6
} tr_address_type;
TR_AF_INET6,
NUM_TR_AF_INET_TYPES
}
tr_address_type;
typedef struct tr_address
{
@ -93,28 +95,6 @@ static TR_INLINE tr_bool tr_isAddress( const tr_address * a ) { return ( a != NU
tr_bool tr_net_hasIPv6( tr_port );
/***********************************************************************
* Socket list housekeeping
**********************************************************************/
typedef struct tr_socketList tr_socketList;
tr_socketList *tr_socketListAppend( tr_socketList * const head,
const tr_address * const addr );
tr_socketList *tr_socketListNew( const tr_address * const addr );
void tr_socketListFree( tr_socketList * const head );
void tr_socketListRemove( tr_socketList * const head,
tr_socketList * const el);
void tr_socketListTruncate( tr_socketList * const head,
tr_socketList * const start );
int tr_socketListGetSocket( const tr_socketList * const el );
const tr_address *tr_socketListGetAddress( const tr_socketList * const el );
void tr_socketListForEach( tr_socketList * const head,
void ( * cb ) ( int * const,
tr_address * const,
void * const ),
void * const userData);
const tr_address *tr_socketListGetType( const tr_socketList * const el,
tr_address_type type );
/***********************************************************************
* Sockets
**********************************************************************/

View File

@ -35,20 +35,16 @@ struct tr_shared
{
tr_bool isEnabled;
tr_bool isShuttingDown;
tr_bool doPortCheck;
tr_port_forwarding natpmpStatus;
tr_port_forwarding upnpStatus;
tr_bool shouldChange;
tr_socketList * bindSockets;
tr_port publicPort;
tr_timer * pulseTimer;
tr_timer * recheckTimer;
tr_upnp * upnp;
tr_natpmp * natpmp;
tr_session * session;
struct event * timer;
};
/***
@ -60,36 +56,21 @@ getNatStateStr( int state )
{
switch( state )
{
/* we're in the process of trying to set up port forwarding */
case TR_PORT_MAPPING:
return _( "Starting" );
/* we've successfully forwarded the port */
case TR_PORT_MAPPED:
return _( "Forwarded" );
/* we're cancelling the port forwarding */
case TR_PORT_UNMAPPING:
return _( "Stopping" );
/* the port isn't forwarded */
case TR_PORT_UNMAPPED:
return _( "Not forwarded" );
case TR_PORT_ERROR:
return "???";
case TR_PORT_MAPPING: return _( "Starting" );
case TR_PORT_MAPPED: return _( "Forwarded" );
case TR_PORT_UNMAPPING: return _( "Stopping" );
case TR_PORT_UNMAPPED: return _( "Not forwarded" );
default: return "???";
}
return "notfound";
}
static void
natPulse( tr_shared * s, tr_bool doPortCheck )
{
const tr_port port = s->publicPort;
const int isEnabled = s->isEnabled && !s->isShuttingDown;
int oldStatus;
int newStatus;
const tr_port port = s->session->peerPort;
const int isEnabled = s->isEnabled && !s->isShuttingDown;
int oldStatus;
int newStatus;
oldStatus = tr_sharedTraversalStatus( s );
s->natpmpStatus = tr_natpmpPulse( s->natpmp, port, isEnabled );
@ -102,187 +83,125 @@ natPulse( tr_shared * s, tr_bool doPortCheck )
getNatStateStr( newStatus ) );
}
/*
* Callbacks for socket list
*/
static void
closeCb( int * const socket,
tr_address * const addr,
void * const userData )
onTimer( int fd UNUSED, short what UNUSED, void * vshared )
{
tr_shared * s = ( tr_shared * )userData;
if( *socket >= 0 )
{
tr_ninf( getKey( ), _( "Closing port %d on %s" ), s->publicPort,
tr_ntop_non_ts( addr ) );
tr_netClose( *socket );
}
}
tr_shared * s = vshared;
struct timeval interval;
static void
acceptCb( int * const socket,
tr_address * const addr UNUSED,
void * const userData )
{
tr_shared * s = ( tr_shared * )userData;
tr_address clientAddr;
tr_port clientPort;
int clientSocket;
clientSocket = tr_netAccept( s->session, *socket, &clientAddr, &clientPort );
if( clientSocket > 0 )
{
tr_deepLog( __FILE__, __LINE__, NULL,
"New INCOMING connection %d (%s)",
clientSocket, tr_peerIoAddrStr( &clientAddr, clientPort ) );
assert( s );
assert( s->timer );
tr_peerMgrAddIncoming( s->session->peerMgr, &clientAddr, clientPort,
clientSocket );
}
}
/* do something */
natPulse( s, s->doPortCheck );
s->doPortCheck = FALSE;
static void
bindCb( int * const socket,
tr_address * const addr,
void * const userData )
{
tr_shared * s = ( tr_shared * )userData;
*socket = tr_netBindTCP( addr, s->publicPort, FALSE );
if( *socket >= 0 )
/* when to wake up again */
switch( tr_sharedTraversalStatus( s ) )
{
tr_ninf( getKey( ),
_( "Opened port %d on %s to listen for incoming peer connections" ),
s->publicPort, tr_ntop_non_ts( addr ) );
listen( *socket, 10 );
}
else
{
tr_nerr( getKey( ),
_(
"Couldn't open port %d on %s to listen for incoming peer connections (errno %d - %s)" ),
s->publicPort, tr_ntop_non_ts( addr ), errno, tr_strerror( errno ) );
}
}
case TR_PORT_MAPPED:
/* if we're mapped, everything is fine... check back in 20 minutes
* to renew the port forwarding if it's expired */
s->doPortCheck = TRUE;
interval.tv_sec = 60*20;
break;
static void
incomingPeersPulse( tr_shared * s )
{
if( s->shouldChange )
{
tr_socketListForEach( s->bindSockets, &closeCb, s );
s->shouldChange = FALSE;
if( s->publicPort > 0 )
tr_socketListForEach( s->bindSockets, &bindCb, s );
case TR_PORT_ERROR:
/* some kind of an error. wait 60 seconds and retry */
interval.tv_sec = 60;
break;
default:
/* in progress. pulse frequently. */
interval.tv_sec = 0;
interval.tv_usec = 333000;
break;
}
/* (jhujhiti):
* This has been changed from a loop that will end when the listener queue
* is exhausted to one that will only check for one connection at a time.
* I think it unlikely that we get many more than one connection in the
* time between pulses (currently one second). However, just to be safe,
* I have increased the length of the listener queue from 5 to 10
* (see acceptCb() above). */
tr_socketListForEach( s->bindSockets, &acceptCb, s );
}
static int
sharedPulse( void * vshared )
{
tr_bool keepPulsing = TRUE;
tr_shared * shared = vshared;
natPulse( shared, FALSE );
if( !shared->isShuttingDown )
{
incomingPeersPulse( shared );
}
else
{
tr_ninf( getKey( ), _( "Stopped" ) );
tr_timerFree( &shared->pulseTimer );
tr_timerFree( &shared->recheckTimer );
tr_socketListForEach( shared->bindSockets, &closeCb, shared );
tr_socketListFree( shared->bindSockets );
tr_natpmpClose( shared->natpmp );
tr_upnpClose( shared->upnp );
shared->session->shared = NULL;
tr_free( shared );
keepPulsing = FALSE;
}
return keepPulsing;
}
static int
recheckPulse( void * vshared )
{
tr_bool keepPulsing = TRUE;
tr_shared * shared = vshared;
tr_ninf( getKey( ), _( "Checking to see if port %d is still open" ), shared->publicPort );
natPulse( shared, TRUE );
if( shared->isShuttingDown )
keepPulsing = FALSE;
return keepPulsing;
evtimer_add( s->timer, &interval );
}
/***
****
***/
static void
start_timer( tr_shared * s )
{
s->timer = tr_new0( struct event, 1 );
evtimer_set( s->timer, onTimer, s );
onTimer( 0, 0, s );
}
tr_shared *
tr_sharedInit( tr_session * session,
tr_bool isEnabled,
tr_port publicPort,
tr_socketList * socks )
tr_sharedInit( tr_session * session, tr_bool isEnabled )
{
tr_shared * s = tr_new0( tr_shared, 1 );
s->session = session;
s->publicPort = publicPort;
s->bindSockets = socks;
s->shouldChange = TRUE;
s->natpmp = tr_natpmpInit( );
s->upnp = tr_upnpInit( );
s->pulseTimer = tr_timerNew( session, sharedPulse, s, 1000 );
s->recheckTimer = tr_timerNew( session, recheckPulse, s, 1000*60*20 ); /* 20 minutes */
s->isEnabled = isEnabled;
s->upnpStatus = TR_PORT_UNMAPPED;
s->natpmpStatus = TR_PORT_UNMAPPED;
if( isEnabled )
start_timer( s );
return s;
}
void
tr_sharedShuttingDown( tr_shared * s )
static void
stop_timer( tr_shared * s )
{
s->isShuttingDown = 1;
if( s->timer != NULL )
{
evtimer_del( s->timer );
tr_free( s->timer );
s->timer = NULL;
}
}
static void
stop_forwarding( tr_shared * s )
{
tr_ninf( getKey( ), _( "Stopped" ) );
natPulse( s, FALSE );
tr_natpmpClose( s->natpmp );
tr_upnpClose( s->upnp );
stop_timer( s );
}
void
tr_sharedSetPort( tr_shared * s, tr_port port )
tr_sharedClose( tr_session * session )
{
tr_torrent * tor = NULL;
tr_shared * s = session->shared;
s->publicPort = port;
s->shouldChange = TRUE;
while( ( tor = tr_torrentNext( s->session, tor ) ) )
tr_torrentChangeMyPort( tor );
}
tr_port
tr_sharedGetPeerPort( const tr_shared * s )
{
return s->publicPort;
s->isShuttingDown = TRUE;
stop_forwarding( s );
s->session->shared = NULL;
tr_free( s );
}
void
tr_sharedTraversalEnable( tr_shared * s, tr_bool isEnabled )
{
s->isEnabled = isEnabled;
if(( s->isEnabled = isEnabled ))
start_timer( s );
else
stop_forwarding( s );
}
void
tr_sharedPortChanged( tr_session * session )
{
tr_shared * s = session->shared;
if( s->isEnabled )
{
stop_timer( s );
start_timer( s );
}
}
tr_bool
@ -296,9 +215,3 @@ tr_sharedTraversalStatus( const tr_shared * s )
{
return MAX( s->natpmpStatus, s->upnpStatus );
}
const tr_socketList *
tr_sharedGetBindSockets( const tr_shared * shared )
{
return shared->bindSockets;
}

View File

@ -32,14 +32,15 @@
#include "transmission.h"
#include "net.h"
struct tr_bindsockets;
typedef struct tr_shared tr_shared;
tr_shared* tr_sharedInit( tr_session*, tr_bool isEnabled, tr_port publicPort,
tr_socketList * socks);
tr_shared* tr_sharedInit( tr_session*, tr_bool isEnabled );
void tr_sharedShuttingDown( tr_shared * );
void tr_sharedClose( tr_session * );
void tr_sharedSetPort( tr_shared *, tr_port publicPort );
void tr_sharedPortChanged( tr_session * );
void tr_sharedTraversalEnable( tr_shared *, tr_bool isEnabled );
@ -49,5 +50,4 @@ tr_bool tr_sharedTraversalIsEnabled( const tr_shared * s );
int tr_sharedTraversalStatus( const tr_shared * );
const tr_socketList *tr_sharedGetBindSockets( const tr_shared * shared );
#endif

View File

@ -32,6 +32,7 @@
#include "list.h"
#include "metainfo.h" /* tr_metainfoFree */
#include "net.h"
#include "peer-io.h"
#include "peer-mgr.h"
#include "platform.h" /* tr_lock */
#include "port-forwarding.h"
@ -125,9 +126,100 @@ tr_sessionSetEncryption( tr_session * session,
****
***/
struct tr_bindinfo
{
int socket;
tr_address addr;
struct event ev;
};
static void
close_bindinfo( struct tr_bindinfo * b )
{
if( b->socket >=0 )
{
event_del( &b->ev );
EVUTIL_CLOSESOCKET( b->socket );
}
}
static void
close_incoming_peer_port( tr_session * session )
{
close_bindinfo( session->public_ipv4 );
close_bindinfo( session->public_ipv6 );
}
static void
free_incoming_peer_port( tr_session * session )
{
close_bindinfo( session->public_ipv4 );
tr_free( session->public_ipv4 );
session->public_ipv4 = NULL;
close_bindinfo( session->public_ipv6 );
tr_free( session->public_ipv6 );
session->public_ipv6 = NULL;
}
static void
accept_incoming_peer( int fd, short what UNUSED, void * vsession )
{
int clientSocket;
tr_port clientPort;
tr_address clientAddr;
tr_session * session = vsession;
clientSocket = tr_netAccept( session, fd, &clientAddr, &clientPort );
if( clientSocket > 0 ) {
tr_deepLog( __FILE__, __LINE__, NULL, "new incoming connection %d (%s)",
clientSocket, tr_peerIoAddrStr( &clientAddr, clientPort ) );
tr_peerMgrAddIncoming( session->peerMgr, &clientAddr, clientPort, clientSocket );
}
}
static void
open_incoming_peer_port( tr_session * session )
{
struct tr_bindinfo * b;
/* bind an ipv4 port to listen for incoming peers... */
b = session->public_ipv4;
b->socket = tr_netBindTCP( &b->addr, session->peerPort, FALSE );
if( b->socket >= 0 ) {
event_set( &b->ev, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session );
event_add( &b->ev, NULL );
}
/* and do the exact same thing for ipv6, if it's supported... */
if( tr_net_hasIPv6( session->peerPort ) ) {
b = session->public_ipv6;
b->socket = tr_netBindTCP( &b->addr, session->peerPort, FALSE );
if( b->socket >= 0 ) {
event_set( &b->ev, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session );
event_add( &b->ev, NULL );
}
}
}
const tr_address*
tr_sessionGetPublicAddress( const tr_session * session, int tr_af_type )
{
switch( tr_af_type )
{
case TR_AF_INET: return &session->public_ipv4->addr;
case TR_AF_INET6: return &session->public_ipv6->addr; break;
default: return NULL;
}
}
/***
****
***/
static int
tr_stringEndsWith( const char * str,
const char * end )
tr_stringEndsWith( const char * str, const char * end )
{
const size_t slen = strlen( str );
const size_t elen = strlen( end );
@ -303,14 +395,9 @@ tr_sessionGetDefaultSettings( tr_benc * d )
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6, TR_DEFAULT_BIND_ADDRESS_IPV6 );
}
const tr_socketList * tr_getSessionBindSockets( const tr_session * session );
void
tr_sessionGetSettings( tr_session * s, struct tr_benc * d )
{
const char * val;
const tr_address * addr;
assert( tr_bencIsDict( d ) );
tr_bencDictReserve( d, 30 );
@ -359,14 +446,8 @@ tr_sessionGetSettings( tr_session * s, struct tr_benc * d )
tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED, tr_sessionGetSpeedLimit( s, TR_UP ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, s->uploadSlotsPerTorrent );
addr = tr_socketListGetType( tr_getSessionBindSockets( s ), TR_AF_INET );
val = addr ? tr_ntop_non_ts( addr ) : TR_DEFAULT_BIND_ADDRESS_IPV4;
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4, val );
addr = tr_socketListGetType( tr_getSessionBindSockets( s ), TR_AF_INET6 );
val = addr ? tr_ntop_non_ts( addr ) : TR_DEFAULT_BIND_ADDRESS_IPV6;
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6, val );
tr_bencDictAddStr ( d, TR_DEFAULT_BIND_ADDRESS_IPV4, tr_ntop_non_ts( &s->public_ipv4->addr ) );
tr_bencDictAddStr ( d, TR_DEFAULT_BIND_ADDRESS_IPV6, tr_ntop_non_ts( &s->public_ipv6->addr ) );
}
void
@ -512,8 +593,6 @@ tr_sessionInitImpl( void * vdata )
struct init_data * data = vdata;
tr_benc * clientSettings = data->clientSettings;
tr_session * session = data->session;
tr_address address;
tr_socketList * socketList;
assert( tr_amInEventThread( session ) );
assert( tr_bencIsDict( clientSettings ) );
@ -633,39 +712,30 @@ tr_sessionInitImpl( void * vdata )
assert( found );
session->peerPort = session->isPortRandom ? getRandomPort( session ) : j;
/* bind addresses */
/* public addresses */
found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4,
&str );
assert( found );
if( tr_pton( str, &address ) == NULL ) {
tr_err( _( "%s is not a valid address" ), str );
socketList = tr_socketListNew( &tr_inaddr_any );
} else if( address.type != TR_AF_INET ) {
tr_err( _( "%s is not an IPv4 address" ), str );
socketList = tr_socketListNew( &tr_inaddr_any );
} else
socketList = tr_socketListNew( &address );
{
struct tr_bindinfo b;
const char * str;
found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6,
&str );
assert( found );
if( tr_pton( str, &address ) == NULL ) {
tr_err( _( "%s is not a valid address" ), str );
address = tr_in6addr_any;
} else if( address.type != TR_AF_INET6 ) {
tr_err( _( "%s is not an IPv6 address" ), str );
address = tr_in6addr_any;
str = TR_PREFS_KEY_BIND_ADDRESS_IPV4;
tr_bencDictFindStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, &str );
if( !tr_pton( str, &b.addr ) || ( b.addr.type != TR_AF_INET ) )
b.addr = tr_inaddr_any;
b.socket = -1;
session->public_ipv4 = tr_memdup( &b, sizeof( struct tr_bindinfo ) );
str = TR_PREFS_KEY_BIND_ADDRESS_IPV6;
tr_bencDictFindStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, &str );
if( !tr_pton( str, &b.addr ) || ( b.addr.type != TR_AF_INET6 ) )
b.addr = tr_in6addr_any;
b.socket = -1;
session->public_ipv6 = tr_memdup( &b, sizeof( struct tr_bindinfo ) );
open_incoming_peer_port( session );
}
if( tr_net_hasIPv6( session->peerPort ) )
tr_socketListAppend( socketList, &address );
else
tr_inf( _( "System does not seem to support IPv6. Not listening on"
" an IPv6 address" ) );
session->shared = tr_sharedInit( session, boolVal, session->peerPort,
socketList );
session->isPortSet = session->peerPort > 0;
session->shared = tr_sharedInit( session, boolVal );
/**
**/
@ -818,36 +888,19 @@ tr_globalIsLocked( const tr_session * session )
*
**********************************************************************/
struct bind_port_data
{
tr_session * session;
tr_port port;
};
static void
tr_setBindPortImpl( void * vdata )
setPeerPort( void * session )
{
struct bind_port_data * data = vdata;
tr_session * session = data->session;
const tr_port port = data->port;
session->isPortSet = 1;
tr_sharedSetPort( session->shared, port );
tr_free( data );
}
static void
setPortImpl( tr_session * session, tr_port port )
{
struct bind_port_data * data;
tr_torrent * tor = NULL;
assert( tr_isSession( session ) );
data = tr_new( struct bind_port_data, 1 );
data->session = session;
data->port = port;
tr_runInEventThread( session, tr_setBindPortImpl, data );
close_incoming_peer_port( session );
open_incoming_peer_port( session );
tr_sharedPortChanged( session );
while(( tor = tr_torrentNext( session, tor )))
tr_torrentChangeMyPort( tor );
}
void
@ -857,7 +910,8 @@ tr_sessionSetPeerPort( tr_session * session,
assert( tr_isSession( session ) );
session->peerPort = port;
setPortImpl( session, session->peerPort );
tr_runInEventThread( session, setPeerPort, session );
}
tr_port
@ -873,8 +927,7 @@ tr_sessionSetPeerPortRandom( tr_session * session )
{
assert( tr_isSession( session ) );
session->peerPort = getRandomPort( session );
setPortImpl( session, session->peerPort );
tr_sessionSetPeerPort( session, getRandomPort( session ) );
return session->peerPort;
}
@ -1348,13 +1401,15 @@ tr_closeAllConnections( void * vsession )
assert( tr_isSession( session ) );
free_incoming_peer_port( session );
evtimer_del( session->altTimer );
tr_free( session->altTimer );
session->altTimer = NULL;
tr_verifyClose( session );
tr_statsClose( session );
tr_sharedShuttingDown( session->shared );
tr_sharedClose( session );
tr_rpcClose( &session->rpcServer );
/* close the torrents. get the most active ones first so that
@ -2093,9 +2148,3 @@ tr_sessionGetActiveTorrentCount( tr_session * session )
return ret;
}
const tr_socketList *
tr_getSessionBindSockets( const tr_session * session )
{
return tr_sharedGetBindSockets( session->shared );
}

View File

@ -55,10 +55,10 @@ struct tr_metainfo_lookup
struct tr_address;
struct tr_bandwidth;
struct tr_bindsockets;
struct tr_session
{
tr_bool isPortSet;
tr_bool isPortRandom;
tr_bool isPexEnabled;
tr_bool isBlocklistEnabled;
@ -151,6 +151,9 @@ struct tr_session
struct tr_bandwidth * bandwidth;
double desiredRatio;
struct tr_bindinfo * public_ipv4;
struct tr_bindinfo * public_ipv6;
};
tr_bool tr_sessionGetActiveSpeedLimit( const tr_session * session,
@ -173,6 +176,10 @@ void tr_globalUnlock( tr_session * );
tr_bool tr_globalIsLocked( const tr_session * );
const struct tr_address* tr_sessionGetPublicAddress( const tr_session *, int tr_af_type );
struct tr_bindsockets * tr_sessionGetBindSockets( tr_session * );
enum
{
SESSION_MAGIC_NUMBER = 3845

View File

@ -596,7 +596,6 @@ torrentRealInit( tr_torrent * tor, const tr_ctor * ctor )
tr_peerMgrAddTorrent( session->peerMgr, tor );
assert( session->isPortSet );
assert( !tor->downloadedCur );
assert( !tor->uploadedCur );