From 5fec8fe559b1a1f4bcb55c8f2c1f048f5abee3de Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 2 May 2011 15:05:10 -0400 Subject: [PATCH 1/8] "(Socks|Control|etc)Port auto" now tells Tor to open an arbitrary port This is the major part of the implementation for trac issue 3076. --- changes/feature3076 | 5 +++++ doc/tor.1.txt | 37 ++++++++++++++++++------------ src/or/config.c | 55 +++++++++++++++++++++------------------------ src/or/connection.c | 45 ++++++++++++++++++++++++++----------- src/or/or.h | 4 ++++ 5 files changed, 90 insertions(+), 56 deletions(-) create mode 100644 changes/feature3076 diff --git a/changes/feature3076 b/changes/feature3076 new file mode 100644 index 0000000000..582285070e --- /dev/null +++ b/changes/feature3076 @@ -0,0 +1,5 @@ + o Minor features + - The options SocksPort, ControlPort, and so on now all accept an + optional value "auto" that opens a socket on an OS-selected port. + + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index fb4b3f9bf3..e48342e165 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -145,13 +145,15 @@ Other options can be specified either on the command-line (--option all sockets will be set to this limit. Must be a value between 2048 and 262144, in 1024 byte increments. Default of 8192 is recommended. -**ControlPort** __Port__:: +**ControlPort** __PORT__|**auto**:: If set, Tor will accept connections on this port and allow those connections to control the Tor process using the Tor Control Protocol (described in control-spec.txt). Note: unless you also specify one of - **HashedControlPassword** or **CookieAuthentication**, setting this option will + **HashedControlPassword** or **CookieAuthentication**, setting this + option will cause Tor to allow any process on the local host to control it. This option is required for many Tor controllers; most use the value of 9051. + Set it to "auto" to have Tor pick a port for you. (Default: 0). **ControlListenAddress** __IP__[:__PORT__]:: Bind the controller listener to this address. If you specify a port, bind @@ -647,10 +649,11 @@ The following options are useful only for clients (that is, if the same circuit. Currently, two addresses are "too close" if they lie in the same /16 range. (Default: 1) -**SocksPort** __PORT__:: +**SocksPort** __PORT__|**auto**:: Advertise this port to listen for connections from Socks-speaking applications. Set this to 0 if you don't want to allow application - connections. (Default: 9050) + connections via SOCKS. Set it to "auto" to have Tor pick a port for + you. (Default: 9050) **SocksListenAddress** __IP__[:__PORT__]:: Bind to this address to listen for connections from Socks-speaking @@ -759,23 +762,25 @@ The following options are useful only for clients (that is, if operating as a relay, and it will never use the public key step if it doesn't yet know the onion key of the first hop. (Default: 1) -**TransPort** __PORT__:: +**TransPort** __PORT__|**auto**:: If non-zero, enables transparent proxy support on __PORT__ (by convention, 9040). Requires OS support for transparent proxies, such as BSDs' pf or Linux's IPTables. If you're planning to use Tor as a transparent proxy for a network, you'll want to examine and change VirtualAddrNetwork from the default setting. You'll also want to set the TransListenAddress option for - the network you'd like to proxy. (Default: 0). + the network you'd like to proxy. Set it to "auto" to have Tor pick a + port for you. (Default: 0). **TransListenAddress** __IP__[:__PORT__]:: Bind to this address to listen for transparent proxy connections. (Default: 127.0.0.1). This is useful for exporting a transparent proxy server to an entire network. -**NATDPort** __PORT__:: +**NATDPort** __PORT__|**auto**:: Allow old versions of ipfw (as included in old versions of FreeBSD, etc.) to send connections through Tor using the NATD protocol. This option is - only for people who cannot use TransPort. + only for people who cannot use TransPort. Set it to "auto" to have Tor + pick a port for you. (Default: 0) **NATDListenAddress** __IP__[:__PORT__]:: Bind to this address to listen for NATD connections. (Default: 127.0.0.1). @@ -791,9 +796,10 @@ The following options are useful only for clients (that is, if A comma-separated list of suffixes to use with **AutomapHostsOnResolve**. The "." suffix is equivalent to "all addresses." (Default: .exit,.onion). -**DNSPort** __PORT__:: +**DNSPort** __PORT__|**auto**:: If non-zero, Tor listens for UDP DNS requests on this port and resolves - them anonymously. (Default: 0). + them anonymously. Set it to "auto" to have Tor pick a port for + you. (Default: 0). **DNSListenAddress** __IP__[:__PORT__]:: Bind to this address to listen for DNS connections. (Default: 127.0.0.1). @@ -945,8 +951,10 @@ is non-zero): **NumCPUs** __num__:: How many processes to use at once for decrypting onionskins. (Default: 1) -**ORPort** __PORT__:: - Advertise this port to listen for connections from Tor clients and servers. +**ORPort** __PORT__|**auto**:: + Advertise this port to listen for connections from Tor clients and + servers. This option is required to be a Tor server. + Set it to "auto" to have Tor pick a port for you. (Default: 0). **ORListenAddress** __IP__[:__PORT__]:: Bind to this IP address to listen for connections from Tor clients and @@ -1158,8 +1166,9 @@ if DirPort is non-zero): Minimum uptime of a v2 hidden service directory to be accepted as such by authoritative directories. (Default: 24 hours) -**DirPort** __PORT__:: - Advertise the directory service on this port. +**DirPort** __PORT__|**auto**:: + If this option is nonzero, advertise the directory service on this port. + Set it to "auto" to have Tor pick a port for you. (Default: 0) **DirListenAddress** __IP__[:__PORT__]:: Bind the directory service to this address. If you specify a port, bind to diff --git a/src/or/config.c b/src/or/config.c index 9f94ef1af8..055a4b8919 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -43,6 +43,8 @@ typedef enum config_type_t { CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ CONFIG_TYPE_FILENAME, /**< A filename: some prefixes get expanded. */ CONFIG_TYPE_UINT, /**< A non-negative integer less than MAX_INT */ + CONFIG_TYPE_PORT, /**< A port from 1...65535, 0 for "not set", or + * "auto". */ CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/ CONFIG_TYPE_MEMUNIT, /**< A number of bytes, with optional units*/ CONFIG_TYPE_DOUBLE, /**< A floating-point value */ @@ -203,7 +205,7 @@ static config_var_t _option_vars[] = { V(ConstrainedSockSize, MEMUNIT, "8192"), V(ContactInfo, STRING, NULL), V(ControlListenAddress, LINELIST, NULL), - V(ControlPort, UINT, "0"), + V(ControlPort, PORT, "0"), V(ControlSocket, LINELIST, NULL), V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), @@ -215,7 +217,7 @@ static config_var_t _option_vars[] = { V(DirListenAddress, LINELIST, NULL), OBSOLETE("DirFetchPeriod"), V(DirPolicy, LINELIST, NULL), - V(DirPort, UINT, "0"), + V(DirPort, PORT, "0"), V(DirPortFrontPage, FILENAME, NULL), OBSOLETE("DirPostPeriod"), OBSOLETE("DirRecordUsageByCountry"), @@ -225,7 +227,7 @@ static config_var_t _option_vars[] = { V(DirReqStatistics, BOOL, "0"), VAR("DirServer", LINELIST, DirServers, NULL), V(DisableAllSwap, BOOL, "0"), - V(DNSPort, UINT, "0"), + V(DNSPort, PORT, "0"), V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), V(EnforceDistinctSubnets, BOOL, "1"), @@ -304,7 +306,7 @@ static config_var_t _option_vars[] = { V(NewCircuitPeriod, INTERVAL, "30 seconds"), VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"), V(NATDListenAddress, LINELIST, NULL), - V(NATDPort, UINT, "0"), + V(NATDPort, PORT, "0"), V(Nickname, STRING, NULL), V(WarnUnsafeSocks, BOOL, "1"), OBSOLETE("NoPublish"), @@ -312,7 +314,7 @@ static config_var_t _option_vars[] = { V(NumCPUs, UINT, "1"), V(NumEntryGuards, UINT, "3"), V(ORListenAddress, LINELIST, NULL), - V(ORPort, UINT, "0"), + V(ORPort, PORT, "0"), V(OutboundBindAddress, STRING, NULL), OBSOLETE("PathlenCoinWeight"), V(PerConnBWBurst, MEMUNIT, "0"), @@ -355,7 +357,7 @@ static config_var_t _option_vars[] = { V(ShutdownWaitLength, INTERVAL, "30 seconds"), V(SocksListenAddress, LINELIST, NULL), V(SocksPolicy, LINELIST, NULL), - V(SocksPort, UINT, "9050"), + V(SocksPort, PORT, "9050"), V(SocksTimeout, INTERVAL, "2 minutes"), OBSOLETE("StatusFetchPeriod"), V(StrictNodes, BOOL, "0"), @@ -366,7 +368,7 @@ static config_var_t _option_vars[] = { V(TrackHostExitsExpire, INTERVAL, "30 minutes"), OBSOLETE("TrafficShaping"), V(TransListenAddress, LINELIST, NULL), - V(TransPort, UINT, "0"), + V(TransPort, PORT, "0"), V(TunnelDirConns, BOOL, "1"), V(UpdateBridgesFromAuthority, BOOL, "0"), V(UseBridges, BOOL, "0"), @@ -1689,8 +1691,16 @@ config_assign_value(config_format_t *fmt, or_options_t *options, switch (var->type) { + case CONFIG_TYPE_PORT: + if (!strcasecmp(c->value, "auto")) { + *(int *)lvalue = CFG_AUTO_PORT; + break; + } + /* fall through */ case CONFIG_TYPE_UINT: - i = (int)tor_parse_long(c->value, 10, 0, INT_MAX, &ok, NULL); + i = (int)tor_parse_long(c->value, 10, 0, + var->type==CONFIG_TYPE_PORT ? 65535 : INT_MAX, + &ok, NULL); if (!ok) { tor_asprintf(msg, "Int keyword '%s %s' is malformed or out of bounds.", @@ -1998,6 +2008,12 @@ get_assigned_option(config_format_t *fmt, void *options, } escape_val = 0; /* Can't need escape. */ break; + case CONFIG_TYPE_PORT: + if (*(int*)value == CFG_AUTO_PORT) { + result->value = tor_strdup("auto"); + escape_val = 0; + break; + } case CONFIG_TYPE_INTERVAL: case CONFIG_TYPE_UINT: /* This means every or_options_t uint or bool element @@ -2227,6 +2243,7 @@ option_clear(config_format_t *fmt, or_options_t *options, config_var_t *var) break; case CONFIG_TYPE_INTERVAL: case CONFIG_TYPE_UINT: + case CONFIG_TYPE_PORT: case CONFIG_TYPE_BOOL: *(int*)lvalue = 0; break; @@ -2851,9 +2868,6 @@ options_validate(or_options_t *old_options, or_options_t *options, tor_assert(msg); *msg = NULL; - if (options->ORPort < 0 || options->ORPort > 65535) - REJECT("ORPort option out of bounds."); - if (server_mode(options) && (!strcmpstart(uname, "Windows 95") || !strcmpstart(uname, "Windows 98") || @@ -2968,18 +2982,6 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("Can't use a relative path to torrc when RunAsDaemon is set."); #endif - if (options->SocksPort < 0 || options->SocksPort > 65535) - REJECT("SocksPort option out of bounds."); - - if (options->DNSPort < 0 || options->DNSPort > 65535) - REJECT("DNSPort option out of bounds."); - - if (options->TransPort < 0 || options->TransPort > 65535) - REJECT("TransPort option out of bounds."); - - if (options->NATDPort < 0 || options->NATDPort > 65535) - REJECT("NATDPort option out of bounds."); - if (options->SocksPort == 0 && options->TransPort == 0 && options->NATDPort == 0 && options->ORPort == 0 && options->DNSPort == 0 && !options->RendConfigLines) @@ -2988,12 +2990,6 @@ options_validate(or_options_t *old_options, or_options_t *options, "undefined, and there aren't any hidden services configured. " "Tor will still run, but probably won't do anything."); - if (options->ControlPort < 0 || options->ControlPort > 65535) - REJECT("ControlPort option out of bounds."); - - if (options->DirPort < 0 || options->DirPort > 65535) - REJECT("DirPort option out of bounds."); - #ifndef USE_TRANSPARENT if (options->TransPort || options->TransListenAddress) REJECT("TransPort and TransListenAddress are disabled in this build."); @@ -5238,6 +5234,7 @@ getinfo_helper_config(control_connection_t *conn, case CONFIG_TYPE_STRING: type = "String"; break; case CONFIG_TYPE_FILENAME: type = "Filename"; break; case CONFIG_TYPE_UINT: type = "Integer"; break; + case CONFIG_TYPE_PORT: type = "Port"; break; case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break; case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break; case CONFIG_TYPE_DOUBLE: type = "Float"; break; diff --git a/src/or/connection.c b/src/or/connection.c index eaae791efc..47e5423e0b 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -37,7 +37,7 @@ #include "routerparse.h" static connection_t *connection_create_listener( - struct sockaddr *listensockaddr, + const struct sockaddr *listensockaddr, socklen_t listensocklen, int type, char* address); static void connection_init(time_t now, connection_t *conn, int type, @@ -759,7 +759,7 @@ connection_expire_held_open(void) * The listenaddr struct has to be freed by the caller. */ static struct sockaddr_in * -create_inet_sockaddr(const char *listenaddress, uint16_t listenport, +create_inet_sockaddr(const char *listenaddress, int listenport, char **readable_address, socklen_t *socklen_out) { struct sockaddr_in *listenaddr = NULL; uint32_t addr; @@ -771,8 +771,10 @@ create_inet_sockaddr(const char *listenaddress, uint16_t listenport, "Error parsing/resolving ListenAddress %s", listenaddress); goto err; } - if (usePort==0) - usePort = listenport; + if (usePort==0) { + if (listenport != CFG_AUTO_PORT) + usePort = listenport; + } listenaddr = tor_malloc_zero(sizeof(struct sockaddr_in)); listenaddr->sin_addr.s_addr = htonl(addr); @@ -858,12 +860,13 @@ warn_too_many_conns(void) * to the conn. */ static connection_t * -connection_create_listener(struct sockaddr *listensockaddr, socklen_t socklen, +connection_create_listener(const struct sockaddr *listensockaddr, + socklen_t socklen, int type, char* address) { connection_t *conn; int s; /* the socket we're going to make */ - uint16_t usePort = 0; + uint16_t usePort = 0, gotPort = 0; int start_reading = 0; if (get_n_open_sockets() >= get_options()->_ConnLimit-1) { @@ -872,6 +875,7 @@ connection_create_listener(struct sockaddr *listensockaddr, socklen_t socklen, } if (listensockaddr->sa_family == AF_INET) { + tor_addr_t addr; int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER); #ifndef MS_WINDOWS int one=1; @@ -879,11 +883,10 @@ connection_create_listener(struct sockaddr *listensockaddr, socklen_t socklen, if (is_tcp) start_reading = 1; - usePort = ntohs( (uint16_t) - ((struct sockaddr_in *)listensockaddr)->sin_port); + tor_addr_from_sockaddr(&addr, listensockaddr, &usePort); log_notice(LD_NET, "Opening %s on %s:%d", - conn_type_to_string(type), address, usePort); + conn_type_to_string(type), fmt_addr(&addr), usePort); s = tor_open_socket(PF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, @@ -921,6 +924,21 @@ connection_create_listener(struct sockaddr *listensockaddr, socklen_t socklen, goto err; } } + + if (usePort != 0) { + gotPort = usePort; + } else { + tor_addr_t addr2; + struct sockaddr_storage ss; + socklen_t ss_len=sizeof(ss); + if (getsockname(s, (struct sockaddr*)&ss, &ss_len)<0) { + log_warn(LD_NET, "getsockname() couldn't learn address for %s: %s", + conn_type_to_string(type), + tor_socket_strerror(tor_socket_errno(s))); + gotPort = 0; + } + tor_addr_from_sockaddr(&addr2, (struct sockaddr*)&ss, &gotPort); + } #ifdef HAVE_SYS_UN_H } else if (listensockaddr->sa_family == AF_UNIX) { start_reading = 1; @@ -968,7 +986,7 @@ connection_create_listener(struct sockaddr *listensockaddr, socklen_t socklen, conn->socket_family = listensockaddr->sa_family; conn->s = s; conn->address = tor_strdup(address); - conn->port = usePort; + conn->port = gotPort; if (connection_add(conn) < 0) { /* no space, forget it */ log_warn(LD_NET,"connection_add for listener failed. Giving up."); @@ -976,8 +994,9 @@ connection_create_listener(struct sockaddr *listensockaddr, socklen_t socklen, goto err; } - log_debug(LD_NET,"%s listening on port %u.", - conn_type_to_string(type), usePort); + log_fn(usePort==gotPort ? LOG_DEBUG : LOG_NOTICE, LD_NET, + "%s listening on port %u.", + conn_type_to_string(type), gotPort); conn->state = LISTENER_STATE_READY; if (start_reading) { @@ -1804,7 +1823,7 @@ retry_listeners(int type, config_line_t *cfg, case AF_INET: listensockaddr = (struct sockaddr *) create_inet_sockaddr(cfg_line->value, - (uint16_t) port_option, + port_option, &address, &listensocklen); break; case AF_UNIX: diff --git a/src/or/or.h b/src/or/or.h index d667358eb0..0a089f774e 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2353,6 +2353,10 @@ typedef struct config_line_t { typedef struct routerset_t routerset_t; +/** A magic value for the (Socks|OR|...)Port options below, telling Tor + * to pick its own port. */ +#define CFG_AUTO_PORT 0xc4005e + /** Configuration options for a Tor process. */ typedef struct { uint32_t _magic; From 61c06cbc66ecfaece20501417ef81b3f6f202391 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 2 May 2011 15:26:39 -0400 Subject: [PATCH 2/8] Teach retry_listener about "auto" ports. Otherwise, it will just immediately close any port declared with "auto" on the grounds that it wasn't configured. Now, it will allow "auto" to match any port. This means FWIW if you configure a socks port with SocksPort 9999 and then transition to SocksPort auto, the original socksport will not get closed and reopened. I'm considering this a feature. --- src/or/connection.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index 47e5423e0b..bc18dab1ec 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1772,10 +1772,23 @@ retry_listeners(int type, config_line_t *cfg, if (!parse_addr_port(LOG_WARN, wanted->value, &address, NULL, &port)) { int addr_matches = !strcasecmp(address, conn->address); + int port_matches; tor_free(address); - if (! port) - port = port_option; - if (port == conn->port && addr_matches) { + if (port) { + /* The Listener line has a port */ + port_matches = (port == conn->port); + } else if (port_option == CFG_AUTO_PORT) { + /* The Listener line has no port, and the Port line is "auto". + * "auto" matches anything; transitions from any port to + * "auto" succeed. */ + port_matches = 1; + } else { + /* The Listener line has no port, and the Port line is "auto". + * "auto" matches anything; transitions from any port to + * "auto" succeed. */ + port_matches = (port_option == conn->port); + } + if (port_matches && addr_matches) { line = wanted; break; } From 6f5998fd73849b98c9c1a017845c0669c32fda5d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 2 May 2011 15:31:49 -0400 Subject: [PATCH 3/8] Correct the signature for is_listening_on_low_port for "auto" ports --- src/or/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/or/config.c b/src/or/config.c index 055a4b8919..914c4cb3b8 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -2623,7 +2623,7 @@ options_init(or_options_t *options) * it is, or 0 if it isn't or the concept of a low port isn't applicable for * the platform we're on. */ static int -is_listening_on_low_port(uint16_t port_option, +is_listening_on_low_port(int port_option, const config_line_t *listen_options) { #ifdef MS_WINDOWS From 3da661b2422dbf5a098eb3b8ad9dd2033599016f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 2 May 2011 15:51:30 -0400 Subject: [PATCH 4/8] Advertise correct DirPort/ORPort when configured with "auto" We'll eventually want to do more work here to make sure that the ports are stable over multiple invocations. Otherwise, turning your node on and off will get you a new DirPort/ORPort needlessly. --- src/or/config.c | 2 +- src/or/dirserv.c | 6 +++--- src/or/router.c | 38 ++++++++++++++++++++++++++++++++++---- src/or/router.h | 3 +++ 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index 914c4cb3b8..b06c8c95c3 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -563,7 +563,7 @@ static int or_state_validate(or_state_t *old_options, or_state_t *options, static int or_state_load(void); static int options_init_logs(or_options_t *options, int validate_only); -static int is_listening_on_low_port(uint16_t port_option, +static int is_listening_on_low_port(int port_option, const config_line_t *listen_options); static uint64_t config_parse_memunit(const char *s, int *ok); diff --git a/src/or/dirserv.c b/src/or/dirserv.c index eadc2d7430..79b68cdac8 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -2699,8 +2699,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key, voter->sigs = smartlist_create(); voter->address = hostname; voter->addr = addr; - voter->dir_port = options->DirPort; - voter->or_port = options->ORPort; + voter->dir_port = router_get_advertised_dir_port(options); + voter->or_port = router_get_advertised_or_port(options); voter->contact = tor_strdup(contact); if (options->V3AuthUseLegacyKey) { authority_cert_t *c = get_my_v3_legacy_cert(); @@ -2806,7 +2806,7 @@ generate_v2_networkstatus_opinion(void) "dir-options%s%s%s%s\n" "%s" /* client version line, server version line. */ "dir-signing-key\n%s", - hostname, ipaddr, (int)options->DirPort, + hostname, ipaddr, (int)router_get_advertised_dir_port(options), fingerprint, contact, published, diff --git a/src/or/router.c b/src/or/router.c index a7148ea1f7..616a290d8a 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -704,8 +704,8 @@ init_keys(void) ds = router_get_trusteddirserver_by_digest(digest); if (!ds) { ds = add_trusted_dir_server(options->Nickname, NULL, - (uint16_t)options->DirPort, - (uint16_t)options->ORPort, + router_get_advertised_dir_port(options), + router_get_advertised_or_port(options), digest, v3_digest, type); @@ -1165,6 +1165,36 @@ consider_publishable_server(int force) } } +/** Return the port that we should advertise as our ORPort; this is either + * the one configured in the ORPort option, or the one we actually bound to + * if ORPort is "auto". */ +uint16_t +router_get_advertised_or_port(or_options_t *options) +{ + if (options->ORPort == CFG_AUTO_PORT) { + connection_t *c = connection_get_by_type(CONN_TYPE_OR_LISTENER); + if (c) + return c->port; + return 0; + } + return options->ORPort; +} + +/** Return the port that we should advertise as our DirPort; this is either + * the one configured in the DirPort option, or the one we actually bound to + * if DirPort is "auto". */ +uint16_t +router_get_advertised_dir_port(or_options_t *options) +{ + if (options->DirPort == CFG_AUTO_PORT) { + connection_t *c = connection_get_by_type(CONN_TYPE_DIR_LISTENER); + if (c) + return c->port; + return 0; + } + return options->DirPort; +} + /* * OR descriptor generation. */ @@ -1398,8 +1428,8 @@ router_rebuild_descriptor(int force) ri->address = tor_dup_ip(addr); ri->nickname = tor_strdup(options->Nickname); ri->addr = addr; - ri->or_port = options->ORPort; - ri->dir_port = options->DirPort; + ri->or_port = router_get_advertised_or_port(options); + ri->dir_port = router_get_advertised_dir_port(options); ri->cache_info.published_on = time(NULL); ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from * main thread */ diff --git a/src/or/router.h b/src/or/router.h index 5e021f6fed..1bf1a51ae1 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -50,6 +50,9 @@ int authdir_mode_publishes_statuses(or_options_t *options); int authdir_mode_tests_reachability(or_options_t *options); int authdir_mode_bridge(or_options_t *options); +uint16_t router_get_advertised_or_port(or_options_t *options); +uint16_t router_get_advertised_dir_port(or_options_t *options); + int server_mode(or_options_t *options); int public_server_mode(or_options_t *options); int advertised_server_mode(void); From 28cc7b01800e3e2a46a995b2ca18b0d2d5cdc804 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 2 May 2011 16:43:11 -0400 Subject: [PATCH 5/8] Add a new "tor_sockaddr_to_str()" function It does what it says on the tin. It turns out I'll want this in a couple of places. --- src/common/address.c | 30 ++++++++++++++++++++++++++++++ src/common/address.h | 1 + 2 files changed, 31 insertions(+) diff --git a/src/common/address.c b/src/common/address.c index 34e109fcf4..1c725393d9 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -43,6 +43,9 @@ #ifdef HAVE_SYS_PARAM_H #include /* FreeBSD needs this to know what version it is */ #endif +#ifdef HAVE_SYS_UN_H +#include +#endif #include #include #include @@ -120,6 +123,33 @@ tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa, return 0; } +/** Return a newly allocated string holding the address described in + * sa. AF_UNIX, AF_UNSPEC, AF_INET, and AF_INET6 are supported. */ +char * +tor_sockaddr_to_str(const struct sockaddr *sa) +{ + char address[TOR_ADDR_BUF_LEN]; + char *result; + tor_addr_t addr; + uint16_t port; +#ifdef HAVE_SYS_UN_H + if (sa->sa_family == AF_UNIX) { + struct sockaddr_un *s_un = (struct sockaddr_un *)sa; + tor_asprintf(&result, "unix:%s", s_un->sun_path); + return result; + } +#endif + if (sa->sa_family == AF_UNSPEC) + return tor_strdup("unspec"); + + if (tor_addr_from_sockaddr(&addr, sa, &port) < 0) + return NULL; + if (! tor_addr_to_str(address, &addr, sizeof(address), 1)) + return NULL; + tor_asprintf(&result, "%s:%d", address, (int)port); + return result; +} + /** Set address a to the unspecified address. This address belongs to * no family. */ void diff --git a/src/common/address.h b/src/common/address.h index d05a3de2e7..9a7656f69b 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -44,6 +44,7 @@ socklen_t tor_addr_to_sockaddr(const tor_addr_t *a, uint16_t port, int tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa, uint16_t *port_out); void tor_addr_make_unspec(tor_addr_t *a); +char *tor_sockaddr_to_str(const struct sockaddr *sa); /** Return an in6_addr* equivalent to a, or NULL if a is not * an IPv6 address. */ From c55c8f0d4926481ea2cf49e14915688de2eb4938 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 2 May 2011 16:44:37 -0400 Subject: [PATCH 6/8] new GETINFO command to return list of listeners of a given type --- changes/feature3076 | 7 ++++-- src/or/control.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/changes/feature3076 b/changes/feature3076 index 582285070e..aadd296274 100644 --- a/changes/feature3076 +++ b/changes/feature3076 @@ -1,5 +1,8 @@ o Minor features - The options SocksPort, ControlPort, and so on now all accept an optional value "auto" that opens a socket on an OS-selected port. - - + o Minor features (controller) + - GETINFO net/listeners/(type) now returns a list of the addresses + and ports that are bound for listeners for a given connection + type. This is useful for if the user has selected SocksPort + "auto", and you need to know which port got chosen. diff --git a/src/or/control.c b/src/or/control.c index c5362424ad..19e8539a20 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1407,6 +1407,63 @@ munge_extrainfo_into_routerinfo(const char *ri_body, signed_descriptor_t *ri, return tor_strndup(ri_body, ri->signed_descriptor_len); } +/** Implementation helper for GETINFO: answers requests for information about + * which ports are bound. */ +static int +getinfo_helper_listeners(control_connection_t *control_conn, + const char *question, + char **answer, const char **errmsg) +{ + int type; + smartlist_t *res; + + (void)control_conn; + (void)errmsg; + + if (!strcmp(question, "net/listeners/or")) + type = CONN_TYPE_OR_LISTENER; + else if (!strcmp(question, "net/listeners/dir")) + type = CONN_TYPE_DIR_LISTENER; + else if (!strcmp(question, "net/listeners/socks")) + type = CONN_TYPE_AP_LISTENER; + else if (!strcmp(question, "net/listeners/trans")) + type = CONN_TYPE_AP_TRANS_LISTENER; + else if (!strcmp(question, "net/listeners/natd")) + type = CONN_TYPE_AP_NATD_LISTENER; + else if (!strcmp(question, "net/listeners/dns")) + type = CONN_TYPE_AP_DNS_LISTENER; + else if (!strcmp(question, "net/listeners/control")) + type = CONN_TYPE_CONTROL_LISTENER; + else + return 0; /* unknown key */ + + res = smartlist_create(); + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + char *addr; + struct sockaddr_storage ss; + socklen_t ss_len = sizeof(ss); + + if (conn->type != type || conn->marked_for_close || conn->s < 0) + continue; + + if (getsockname(conn->s, (struct sockaddr *)&ss, &ss_len) < 0) { + tor_asprintf(&addr, "%s:%d", conn->address, (int)conn->port); + } else { + char *tmp = tor_sockaddr_to_str((struct sockaddr *)&ss); + addr = esc_for_log(tmp); + tor_free(tmp); + } + if (addr) + smartlist_add(res, addr); + } SMARTLIST_FOREACH_END(conn); + + *answer = smartlist_join_strings(res, " ", 0, NULL); + + SMARTLIST_FOREACH(res, char *, cp, tor_free(cp)); + smartlist_free(res); + return 0; +} + /** Implementation helper for GETINFO: knows the answers for questions about * directory information. */ static int @@ -1861,6 +1918,7 @@ static const getinfo_item_t getinfo_items[] = { "All non-expired, non-superseded router descriptors."), ITEM("desc/all-recent-extrainfo-hack", dir, NULL), /* Hack. */ PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."), + PREFIX("net/listeners/", listeners, "Bound addresses by type"), ITEM("ns/all", networkstatus, "Brief summary of router status (v2 directory format)"), PREFIX("ns/id/", networkstatus, From dad12188a6ca957f6fde1eb602fd98b2fa93b1a4 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 9 May 2011 12:13:37 -0400 Subject: [PATCH 7/8] Write automatically-chosen control ports to a file. --- changes/feature3076 | 3 +++ doc/tor.1.txt | 5 +++++ src/or/config.c | 1 + src/or/connection.c | 3 +++ src/or/control.c | 39 +++++++++++++++++++++++++++++++++++++++ src/or/control.h | 2 ++ src/or/main.c | 6 ++++-- src/or/or.h | 3 +++ 8 files changed, 60 insertions(+), 2 deletions(-) diff --git a/changes/feature3076 b/changes/feature3076 index aadd296274..ed42e4595b 100644 --- a/changes/feature3076 +++ b/changes/feature3076 @@ -6,3 +6,6 @@ and ports that are bound for listeners for a given connection type. This is useful for if the user has selected SocksPort "auto", and you need to know which port got chosen. + - There is a ControlPortWriteToFile option that tells Tor to write + its actual control port or ports to a chosen file. + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index e48342e165..606580db55 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -191,6 +191,11 @@ Other options can be specified either on the command-line (--option the default GID. [Making the file readable by other groups is not yet implemented; let us know if you need this for some reason.] (Default: 0). +**ControlPortWriteToFile** __Path__:: + If set, Tor writes the address and port of any control port it opens to + this address. Usable by controllers to learn the actual control port + when ControlPort is set to "auto". + **DataDirectory** __DIR__:: Store working data in DIR (Default: @LOCALSTATEDIR@/lib/tor) diff --git a/src/or/config.c b/src/or/config.c index b06c8c95c3..5eb62291bc 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -206,6 +206,7 @@ static config_var_t _option_vars[] = { V(ContactInfo, STRING, NULL), V(ControlListenAddress, LINELIST, NULL), V(ControlPort, PORT, "0"), + V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), diff --git a/src/or/connection.c b/src/or/connection.c index bc18dab1ec..01b533d9b5 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -998,6 +998,9 @@ connection_create_listener(const struct sockaddr *listensockaddr, "%s listening on port %u.", conn_type_to_string(type), gotPort); + if (type == CONN_TYPE_CONTROL_LISTENER) + control_ports_write_to_file(); + conn->state = LISTENER_STATE_READY; if (start_reading) { connection_start_reading(conn); diff --git a/src/or/control.c b/src/or/control.c index 19e8539a20..634674233c 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -508,6 +508,45 @@ connection_printf_to_buf(control_connection_t *conn, const char *format, ...) connection_write_to_buf(buf, len, TO_CONN(conn)); } +/** Write all of the open control ports to ControlPortWriteToFile */ +void +control_ports_write_to_file(void) +{ + smartlist_t *lines; + char *joined = NULL; + or_options_t *options = get_options(); + + if (!options->ControlPortWriteToFile) + return; + + lines = smartlist_create(); + + SMARTLIST_FOREACH_BEGIN(get_connection_array(), const connection_t *, conn) { + char *port_str = NULL; + if (conn->type != CONN_TYPE_CONTROL_LISTENER || conn->marked_for_close) + continue; +#ifdef AF_UNIX + if (conn->socket_family == AF_UNIX) { + tor_asprintf(&port_str, "UNIX_PORT=%s\n", conn->address); + smartlist_add(lines, port_str); + continue; + } +#endif + tor_asprintf(&port_str, "PORT=%s:%d\n", conn->address, conn->port); + smartlist_add(lines, port_str); + } SMARTLIST_FOREACH_END(conn); + + joined = smartlist_join_strings(lines, "", 0, NULL); + + if (write_str_to_file(options->ControlPortWriteToFile, joined, 0) < 0) { + log_warn(LD_CONTROL, "Writing %s failed: %s", + options->ControlPortWriteToFile, strerror(errno)); + } + tor_free(joined); + SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp)); + smartlist_free(lines); +} + /** Send a "DONE" message down the control connection conn. */ static void send_control_done(control_connection_t *conn) diff --git a/src/or/control.h b/src/or/control.h index 2ae96b4b8a..a73ed5d3c1 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -15,6 +15,8 @@ void control_update_global_event_mask(void); void control_adjust_event_log_severity(void); +void control_ports_write_to_file(void); + /** Log information about the connection conn, protecting it as with * CONN_LOG_PROTECT. Example: * diff --git a/src/or/main.c b/src/or/main.c index 7bae59ce06..15682d5400 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -2046,12 +2046,14 @@ void tor_cleanup(void) { or_options_t *options = get_options(); - /* Remove our pid file. We don't care if there was an error when we - * unlink, nothing we could do about it anyways. */ if (options->command == CMD_RUN_TOR) { time_t now = time(NULL); + /* Remove our pid file. We don't care if there was an error when we + * unlink, nothing we could do about it anyways. */ if (options->PidFile) unlink(options->PidFile); + if (options->ControlPortWriteToFile) + unlink(options->ControlPortWriteToFile); if (accounting_is_enabled(options)) accounting_record_bandwidth_usage(now, get_or_state()); or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */ diff --git a/src/or/or.h b/src/or/or.h index 0a089f774e..412aac9822 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2874,6 +2874,9 @@ typedef struct { * the defaults have changed. */ int _UsingTestNetworkDefaults; + /** File where we should write the ControlPort. */ + char *ControlPortWriteToFile; + } or_options_t; /** Persistent state for an onion router, as saved to disk. */ From 7f654a6a6fb5e956b996eece36ff95e590a6ad63 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 12 May 2011 19:17:48 -0400 Subject: [PATCH 8/8] Add a ControlPortFileGroupWritable option --- changes/feature3076 | 5 ++++- doc/tor.1.txt | 5 +++++ src/or/config.c | 1 + src/or/control.c | 8 ++++++++ src/or/or.h | 2 ++ 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/changes/feature3076 b/changes/feature3076 index ed42e4595b..a3dcec8741 100644 --- a/changes/feature3076 +++ b/changes/feature3076 @@ -7,5 +7,8 @@ type. This is useful for if the user has selected SocksPort "auto", and you need to know which port got chosen. - There is a ControlPortWriteToFile option that tells Tor to write - its actual control port or ports to a chosen file. + its actual control port or ports to a chosen file. If the option + ControlPortFileGroupReadable is set, the file is created as + group-readable. + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 606580db55..d95d764c67 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -196,6 +196,11 @@ Other options can be specified either on the command-line (--option this address. Usable by controllers to learn the actual control port when ControlPort is set to "auto". +**ControlPortFileGroupReadable** **0**|**1**:: + If this option is set to 0, don't allow the filesystem group to read the + control port file. If the option is set to 1, make the control port + file readable by the default GID. (Default: 0). + **DataDirectory** __DIR__:: Store working data in DIR (Default: @LOCALSTATEDIR@/lib/tor) diff --git a/src/or/config.c b/src/or/config.c index 5eb62291bc..a7ff28f462 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -206,6 +206,7 @@ static config_var_t _option_vars[] = { V(ContactInfo, STRING, NULL), V(ControlListenAddress, LINELIST, NULL), V(ControlPort, PORT, "0"), + V(ControlPortFileGroupReadable,BOOL, "0"), V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), V(CookieAuthentication, BOOL, "0"), diff --git a/src/or/control.c b/src/or/control.c index 634674233c..384e579f93 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -542,6 +542,14 @@ control_ports_write_to_file(void) log_warn(LD_CONTROL, "Writing %s failed: %s", options->ControlPortWriteToFile, strerror(errno)); } +#ifndef MS_WINDOWS + if (options->ControlPortFileGroupReadable) { + if (chmod(options->ControlPortWriteToFile, 0640)) { + log_warn(LD_FS,"Unable to make %s group-readable.", + options->ControlPortWriteToFile); + } + } +#endif tor_free(joined); SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp)); smartlist_free(lines); diff --git a/src/or/or.h b/src/or/or.h index 412aac9822..a73d98ab74 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2876,6 +2876,8 @@ typedef struct { /** File where we should write the ControlPort. */ char *ControlPortWriteToFile; + /** Should that file be group-readable? */ + int ControlPortFileGroupReadable; } or_options_t;