]> granicus.if.org Git - transmission/commitdiff
(trunk) #1497 Options to listen on specific network sockets
authorErick Turnquist <jhujhiti@adjectivism.org>
Wed, 15 Apr 2009 21:05:58 +0000 (21:05 +0000)
committerErick Turnquist <jhujhiti@adjectivism.org>
Wed, 15 Apr 2009 21:05:58 +0000 (21:05 +0000)
daemon/daemon.c
daemon/transmission-daemon.1
libtransmission/net.c
libtransmission/net.h
libtransmission/port-forwarding.c
libtransmission/port-forwarding.h
libtransmission/rpc-server.c
libtransmission/rpc-server.h
libtransmission/session.c
libtransmission/session.h
libtransmission/transmission.h

index da8024eadb496c8a3ccb9842903a844bc413b204..5369eecf5883a5dcaec7a67c164e0397862739b6 100644 (file)
@@ -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, "<ipv4 address>" },
+    { 'I', "bind-address-ipv6", "Where to listen for peer connections", "I", 1, "<ipv6 address" },
+    { 'r', "rpc-bind-address", "Where to listen for RPC connections", "r", 1, "<ipv4 address>" },
     { 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;
         }
index b4ee367346ca031f8ad7f88c8569b6e2450ed52c..971e0aff74a7a37492ac4215be7dc2a2afd1d7c1 100644 (file)
@@ -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
index c29d4371c03b875ad79855df64607bc5478dfa99..a3dde5fb712ed15a86b357d52bbd7531b1ceea0d 100644 (file)
@@ -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 )
index 0b895fe40a278cee470566b320aa34238e58932d..fcfaf7c7bd64ce744bdb48120df28015809d23df 100644 (file)
@@ -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
index 6f6d0f6d0309ed3bd8d3eba9691b9b2967c7b508..a031f3c4ed98dc96ba3586f4b0e7082c383d68a0 100644 (file)
@@ -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;
+}
index ee81157807f6105c615a885cf247ef2a6ee90dce..47c08de3dd8a067d886a66af0e56aaaca51ac075 100644 (file)
 #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
index 9766b47fdc634ec65b89f7eba30f58cb392e45e0..c7dc06aa92490969af1b65f2efd600fd741d8623 100644 (file)
@@ -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;
index cfb2fa854f3f97920fa21cc13638511041eda9ce..8eba2b2a2ced05105bdd14eff4a6c33445dbca6b 100644 (file)
@@ -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
index 999c6f3140423413e6f1e006099b23bca60c76d1..56b611476f5fe8709648e4573567c6a87a3dcac0 100644 (file)
@@ -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; i<n; ++i )
         tr_free( freeme[i] );
@@ -500,6 +510,8 @@ 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 ) );
@@ -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 );
+}
index dc9ff7c8242b7b338dc37173c25d0db33fa01b7a..bc0f759d72c866d1d85b2228f0bdd40c26c429fc 100644 (file)
@@ -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
index 24b85ccfded9e9a5a5e7b716a8367d4a548e5fd8..fb99bca2c9a39c392db8f7dac02dfbbfb703d588 100644 (file)
@@ -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
 {