Browse Source

"(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.
Nick Mathewson 14 years ago
parent
commit
5fec8fe559
5 changed files with 90 additions and 56 deletions
  1. 5 0
      changes/feature3076
  2. 23 14
      doc/tor.1.txt
  3. 26 29
      src/or/config.c
  4. 32 13
      src/or/connection.c
  5. 4 0
      src/or/or.h

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

+ 23 - 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
     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.
     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
     If set, Tor will accept connections on this port and allow those
     connections to control the Tor process using the Tor Control Protocol
     connections to control the Tor process using the Tor Control Protocol
     (described in control-spec.txt). Note: unless you also specify one of
     (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
     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.
     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__]::
 **ControlListenAddress** __IP__[:__PORT__]::
     Bind the controller listener to this address. If you specify a port, bind
     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 circuit. Currently, two addresses are "too close" if they lie in
     the same /16 range. (Default: 1)
     the same /16 range. (Default: 1)
 
 
-**SocksPort** __PORT__::
+**SocksPort** __PORT__|**auto**::
     Advertise this port to listen for connections from Socks-speaking
     Advertise this port to listen for connections from Socks-speaking
     applications. Set this to 0 if you don't want to allow application
     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__]::
 **SocksListenAddress** __IP__[:__PORT__]::
     Bind to this address to listen for connections from Socks-speaking
     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
     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)
     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,
     If non-zero, enables transparent proxy support on __PORT__ (by convention,
     9040). Requires OS support for transparent proxies, such as BSDs' pf or
     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
     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
     a network, you'll want to examine and change VirtualAddrNetwork from the
     default setting. You'll also want to set the TransListenAddress option for
     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__]::
 **TransListenAddress** __IP__[:__PORT__]::
     Bind to this address to listen for transparent proxy connections. (Default:
     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
     127.0.0.1). This is useful for exporting a transparent proxy server to an
     entire network.
     entire network.
 
 
-**NATDPort** __PORT__::
+**NATDPort** __PORT__|**auto**::
     Allow old versions of ipfw (as included in old versions of FreeBSD, etc.)
     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
     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__]::
 **NATDListenAddress** __IP__[:__PORT__]::
     Bind to this address to listen for NATD connections. (Default: 127.0.0.1).
     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**.
     A comma-separated list of suffixes to use with **AutomapHostsOnResolve**.
     The "." suffix is equivalent to "all addresses." (Default: .exit,.onion).
     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
     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__]::
 **DNSListenAddress** __IP__[:__PORT__]::
     Bind to this address to listen for DNS connections. (Default: 127.0.0.1).
     Bind to this address to listen for DNS connections. (Default: 127.0.0.1).
@@ -945,8 +951,10 @@ is non-zero):
 **NumCPUs** __num__::
 **NumCPUs** __num__::
     How many processes to use at once for decrypting onionskins. (Default: 1)
     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__]::
 **ORListenAddress** __IP__[:__PORT__]::
     Bind to this IP address to listen for connections from Tor clients and
     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
     Minimum uptime of a v2 hidden service directory to be accepted as such by
     authoritative directories. (Default: 24 hours)
     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__]::
 **DirListenAddress** __IP__[:__PORT__]::
     Bind the directory service to this address. If you specify a port, bind to
     Bind the directory service to this address. If you specify a port, bind to

+ 26 - 29
src/or/config.c

