From 2719372bc6e9f270af8986d4955ee21dcda3e829 Mon Sep 17 00:00:00 2001 From: Erick Turnquist Date: Wed, 15 Apr 2009 21:05:58 +0000 Subject: [PATCH] (trunk) #1497 Options to listen on specific network sockets --- daemon/daemon.c | 12 +++++++ daemon/transmission-daemon.1 | 7 ++++ libtransmission/net.c | 31 ++++++++++++++++ libtransmission/net.h | 2 ++ libtransmission/port-forwarding.c | 25 +++++-------- libtransmission/port-forwarding.h | 5 ++- libtransmission/rpc-server.c | 30 +++++++++++++++- libtransmission/rpc-server.h | 1 + libtransmission/session.c | 60 ++++++++++++++++++++++++++++++- libtransmission/session.h | 2 -- libtransmission/transmission.h | 7 ++++ 11 files changed, 160 insertions(+), 22 deletions(-) diff --git a/daemon/daemon.c b/daemon/daemon.c index da8024ead..5369eecf5 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -84,6 +84,9 @@ static const struct tr_option options[] = { 910, "encryption-required", "Encrypt all peer connections", "er", 0, NULL }, { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL }, { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL }, + { 'i', "bind-address-ipv4", "Where to listen for peer connections", "i", 1, "" }, + { 'I', "bind-address-ipv6", "Where to listen for peer connections", "I", 1, "" }, { 0, NULL, NULL, NULL, 0, NULL } }; @@ -276,6 +279,15 @@ main( int argc, char ** argv ) break; case 912: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_CLEAR_PREFERRED ); break; + case 'i': + tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, optarg ); + break; + case 'I': + tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, optarg ); + break; + case 'r': + tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_BIND_ADDRESS, optarg ); + break; default: showUsage( ); break; } diff --git a/daemon/transmission-daemon.1 b/daemon/transmission-daemon.1 index b4ee36734..971e0aff7 100644 --- a/daemon/transmission-daemon.1 +++ b/daemon/transmission-daemon.1 @@ -74,6 +74,13 @@ Prefer unencrypted peer connections. .It Fl h Fl -help Print command-line option descriptions. +.It Fl i Fl -bind-address-ipv4 +Listen for IPv4 BitTorrent connections on a specific address. Only one IPv4 listening address is allowed. Default: 0.0.0.0 (All addresses) +.It Fl I Fl -bind-address-ipv6 +Listen for IPv6 BitTorrent connections on a specific address. Only one IPv6 listening address is allowed. Default: :: (All addresses) +.It Fl r Fl -rpc-bind-address +Listen for RPC connections on a specific address. This must be an IPv4 address. Only one RPC listening address is allowed. Default: 0.0.0.0 (All addresses) + .It Fl L Fl -peerlimit-global Ar limit Overall peer limit. Useful on embedded systems where the default might be unsuitable. Default: 240 .It Fl l Fl -peerlimit-torrent Ar limit diff --git a/libtransmission/net.c b/libtransmission/net.c index c29d4371c..a3dde5fb7 100644 --- a/libtransmission/net.c +++ b/libtransmission/net.c @@ -154,6 +154,7 @@ tr_address * tr_pton( const char * src, tr_address * dst ) { int retval = inet_pton( AF_INET, src, &dst->addr ); + assert( dst ); if( retval < 0 ) return NULL; else if( retval == 0 ) @@ -316,6 +317,19 @@ tr_socketListForEach( tr_socketList * const head, 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 **********************************************************************/ @@ -446,6 +460,8 @@ 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, @@ -455,6 +471,9 @@ tr_netOpenTCP( tr_session * session, struct sockaddr_storage sock; const int type = SOCK_STREAM; socklen_t addrlen; + const tr_address * source_addr; + socklen_t sourcelen; + struct sockaddr_storage source_sock; assert( tr_isAddress( addr ) ); @@ -467,6 +486,18 @@ tr_netOpenTCP( tr_session * session, setSndBuf( session, s ); addrlen = setup_sockaddr( addr, port, &sock ); + + /* set source address */ + source_addr = tr_socketListGetType( tr_getSessionBindSockets( session ), + addr->type ); + assert( source_addr ); + sourcelen = setup_sockaddr( source_addr, 0, &source_sock ); + if( bind( s, ( struct sockaddr * ) &source_sock, sourcelen ) ) + { + tr_err( _( "Couldn't set source address %s on %d: %s" ), + tr_ntop_non_ts( source_addr ), s, tr_strerror( errno ) ); + return -errno; + } if( ( connect( s, (struct sockaddr *) &sock, addrlen ) < 0 ) diff --git a/libtransmission/net.h b/libtransmission/net.h index 0b895fe40..fcfaf7c7b 100644 --- a/libtransmission/net.h +++ b/libtransmission/net.h @@ -112,6 +112,8 @@ void tr_socketListForEach( tr_socketList * const head, tr_address * const, void * const ), void * const userData); +const tr_address *tr_socketListGetType( const tr_socketList * const el, + tr_address_type type ); /*********************************************************************** * Sockets diff --git a/libtransmission/port-forwarding.c b/libtransmission/port-forwarding.c index 6f6d0f6d0..a031f3c4e 100644 --- a/libtransmission/port-forwarding.c +++ b/libtransmission/port-forwarding.c @@ -215,32 +215,18 @@ sharedPulse( void * vshared ) **** ***/ -static tr_socketList * -setupBindSockets( tr_port port ) -{ - tr_bool hasIPv6 = tr_net_hasIPv6( port ); - tr_socketList * socks = NULL; - if( hasIPv6 ) - socks = tr_socketListNew( &tr_in6addr_any ); - - if( socks ) - tr_socketListAppend( socks, &tr_inaddr_any ); - else - socks = tr_socketListNew( &tr_inaddr_any ); - return socks; /* Because the dryer gremlins won't */ -} - tr_shared * tr_sharedInit( tr_session * session, tr_bool isEnabled, - tr_port publicPort ) + tr_port publicPort, + tr_socketList * socks ) { tr_shared * s = tr_new0( tr_shared, 1 ); s->session = session; s->publicPort = publicPort; s->shouldChange = TRUE; - s->bindSockets = setupBindSockets( publicPort ); + s->bindSockets = socks; s->shouldChange = TRUE; s->natpmp = tr_natpmpInit( ); s->upnp = tr_upnpInit( ); @@ -294,3 +280,8 @@ tr_sharedTraversalStatus( const tr_shared * s ) return MAX( s->natpmpStatus, s->upnpStatus ); } +const tr_socketList * +tr_sharedGetBindSockets( const tr_shared * shared ) +{ + return shared->bindSockets; +} diff --git a/libtransmission/port-forwarding.h b/libtransmission/port-forwarding.h index ee8115780..47c08de3d 100644 --- a/libtransmission/port-forwarding.h +++ b/libtransmission/port-forwarding.h @@ -30,10 +30,12 @@ #define SHARED_H 1 #include "transmission.h" +#include "net.h" typedef struct tr_shared tr_shared; -tr_shared* tr_sharedInit( tr_session*, tr_bool isEnabled, tr_port publicPort ); +tr_shared* tr_sharedInit( tr_session*, tr_bool isEnabled, tr_port publicPort, + tr_socketList * socks); void tr_sharedShuttingDown( tr_shared * ); @@ -47,4 +49,5 @@ tr_bool tr_sharedTraversalIsEnabled( const tr_shared * s ); int tr_sharedTraversalStatus( const tr_shared * ); +const tr_socketList *tr_sharedGetBindSockets( const tr_shared * shared ); #endif diff --git a/libtransmission/rpc-server.c b/libtransmission/rpc-server.c index 9766b47fd..c7dc06aa9 100644 --- a/libtransmission/rpc-server.c +++ b/libtransmission/rpc-server.c @@ -37,6 +37,7 @@ #include "trevent.h" #include "utils.h" #include "web.h" +#include "net.h" #define MY_NAME "RPC Server" #define MY_REALM "Transmission" @@ -52,6 +53,7 @@ struct tr_rpc_server tr_bool isPasswordEnabled; tr_bool isWhitelistEnabled; tr_port port; + struct in_addr bindAddress; struct evhttp * httpd; tr_session * session; char * username; @@ -528,11 +530,15 @@ static void startServer( void * vserver ) { tr_rpc_server * server = vserver; + tr_address addr; if( !server->httpd ) { + addr.type = TR_AF_INET; + addr.addr.addr4 = server->bindAddress; server->httpd = evhttp_new( tr_eventGetBase( server->session ) ); - evhttp_bind_socket( server->httpd, "0.0.0.0", server->port ); + evhttp_bind_socket( server->httpd, tr_ntop_non_ts( &addr ), + server->port ); evhttp_set_gencb( server->httpd, handle_request, server ); } @@ -707,6 +713,15 @@ tr_rpcIsPasswordEnabled( const tr_rpc_server * server ) return server->isPasswordEnabled; } +const char * +tr_rpcGetBindAddress( const tr_rpc_server * server ) +{ + tr_address addr; + addr.type = TR_AF_INET; + addr.addr.addr4 = server->bindAddress; + return tr_ntop_non_ts( &addr ); +} + /**** ***** LIFE CYCLE ****/ @@ -745,6 +760,7 @@ tr_rpcInit( tr_session * session, tr_bool boolVal; int64_t i; const char *str; + tr_address address; s = tr_new0( tr_rpc_server, 1 ); s->session = session; @@ -780,6 +796,18 @@ tr_rpcInit( tr_session * session, else s->password = strdup( str ); + found = tr_bencDictFindStr( settings, TR_PREFS_KEY_RPC_BIND_ADDRESS, &str ); + assert( found ); + if( tr_pton( str, &address ) == NULL ) { + tr_err( _( "%s is not a valid address" ), str ); + address = tr_inaddr_any; + } else if( address.type != TR_AF_INET ) { + tr_err( _( "%s is not an IPv4 address. RPC listeners must be IPv4" ), + str ); + address = tr_inaddr_any; + } + s->bindAddress = address.addr.addr4; + #ifdef HAVE_ZLIB s->stream.zalloc = (alloc_func) Z_NULL; s->stream.zfree = (free_func) Z_NULL; diff --git a/libtransmission/rpc-server.h b/libtransmission/rpc-server.h index cfb2fa854..8eba2b2a2 100644 --- a/libtransmission/rpc-server.h +++ b/libtransmission/rpc-server.h @@ -63,5 +63,6 @@ void tr_rpcSetPasswordEnabled( tr_rpc_server * server, tr_bool tr_rpcIsPasswordEnabled( const tr_rpc_server * session ); +const char* tr_rpcGetBindAddress( const tr_rpc_server * server ); #endif diff --git a/libtransmission/session.c b/libtransmission/session.c index 999c6f314..56b611476 100644 --- a/libtransmission/session.c +++ b/libtransmission/session.c @@ -281,6 +281,7 @@ tr_sessionGetDefaultSettings( tr_benc * d ) tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO, 2.0 ); tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED, FALSE ); tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED, FALSE ); + tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS, "0.0.0.0" ); tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED, TRUE ); tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD, "" ); tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME, "" ); @@ -297,8 +298,12 @@ tr_sessionGetDefaultSettings( tr_benc * d ) tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED, 100 ); tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, FALSE ); tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, 14 ); + tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4, TR_DEFAULT_BIND_ADDRESS_IPV4 ); + 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 ) { @@ -336,6 +341,7 @@ tr_sessionGetSettings( tr_session * s, struct tr_benc * d ) tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO, s->desiredRatio ); tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED, s->isRatioLimited ); tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED, tr_sessionIsRPCPasswordEnabled( s ) ); + tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS, tr_sessionGetRPCBindAddress( s ) ); tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED, tr_sessionIsRPCEnabled( s ) ); tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD, freeme[n++] = tr_sessionGetRPCPassword( s ) ); tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT, tr_sessionGetRPCPort( s ) ); @@ -352,6 +358,10 @@ 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 ); + tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4, + tr_ntop_non_ts( tr_socketListGetType( tr_getSessionBindSockets( s ), TR_AF_INET ) ) ); + tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6, + tr_ntop_non_ts( tr_socketListGetType( tr_getSessionBindSockets( s ), TR_AF_INET6 ) ) ); for( i=0; iclientSettings; tr_session * session = data->session; + tr_address address; + tr_socketList * socketList; assert( tr_amInEventThread( session ) ); assert( tr_bencIsDict( clientSettings ) ); @@ -618,7 +630,39 @@ tr_sessionInitImpl( void * vdata ) && tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT, &j ); assert( found ); session->peerPort = session->isPortRandom ? getRandomPort( session ) : j; - session->shared = tr_sharedInit( session, boolVal, session->peerPort ); + + /* bind 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 ); + + 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; + } + 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; /** @@ -1889,6 +1933,14 @@ tr_sessionIsRPCPasswordEnabled( const tr_session * session ) return tr_rpcIsPasswordEnabled( session->rpcServer ); } +const char * +tr_sessionGetRPCBindAddress( const tr_session * session ) +{ + assert( tr_isSession( session ) ); + + return tr_rpcGetBindAddress( session->rpcServer ); +} + /*** **** ***/ @@ -2038,3 +2090,9 @@ tr_sessionGetActiveTorrentCount( tr_session * session ) return ret; } + +const tr_socketList * +tr_getSessionBindSockets( const tr_session * session ) +{ + return tr_sharedGetBindSockets( session->shared ); +} diff --git a/libtransmission/session.h b/libtransmission/session.h index dc9ff7c82..bc0f759d7 100644 --- a/libtransmission/session.h +++ b/libtransmission/session.h @@ -171,8 +171,6 @@ void tr_globalLock( tr_session * ); void tr_globalUnlock( tr_session * ); -tr_bool tr_globalIsLocked( const tr_session * ); - enum { SESSION_MAGIC_NUMBER = 3845 diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index 24b85ccfd..fb99bca2c 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -150,6 +150,8 @@ static TR_INLINE tr_bool tr_isEncryptionMode( tr_encryption_mode m ) } +#define TR_DEFAULT_BIND_ADDRESS_IPV4 "0.0.0.0" +#define TR_DEFAULT_BIND_ADDRESS_IPV6 "::" #define TR_DEFAULT_OPEN_FILE_LIMIT_STR "32" #define TR_DEFAULT_RPC_WHITELIST "127.0.0.1" #define TR_DEFAULT_RPC_PORT_STR "9091" @@ -165,6 +167,8 @@ static TR_INLINE tr_bool tr_isEncryptionMode( tr_encryption_mode m ) #define TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED "alt-speed-time-enabled" #define TR_PREFS_KEY_ALT_SPEED_TIME_END "alt-speed-time-end" #define TR_PREFS_KEY_ALT_SPEED_TIME_DAY "alt-speed-time-day" +#define TR_PREFS_KEY_BIND_ADDRESS_IPV4 "bind-address-ipv4" +#define TR_PREFS_KEY_BIND_ADDRESS_IPV6 "bind-address-ipv6" #define TR_PREFS_KEY_BLOCKLIST_ENABLED "blocklist-enabled" #define TR_PREFS_KEY_DOWNLOAD_DIR "download-dir" #define TR_PREFS_KEY_ENCRYPTION "encryption" @@ -191,6 +195,7 @@ static TR_INLINE tr_bool tr_isEncryptionMode( tr_encryption_mode m ) #define TR_PREFS_KEY_RATIO "ratio-limit" #define TR_PREFS_KEY_RATIO_ENABLED "ratio-limit-enabled" #define TR_PREFS_KEY_RPC_AUTH_REQUIRED "rpc-authentication-required" +#define TR_PREFS_KEY_RPC_BIND_ADDRESS "rpc-bind-address" #define TR_PREFS_KEY_RPC_ENABLED "rpc-enabled" #define TR_PREFS_KEY_RPC_PASSWORD "rpc-password" #define TR_PREFS_KEY_RPC_PORT "rpc-port" @@ -397,6 +402,8 @@ void tr_sessionSetRPCPasswordEnabled( tr_session * session, tr_bool tr_sessionIsRPCPasswordEnabled( const tr_session * session ); +const char* tr_sessionGetRPCBindAddress( const tr_session * session ); + typedef enum { -- 2.40.0