Selaa lähdekoodia

Merge remote-tracking branch 'origin/maint-0.2.2'

Conflicts:
	src/or/config.c
	src/or/dirserv.c
	src/or/or.h
Nick Mathewson 13 vuotta sitten
vanhempi
commit
600744b4be
13 muutettua tiedostoa jossa 320 lisäystä ja 70 poistoa
  1. 14 0
      changes/feature3076
  2. 33 14
      doc/tor.1.txt
  3. 30 0
      src/common/address.c
  4. 1 0
      src/common/address.h
  5. 30 31
      src/or/config.c
  6. 51 16
      src/or/connection.c
  7. 105 0
      src/or/control.c
  8. 2 0
      src/or/control.h
  9. 4 3
      src/or/dirserv.c
  10. 4 2
      src/or/main.c
  11. 9 0
      src/or/or.h
  12. 34 4
      src/or/router.c
  13. 3 0
      src/or/router.h

+ 14 - 0
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.
+
+

+ 33 - 14
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)
 
@@ -665,10 +677,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
@@ -777,23 +790,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).
@@ -809,9 +824,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).
@@ -965,8 +981,10 @@ is non-zero):
     parallelizable operations.  If this is set to 0, Tor will try to detect
     how many CPUs you have, defaulting to 1 if it can't tell.  (Default: 0)
 
-**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
@@ -1199,8 +1217,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

+ 30 - 0
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

+ 1 - 0
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. */

+ 30 - 31
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_MSEC_INTERVAL,/**< A number of milliseconds, with optional
                               * units */
@@ -208,7 +210,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"),
@@ -221,7 +225,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"),
@@ -232,7 +236,7 @@ static config_var_t _option_vars[] = {
   VAR("DirServer",               LINELIST, DirServers, NULL),
   V(DisableAllSwap,              BOOL,     "0"),
   V(DisableIOCP,                 BOOL,     "1"),
-  V(DNSPort,                     UINT,     "0"),
+  V(DNSPort,                     PORT,     "0"),
   V(DNSListenAddress,            LINELIST, NULL),
   V(DownloadExtraInfo,           BOOL,     "0"),
   V(EnforceDistinctSubnets,      BOOL,     "1"),
@@ -313,7 +317,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"),
@@ -321,7 +325,7 @@ static config_var_t _option_vars[] = {
   V(NumCPUs,                     UINT,     "0"),
   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"),
@@ -366,7 +370,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"),
@@ -377,7 +381,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"),
@@ -576,7 +580,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);
@@ -1723,8 +1727,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.",
@@ -2058,6 +2070,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_MSEC_INTERVAL:
     case CONFIG_TYPE_UINT:
@@ -2297,6 +2315,7 @@ option_clear(config_format_t *fmt, or_options_t *options, config_var_t *var)
     case CONFIG_TYPE_INTERVAL:
     case CONFIG_TYPE_MSEC_INTERVAL:
     case CONFIG_TYPE_UINT:
+    case CONFIG_TYPE_PORT:
     case CONFIG_TYPE_BOOL:
       *(int*)lvalue = 0;
       break;
@@ -2677,7 +2696,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
@@ -2926,9 +2945,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") ||
@@ -3037,18 +3053,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)
@@ -3057,12 +3061,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.");
@@ -5415,6 +5413,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_MSEC_INTERVAL: type = "TimeMsecInterval"; break;
         case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break;

+ 51 - 16
src/or/connection.c

@@ -41,7 +41,7 @@
 #endif
 
 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,
@@ -802,7 +802,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;
@@ -814,8 +814,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);
@@ -901,12 +903,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) {
@@ -915,6 +918,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;
@@ -922,11 +926,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,
@@ -964,6 +967,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;
@@ -1011,7 +1029,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.");
@@ -1019,8 +1037,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) {
@@ -1809,10 +1831,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;
               }
@@ -1860,7 +1895,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:

+ 105 - 0
src/or/control.c

@@ -515,6 +515,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)
@@ -1472,6 +1519,63 @@ munge_extrainfo_into_routerinfo(const char *ri_body,
   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
@@ -1927,6 +2031,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,

+ 2 - 0
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:
  *

+ 4 - 3
src/or/dirserv.c

@@ -2726,8 +2726,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();
@@ -2829,7 +2829,8 @@ 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, fmt_addr32(addr), (int)options->DirPort,
+               hostname, fmt_addr32(addr),
+               (int)router_get_advertised_dir_port(options),
                fingerprint,
                contact,
                published,

+ 4 - 2
src/or/main.c

@@ -2283,12 +2283,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. */

+ 9 - 0
src/or/or.h

@@ -2518,6 +2518,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;
@@ -3057,6 +3061,11 @@ typedef struct {
    * If -1, Tor decides. */
   int UseMicrodescriptors;
 
+  /** 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. */

+ 34 - 4
src/or/router.c

@@ -707,8 +707,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);
@@ -1171,6 +1171,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.
  */
@@ -1400,8 +1430,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 */

+ 3 - 0
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);