diff options
author | Nick Mathewson <nickm@torproject.org> | 2011-05-13 10:43:41 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2011-05-13 10:43:41 -0400 |
commit | 5f2a1a7b4f20be121cd30def95cf7789924ca70a (patch) | |
tree | c9545f5af482e1380261a2fe7ba2e73b175b3b66 | |
parent | e0d5a6e1849673589c5b7f04d89e25194167344d (diff) | |
parent | 7f654a6a6fb5e956b996eece36ff95e590a6ad63 (diff) | |
download | tor-5f2a1a7b4f20be121cd30def95cf7789924ca70a.tar tor-5f2a1a7b4f20be121cd30def95cf7789924ca70a.tar.gz |
Merge branch 'feature3076_squashed' into maint-0.2.2
-rw-r--r-- | changes/feature3076 | 14 | ||||
-rw-r--r-- | doc/tor.1.txt | 47 | ||||
-rw-r--r-- | src/common/address.c | 30 | ||||
-rw-r--r-- | src/common/address.h | 1 | ||||
-rw-r--r-- | src/or/config.c | 61 | ||||
-rw-r--r-- | src/or/connection.c | 67 | ||||
-rw-r--r-- | src/or/control.c | 105 | ||||
-rw-r--r-- | src/or/control.h | 2 | ||||
-rw-r--r-- | src/or/dirserv.c | 6 | ||||
-rw-r--r-- | src/or/main.c | 6 | ||||
-rw-r--r-- | src/or/or.h | 9 | ||||
-rw-r--r-- | src/or/router.c | 38 | ||||
-rw-r--r-- | src/or/router.h | 3 |
13 files changed, 319 insertions, 70 deletions
diff --git a/changes/feature3076 b/changes/feature3076 new file mode 100644 index 000000000..a3dcec874 --- /dev/null +++ b/changes/feature3076 @@ -0,0 +1,14 @@ + 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. + - There is a ControlPortWriteToFile option that tells Tor to write + 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 fb4b3f9bf..d95d764c6 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 @@ -189,6 +191,16 @@ 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". + +**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) @@ -647,10 +659,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 +772,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 +806,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 +961,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 +1176,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/common/address.c b/src/common/address.c index 34e109fcf..1c725393d 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -43,6 +43,9 @@ #ifdef HAVE_SYS_PARAM_H #include <sys/param.h> /* FreeBSD needs this to know what version it is */ #endif +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -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 + * <b>sa</b>. 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 <b>a</b> to the unspecified address. This address belongs to * no family. */ void diff --git a/src/common/address.h b/src/common/address.h index d05a3de2e..9a7656f69 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 <b>a</b>, or NULL if <b>a</b> is not * an IPv6 address. */ diff --git a/src/or/config.c b/src/or/config.c index 9f94ef1af..a7ff28f46 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,9 @@ 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(ControlPortFileGroupReadable,BOOL, "0"), + V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), @@ -215,7 +219,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 +229,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 +308,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 +316,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 +359,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 +370,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"), @@ -561,7 +565,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); @@ -1689,8 +1693,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 +2010,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 +2245,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; @@ -2606,7 +2625,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 @@ -2851,9 +2870,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 +2984,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 +2992,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 +5236,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 eaae791ef..01b533d9b 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,12 @@ 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); + + if (type == CONN_TYPE_CONTROL_LISTENER) + control_ports_write_to_file(); conn->state = LISTENER_STATE_READY; if (start_reading) { @@ -1753,10 +1775,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; } @@ -1804,7 +1839,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/control.c b/src/or/control.c index c5362424a..384e579f9 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -508,6 +508,53 @@ 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)); + } +#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); +} + /** Send a "DONE" message down the control connection <b>conn</b>. */ static void send_control_done(control_connection_t *conn) @@ -1407,6 +1454,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 +1965,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, diff --git a/src/or/control.h b/src/or/control.h index 2ae96b4b8..a73ed5d3c 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 <b>conn</b>, protecting it as with * CONN_LOG_PROTECT. Example: * diff --git a/src/or/dirserv.c b/src/or/dirserv.c index eadc2d743..79b68cdac 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/main.c b/src/or/main.c index 7bae59ce0..15682d540 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 d667358eb..a73d98ab7 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; @@ -2870,6 +2874,11 @@ typedef struct { * the defaults have changed. */ int _UsingTestNetworkDefaults; + /** File where we should write the ControlPort. */ + char *ControlPortWriteToFile; + /** Should that file be group-readable? */ + int ControlPortFileGroupReadable; + } or_options_t; /** Persistent state for an onion router, as saved to disk. */ diff --git a/src/or/router.c b/src/or/router.c index a7148ea1f..616a290d8 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 5e021f6fe..1bf1a51ae 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); |