@@ -43,6 +43,8 @@ typedef enum config_type_t {
   CONFIG_TYPE_STRING = 0,   /**< An arbitrary string. */
   CONFIG_TYPE_STRING = 0,   /**< An arbitrary string. */
   CONFIG_TYPE_FILENAME,     /**< A filename: some prefixes get expanded. */
   CONFIG_TYPE_FILENAME,     /**< A filename: some prefixes get expanded. */
   CONFIG_TYPE_UINT,         /**< A non-negative integer less than MAX_INT */
   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_INTERVAL,     /**< A number of seconds, with optional units*/
   CONFIG_TYPE_MEMUNIT,      /**< A number of bytes, with optional units*/
   CONFIG_TYPE_MEMUNIT,      /**< A number of bytes, with optional units*/
   CONFIG_TYPE_DOUBLE,       /**< A floating-point value */
   CONFIG_TYPE_DOUBLE,       /**< A floating-point value */
@@ -203,7 +205,7 @@ static config_var_t _option_vars[] = {
   V(ConstrainedSockSize,         MEMUNIT,  "8192"),
   V(ConstrainedSockSize,         MEMUNIT,  "8192"),
   V(ContactInfo,                 STRING,   NULL),
   V(ContactInfo,                 STRING,   NULL),
   V(ControlListenAddress,        LINELIST, NULL),
   V(ControlListenAddress,        LINELIST, NULL),
-  V(ControlPort,                 UINT,     "0"),
+  V(ControlPort,                 PORT,     "0"),
   V(ControlSocket,               LINELIST, NULL),
   V(ControlSocket,               LINELIST, NULL),
   V(CookieAuthentication,        BOOL,     "0"),
   V(CookieAuthentication,        BOOL,     "0"),
   V(CookieAuthFileGroupReadable, BOOL,     "0"),
   V(CookieAuthFileGroupReadable, BOOL,     "0"),
@@ -215,7 +217,7 @@ static config_var_t _option_vars[] = {
   V(DirListenAddress,            LINELIST, NULL),
   V(DirListenAddress,            LINELIST, NULL),
   OBSOLETE("DirFetchPeriod"),
   OBSOLETE("DirFetchPeriod"),
   V(DirPolicy,                   LINELIST, NULL),
   V(DirPolicy,                   LINELIST, NULL),
-  V(DirPort,                     UINT,     "0"),
+  V(DirPort,                     PORT,     "0"),
   V(DirPortFrontPage,            FILENAME, NULL),
   V(DirPortFrontPage,            FILENAME, NULL),
   OBSOLETE("DirPostPeriod"),
   OBSOLETE("DirPostPeriod"),
   OBSOLETE("DirRecordUsageByCountry"),
   OBSOLETE("DirRecordUsageByCountry"),
@@ -225,7 +227,7 @@ static config_var_t _option_vars[] = {
   V(DirReqStatistics,            BOOL,     "0"),
   V(DirReqStatistics,            BOOL,     "0"),
   VAR("DirServer",               LINELIST, DirServers, NULL),
   VAR("DirServer",               LINELIST, DirServers, NULL),
   V(DisableAllSwap,              BOOL,     "0"),
   V(DisableAllSwap,              BOOL,     "0"),
-  V(DNSPort,                     UINT,     "0"),
+  V(DNSPort,                     PORT,     "0"),
   V(DNSListenAddress,            LINELIST, NULL),
   V(DNSListenAddress,            LINELIST, NULL),
   V(DownloadExtraInfo,           BOOL,     "0"),
   V(DownloadExtraInfo,           BOOL,     "0"),
   V(EnforceDistinctSubnets,      BOOL,     "1"),
   V(EnforceDistinctSubnets,      BOOL,     "1"),
@@ -304,7 +306,7 @@ static config_var_t _option_vars[] = {
   V(NewCircuitPeriod,            INTERVAL, "30 seconds"),
   V(NewCircuitPeriod,            INTERVAL, "30 seconds"),
   VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"),
   VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"),
   V(NATDListenAddress,           LINELIST, NULL),
   V(NATDListenAddress,           LINELIST, NULL),
-  V(NATDPort,                    UINT,     "0"),
+  V(NATDPort,                    PORT,     "0"),
   V(Nickname,                    STRING,   NULL),
   V(Nickname,                    STRING,   NULL),
   V(WarnUnsafeSocks,              BOOL,     "1"),
   V(WarnUnsafeSocks,              BOOL,     "1"),
   OBSOLETE("NoPublish"),
   OBSOLETE("NoPublish"),
@@ -312,7 +314,7 @@ static config_var_t _option_vars[] = {
   V(NumCPUs,                     UINT,     "1"),
   V(NumCPUs,                     UINT,     "1"),
   V(NumEntryGuards,              UINT,     "3"),
   V(NumEntryGuards,              UINT,     "3"),
   V(ORListenAddress,             LINELIST, NULL),
   V(ORListenAddress,             LINELIST, NULL),
-  V(ORPort,                      UINT,     "0"),
+  V(ORPort,                      PORT,     "0"),
   V(OutboundBindAddress,         STRING,   NULL),
   V(OutboundBindAddress,         STRING,   NULL),
   OBSOLETE("PathlenCoinWeight"),
   OBSOLETE("PathlenCoinWeight"),
   V(PerConnBWBurst,              MEMUNIT,  "0"),
   V(PerConnBWBurst,              MEMUNIT,  "0"),
@@ -355,7 +357,7 @@ static config_var_t _option_vars[] = {
   V(ShutdownWaitLength,          INTERVAL, "30 seconds"),
   V(ShutdownWaitLength,          INTERVAL, "30 seconds"),
   V(SocksListenAddress,          LINELIST, NULL),
   V(SocksListenAddress,          LINELIST, NULL),
   V(SocksPolicy,                 LINELIST, NULL),
   V(SocksPolicy,                 LINELIST, NULL),
-  V(SocksPort,                   UINT,     "9050"),
+  V(SocksPort,                   PORT,     "9050"),
   V(SocksTimeout,                INTERVAL, "2 minutes"),
   V(SocksTimeout,                INTERVAL, "2 minutes"),
   OBSOLETE("StatusFetchPeriod"),
   OBSOLETE("StatusFetchPeriod"),
   V(StrictNodes,                 BOOL,     "0"),
   V(StrictNodes,                 BOOL,     "0"),
@@ -366,7 +368,7 @@ static config_var_t _option_vars[] = {
   V(TrackHostExitsExpire,        INTERVAL, "30 minutes"),
   V(TrackHostExitsExpire,        INTERVAL, "30 minutes"),
   OBSOLETE("TrafficShaping"),
   OBSOLETE("TrafficShaping"),
   V(TransListenAddress,          LINELIST, NULL),
   V(TransListenAddress,          LINELIST, NULL),
-  V(TransPort,                   UINT,     "0"),
+  V(TransPort,                   PORT,     "0"),
   V(TunnelDirConns,              BOOL,     "1"),
   V(TunnelDirConns,              BOOL,     "1"),
   V(UpdateBridgesFromAuthority,  BOOL,     "0"),
   V(UpdateBridgesFromAuthority,  BOOL,     "0"),
   V(UseBridges,                  BOOL,     "0"),
   V(UseBridges,                  BOOL,     "0"),
@@ -1689,8 +1691,16 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
 
 
   switch (var->type) {
   switch (var->type) {
 
 
+  case CONFIG_TYPE_PORT:
+    if (!strcasecmp(c->value, "auto")) {
+      *(int *)lvalue = CFG_AUTO_PORT;
+      break;
+    }
+    /* fall through */
   case CONFIG_TYPE_UINT:
   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) {
     if (!ok) {
       tor_asprintf(msg,
       tor_asprintf(msg,
           "Int keyword '%s %s' is malformed or out of bounds.",
           "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. */
       escape_val = 0; /* Can't need escape. */
       break;
       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_INTERVAL:
     case CONFIG_TYPE_UINT:
     case CONFIG_TYPE_UINT:
       /* This means every or_options_t uint or bool element
       /* 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;
       break;
     case CONFIG_TYPE_INTERVAL:
     case CONFIG_TYPE_INTERVAL:
     case CONFIG_TYPE_UINT:
     case CONFIG_TYPE_UINT:
+    case CONFIG_TYPE_PORT:
     case CONFIG_TYPE_BOOL:
     case CONFIG_TYPE_BOOL:
       *(int*)lvalue = 0;
       *(int*)lvalue = 0;
       break;
       break;
@@ -2851,9 +2868,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
   tor_assert(msg);
   tor_assert(msg);
   *msg = NULL;
   *msg = NULL;
 
 
-  if (options->ORPort < 0 || options->ORPort > 65535)
-    REJECT("ORPort option out of bounds.");
-
   if (server_mode(options) &&
   if (server_mode(options) &&
       (!strcmpstart(uname, "Windows 95") ||
       (!strcmpstart(uname, "Windows 95") ||
        !strcmpstart(uname, "Windows 98") ||
        !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.");
     REJECT("Can't use a relative path to torrc when RunAsDaemon is set.");
 #endif
 #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 &&
   if (options->SocksPort == 0 && options->TransPort == 0 &&
       options->NATDPort == 0 && options->ORPort == 0 &&
       options->NATDPort == 0 && options->ORPort == 0 &&
       options->DNSPort == 0 && !options->RendConfigLines)
       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.  "
         "undefined, and there aren't any hidden services configured.  "
         "Tor will still run, but probably won't do anything.");
         "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
 #ifndef USE_TRANSPARENT
   if (options->TransPort || options->TransListenAddress)
   if (options->TransPort || options->TransListenAddress)
     REJECT("TransPort and TransListenAddress are disabled in this build.");
     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_STRING: type = "String"; break;
         case CONFIG_TYPE_FILENAME: type = "Filename"; break;
         case CONFIG_TYPE_FILENAME: type = "Filename"; break;
         case CONFIG_TYPE_UINT: type = "Integer"; 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_INTERVAL: type = "TimeInterval"; break;
         case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break;
         case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break;
         case CONFIG_TYPE_DOUBLE: type = "Float"; break;
         case CONFIG_TYPE_DOUBLE: type = "Float"; break;

+ 32 - 13
src/or/connection.c

@@ -37,7 +37,7 @@
 #include "routerparse.h"
 #include "routerparse.h"
 
 
 static connection_t *connection_create_listener(
 static connection_t *connection_create_listener(
-                               struct sockaddr *listensockaddr,
+                               const struct sockaddr *listensockaddr,
                                socklen_t listensocklen, int type,
                                socklen_t listensocklen, int type,
                                char* address);
                                char* address);
 static void connection_init(time_t now, connection_t *conn, int type,
 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.
  * The listenaddr struct has to be freed by the caller.
  */
  */
 static struct sockaddr_in *
 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) {
                      char **readable_address, socklen_t *socklen_out) {
   struct sockaddr_in *listenaddr = NULL;
   struct sockaddr_in *listenaddr = NULL;
   uint32_t addr;
   uint32_t addr;
@@ -771,8 +771,10 @@ create_inet_sockaddr(const char *listenaddress, uint16_t listenport,
              "Error parsing/resolving ListenAddress %s", listenaddress);
              "Error parsing/resolving ListenAddress %s", listenaddress);
     goto err;
     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 = tor_malloc_zero(sizeof(struct sockaddr_in));
   listenaddr->sin_addr.s_addr = htonl(addr);
   listenaddr->sin_addr.s_addr = htonl(addr);
@@ -858,12 +860,13 @@ warn_too_many_conns(void)
  * to the conn.
  * to the conn.
  */
  */
 static connection_t *
 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)
                            int type, char* address)
 {
 {
   connection_t *conn;
   connection_t *conn;
   int s; /* the socket we're going to make */
   int s; /* the socket we're going to make */
-  uint16_t usePort = 0;
+  uint16_t usePort = 0, gotPort = 0;
   int start_reading = 0;
   int start_reading = 0;
 
 
   if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
   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) {
   if (listensockaddr->sa_family == AF_INET) {
+    tor_addr_t addr;
     int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
     int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
 #ifndef MS_WINDOWS
 #ifndef MS_WINDOWS
     int one=1;
     int one=1;
@@ -879,11 +883,10 @@ connection_create_listener(struct sockaddr *listensockaddr, socklen_t socklen,
     if (is_tcp)
     if (is_tcp)
       start_reading = 1;
       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",
     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,
     s = tor_open_socket(PF_INET,
                         is_tcp ? SOCK_STREAM : SOCK_DGRAM,
                         is_tcp ? SOCK_STREAM : SOCK_DGRAM,
@@ -921,6 +924,21 @@ connection_create_listener(struct sockaddr *listensockaddr, socklen_t socklen,
         goto err;
         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
 #ifdef HAVE_SYS_UN_H
   } else if (listensockaddr->sa_family == AF_UNIX) {
   } else if (listensockaddr->sa_family == AF_UNIX) {
     start_reading = 1;
     start_reading = 1;
@@ -968,7 +986,7 @@ connection_create_listener(struct sockaddr *listensockaddr, socklen_t socklen,
   conn->socket_family = listensockaddr->sa_family;
   conn->socket_family = listensockaddr->sa_family;
   conn->s = s;
   conn->s = s;
   conn->address = tor_strdup(address);
   conn->address = tor_strdup(address);
-  conn->port = usePort;
+  conn->port = gotPort;
 
 
   if (connection_add(conn) < 0) { /* no space, forget it */
   if (connection_add(conn) < 0) { /* no space, forget it */
     log_warn(LD_NET,"connection_add for listener failed. Giving up.");
     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;
     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;
   conn->state = LISTENER_STATE_READY;
   if (start_reading) {
   if (start_reading) {
@@ -1804,7 +1823,7 @@ retry_listeners(int type, config_line_t *cfg,
           case AF_INET:
           case AF_INET:
             listensockaddr = (struct sockaddr *)
             listensockaddr = (struct sockaddr *)
                              create_inet_sockaddr(cfg_line->value,
                              create_inet_sockaddr(cfg_line->value,
-                                                  (uint16_t) port_option,
+                                                  port_option,
                                                   &address, &listensocklen);
                                                   &address, &listensocklen);
             break;
             break;
           case AF_UNIX:
           case AF_UNIX:

+ 4 - 0
src/or/or.h

@@ -2353,6 +2353,10 @@ typedef struct config_line_t {
 
 
 typedef struct routerset_t routerset_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. */
 /** Configuration options for a Tor process. */
 typedef struct {
 typedef struct {
   uint32_t _magic;
   uint32_t _magic;