Browse Source

Merge branch 'ipv6_bridges_squashed'

Nick Mathewson 14 years ago
parent
commit
8cc8b016c8

+ 7 - 0
changes/bug3786

@@ -0,0 +1,7 @@
+  o Major features:
+    - Implement support for clients connecting to private bridges over
+      IPv6. Bridges still need at least one IPv4 address in order to
+      connect to other relays. Currently, adding Bridge lines with
+      both an IPv4 and an IPv6 address to the same bridge will most
+      probably result in the IPv6 address not being used. Implements
+      parts of proposal 186.

+ 7 - 0
src/common/address.h

@@ -31,6 +31,13 @@ typedef struct tor_addr_t
   } addr;
   } addr;
 } tor_addr_t;
 } tor_addr_t;
 
 
+/** Holds an IP address and a TCP/UDP port.  */
+typedef struct tor_addr_port_t
+{
+  tor_addr_t addr;
+  uint16_t port;
+} tor_addr_port_t;
+
 static INLINE const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a);
 static INLINE const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a);
 static INLINE uint32_t tor_addr_to_ipv4n(const tor_addr_t *a);
 static INLINE uint32_t tor_addr_to_ipv4n(const tor_addr_t *a);
 static INLINE uint32_t tor_addr_to_ipv4h(const tor_addr_t *a);
 static INLINE uint32_t tor_addr_to_ipv4h(const tor_addr_t *a);

+ 102 - 41
src/or/circuitbuild.c

@@ -3012,7 +3012,7 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
       log_warn(LD_CIRC,"failed to choose an exit server");
       log_warn(LD_CIRC,"failed to choose an exit server");
       return -1;
       return -1;
     }
     }
-    exit = extend_info_from_node(node);
+    exit = extend_info_from_node(node, 0);
     tor_assert(exit);
     tor_assert(exit);
   }
   }
   state->chosen_exit = exit;
   state->chosen_exit = exit;
@@ -3254,14 +3254,19 @@ onion_extend_cpath(origin_circuit_t *circ)
   } else if (cur_len == 0) { /* picking first node */
   } else if (cur_len == 0) { /* picking first node */
     const node_t *r = choose_good_entry_server(purpose, state);
     const node_t *r = choose_good_entry_server(purpose, state);
     if (r) {
     if (r) {
-      info = extend_info_from_node(r);
+      /* If we're extending to a bridge, use the preferred address
+         rather than the primary, for potentially extending to an IPv6
+         bridge.  */
+      int use_pref_addr = (r->ri != NULL &&
+                           r->ri->purpose == ROUTER_PURPOSE_BRIDGE);
+      info = extend_info_from_node(r, use_pref_addr);
       tor_assert(info);
       tor_assert(info);
     }
     }
   } else {
   } else {
     const node_t *r =
     const node_t *r =
       choose_good_middle_server(purpose, state, circ->cpath, cur_len);
       choose_good_middle_server(purpose, state, circ->cpath, cur_len);
     if (r) {
     if (r) {
-      info = extend_info_from_node(r);
+      info = extend_info_from_node(r, 0);
       tor_assert(info);
       tor_assert(info);
     }
     }
   }
   }
@@ -3320,28 +3325,36 @@ extend_info_alloc(const char *nickname, const char *digest,
   return info;
   return info;
 }
 }
 
 
-/** Allocate and return a new extend_info_t that can be used to build a
- * circuit to or through the router <b>r</b>. */
+/** Allocate and return a new extend_info_t that can be used to build
+ * a circuit to or through the router <b>r</b>. Use the primary
+ * address of the router unless <b>for_direct_connect</b> is true, in
+ * which case the preferred address is used instead. */
 extend_info_t *
 extend_info_t *
-extend_info_from_router(const routerinfo_t *r)
+extend_info_from_router(const routerinfo_t *r, int for_direct_connect)
 {
 {
-  tor_addr_t addr;
+  tor_addr_port_t ap;
   tor_assert(r);
   tor_assert(r);
-  tor_addr_from_ipv4h(&addr, r->addr);
+
+  if (for_direct_connect)
+    router_get_pref_orport(r, &ap);
+  else
+    router_get_prim_orport(r, &ap);
   return extend_info_alloc(r->nickname, r->cache_info.identity_digest,
   return extend_info_alloc(r->nickname, r->cache_info.identity_digest,
-                           r->onion_pkey, &addr, r->or_port);
+                           r->onion_pkey, &ap.addr, ap.port);
 }
 }
 
 
-/** Allocate and return a new extend_info that can be used to build a ircuit
- * to or through the node <b>node</b>.  May return NULL if there is not
- * enough info about <b>node</b> to extend to it--for example, if there
- * is no routerinfo_t or microdesc_t.
+/** Allocate and return a new extend_info that can be used to build a
+ * ircuit to or through the node <b>node</b>. Use the primary address
+ * of the node unless <b>for_direct_connect</b> is true, in which case
+ * the preferred address is used instead. May return NULL if there is
+ * not enough info about <b>node</b> to extend to it--for example, if
+ * there is no routerinfo_t or microdesc_t.
  **/
  **/
 extend_info_t *
 extend_info_t *
-extend_info_from_node(const node_t *node)
+extend_info_from_node(const node_t *node, int for_direct_connect)
 {
 {
   if (node->ri) {
   if (node->ri) {
-    return extend_info_from_router(node->ri);
+    return extend_info_from_router(node->ri, for_direct_connect);
   } else if (node->rs && node->md) {
   } else if (node->rs && node->md) {
     tor_addr_t addr;
     tor_addr_t addr;
     tor_addr_from_ipv4h(&addr, node->rs->addr);
     tor_addr_from_ipv4h(&addr, node->rs->addr);
@@ -4841,10 +4854,11 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr,
 static bridge_info_t *
 static bridge_info_t *
 get_configured_bridge_by_routerinfo(const routerinfo_t *ri)
 get_configured_bridge_by_routerinfo(const routerinfo_t *ri)
 {
 {
-  tor_addr_t addr;
-  tor_addr_from_ipv4h(&addr, ri->addr);
-  return get_configured_bridge_by_addr_port_digest(&addr,
-                              ri->or_port, ri->cache_info.identity_digest);
+  tor_addr_port_t ap;
+
+  router_get_pref_orport(ri, &ap);
+  return get_configured_bridge_by_addr_port_digest(&ap.addr, ap.port,
+                                               ri->cache_info.identity_digest);
 }
 }
 
 
 /** Return 1 if <b>ri</b> is one of our known bridges, else 0. */
 /** Return 1 if <b>ri</b> is one of our known bridges, else 0. */
@@ -4858,18 +4872,31 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri)
 int
 int
 node_is_a_configured_bridge(const node_t *node)
 node_is_a_configured_bridge(const node_t *node)
 {
 {
-  tor_addr_t addr;
-  uint16_t orport;
+  int retval = 0;               /* Negative.  */
+  smartlist_t *orports = NULL;
+
   if (!node)
   if (!node)
-    return 0;
-  if (node_get_addr(node, &addr) < 0)
-    return 0;
-  orport = node_get_orport(node);
-  if (orport == 0)
-    return 0;
+    goto out;
+
+  orports = node_get_all_orports(node);
+  if (orports == NULL)
+    goto out;
 
 
-  return get_configured_bridge_by_addr_port_digest(
-                       &addr, orport, node->identity) != NULL;
+  SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, orport) {
+    if (get_configured_bridge_by_addr_port_digest(&orport->addr, orport->port,
+                                                  node->identity) != NULL) {
+      retval = 1;
+      goto out;
+    }
+  } SMARTLIST_FOREACH_END(orport);
+
+ out:
+  if (orports != NULL) {
+    SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p));
+    smartlist_free(orports);
+    orports = NULL;
+  }
+  return retval;
 }
 }
 
 
 /** We made a connection to a router at <b>addr</b>:<b>port</b>
 /** We made a connection to a router at <b>addr</b>:<b>port</b>
@@ -5123,18 +5150,52 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
     routerinfo_t *ri = node->ri;
     routerinfo_t *ri = node->ri;
     tor_addr_from_ipv4h(&addr, ri->addr);
     tor_addr_from_ipv4h(&addr, ri->addr);
 
 
-    if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
-        bridge->port == ri->or_port) {
+    if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
+         bridge->port == ri->or_port) ||
+        (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) &&
+         bridge->port == ri->ipv6_orport)) {
       /* they match, so no need to do anything */
       /* they match, so no need to do anything */
     } else {
     } else {
-      ri->addr = tor_addr_to_ipv4h(&bridge->addr);
-      tor_free(ri->address);
-      ri->address = tor_dup_ip(ri->addr);
-      ri->or_port = bridge->port;
-      log_info(LD_DIR,
-               "Adjusted bridge routerinfo for '%s' to match configured "
-               "address %s:%d.",
-               ri->nickname, ri->address, ri->or_port);
+      if (tor_addr_family(&bridge->addr) == AF_INET) {
+        ri->addr = tor_addr_to_ipv4h(&bridge->addr);
+        tor_free(ri->address);
+        ri->address = tor_dup_ip(ri->addr);
+        ri->or_port = bridge->port;
+        log_info(LD_DIR,
+                 "Adjusted bridge routerinfo for '%s' to match configured "
+                 "address %s:%d.",
+                 ri->nickname, ri->address, ri->or_port);
+      } else if (tor_addr_family(&bridge->addr) == AF_INET6) {
+        tor_addr_copy(&ri->ipv6_addr, &bridge->addr);
+        ri->ipv6_orport = bridge->port;
+        log_info(LD_DIR,
+                 "Adjusted bridge routerinfo for '%s' to match configured "
+                 "address %s:%d.",
+                 ri->nickname, fmt_addr(&ri->ipv6_addr), ri->ipv6_orport);
+      } else {
+        log_err(LD_BUG, "Address family not supported: %d.",
+                tor_addr_family(&bridge->addr));
+        return;
+      }
+    }
+
+    /* Indicate that we prefer connecting to this bridge over the
+       protocol that the bridge address indicates.  Last bridge
+       descriptor handled wins.  */
+    ri->ipv6_preferred = tor_addr_family(&bridge->addr) == AF_INET6;
+
+    /* XXXipv6 we lack support for falling back to another address for
+       the same relay, warn the user */
+    if (!tor_addr_is_null(&ri->ipv6_addr))
+    {
+      tor_addr_port_t ap;
+      router_get_pref_orport(ri, &ap);
+      log_notice(LD_CONFIG,
+                 "Bridge '%s' has both an IPv4 and an IPv6 address.  "
+                 "Will prefer using its %s address (%s:%d).",
+                 ri->nickname,
+                 ri->ipv6_preferred ? "IPv6" : "IPv4",
+                 fmt_addr(&ap.addr), ap.port);
     }
     }
   }
   }
   if (node->rs) {
   if (node->rs) {
@@ -5179,8 +5240,8 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
       rewrite_node_address_for_bridge(bridge, node);
       rewrite_node_address_for_bridge(bridge, node);
       add_an_entry_guard(node, 1, 1);
       add_an_entry_guard(node, 1, 1);
 
 
-      log_notice(LD_DIR, "new bridge descriptor '%s' (%s)", ri->nickname,
-                 from_cache ? "cached" : "fresh");
+      log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname,
+                 from_cache ? "cached" : "fresh", router_describe(ri));
       /* set entry->made_contact so if it goes down we don't drop it from
       /* set entry->made_contact so if it goes down we don't drop it from
        * our entry node list */
        * our entry node list */
       entry_guard_register_connect_status(ri->cache_info.identity_digest,
       entry_guard_register_connect_status(ri->cache_info.identity_digest,

+ 4 - 2
src/or/circuitbuild.h

@@ -59,8 +59,10 @@ void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
 extend_info_t *extend_info_alloc(const char *nickname, const char *digest,
 extend_info_t *extend_info_alloc(const char *nickname, const char *digest,
                                  crypto_pk_env_t *onion_key,
                                  crypto_pk_env_t *onion_key,
                                  const tor_addr_t *addr, uint16_t port);
                                  const tor_addr_t *addr, uint16_t port);
-extend_info_t *extend_info_from_router(const routerinfo_t *r);
-extend_info_t *extend_info_from_node(const node_t *node);
+extend_info_t *extend_info_from_router(const routerinfo_t *r,
+                                       int for_direct_connect);
+extend_info_t *extend_info_from_node(const node_t *node,
+                                     int for_direct_connect);
 extend_info_t *extend_info_dup(extend_info_t *info);
 extend_info_t *extend_info_dup(extend_info_t *info);
 void extend_info_free(extend_info_t *info);
 void extend_info_free(extend_info_t *info);
 const node_t *build_state_get_exit_node(cpath_build_state_t *state);
 const node_t *build_state_get_exit_node(cpath_build_state_t *state);

+ 4 - 1
src/or/circuituse.c

@@ -1439,7 +1439,10 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
         int opt = conn->chosen_exit_optional;
         int opt = conn->chosen_exit_optional;
         r = node_get_by_nickname(conn->chosen_exit_name, 1);
         r = node_get_by_nickname(conn->chosen_exit_name, 1);
         if (r && node_has_descriptor(r)) {
         if (r && node_has_descriptor(r)) {
-          extend_info = extend_info_from_node(r);
+          /* We might want to connect to an IPv6 bridge for loading
+             descriptors so we use the preferred address rather than
+             the primary.  */
+          extend_info = extend_info_from_node(r, conn->want_onehop ? 1 : 0);
         } else {
         } else {
           log_debug(LD_DIR, "considering %d, %s",
           log_debug(LD_DIR, "considering %d, %s",
                     want_onehop, conn->chosen_exit_name);
                     want_onehop, conn->chosen_exit_name);

+ 389 - 179
src/or/config.c

@@ -220,7 +220,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,                 PORT,     "0"),
+  V(ControlPort,                 LINELIST, NULL),
   V(ControlPortFileGroupReadable,BOOL,     "0"),
   V(ControlPortFileGroupReadable,BOOL,     "0"),
   V(ControlPortWriteToFile,      FILENAME, NULL),
   V(ControlPortWriteToFile,      FILENAME, NULL),
   V(ControlSocket,               LINELIST, NULL),
   V(ControlSocket,               LINELIST, NULL),
@@ -237,7 +237,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,                     PORT,     "0"),
+  V(DirPort,                     LINELIST, NULL),
   V(DirPortFrontPage,            FILENAME, NULL),
   V(DirPortFrontPage,            FILENAME, NULL),
   OBSOLETE("DirPostPeriod"),
   OBSOLETE("DirPostPeriod"),
   OBSOLETE("DirRecordUsageByCountry"),
   OBSOLETE("DirRecordUsageByCountry"),
@@ -343,7 +343,7 @@ static config_var_t _option_vars[] = {
   V(NumCPUs,                     UINT,     "0"),
   V(NumCPUs,                     UINT,     "0"),
   V(NumEntryGuards,              UINT,     "3"),
   V(NumEntryGuards,              UINT,     "3"),
   V(ORListenAddress,             LINELIST, NULL),
   V(ORListenAddress,             LINELIST, NULL),
-  V(ORPort,                      PORT,     "0"),
+  V(ORPort,                      LINELIST, NULL),
   V(OutboundBindAddress,         STRING,   NULL),
   V(OutboundBindAddress,         STRING,   NULL),
   OBSOLETE("PathlenCoinWeight"),
   OBSOLETE("PathlenCoinWeight"),
   V(PerConnBWBurst,              MEMUNIT,  "0"),
   V(PerConnBWBurst,              MEMUNIT,  "0"),
@@ -600,8 +600,11 @@ static int parse_dir_server_line(const char *line,
                                  dirinfo_type_t required_type,
                                  dirinfo_type_t required_type,
                                  int validate_only);
                                  int validate_only);
 static void port_cfg_free(port_cfg_t *port);
 static void port_cfg_free(port_cfg_t *port);
-static int parse_client_ports(const or_options_t *options, int validate_only,
+static int parse_ports(const or_options_t *options, int validate_only,
                               char **msg_out, int *n_ports_out);
                               char **msg_out, int *n_ports_out);
+static int check_server_ports(const smartlist_t *ports,
+                              const or_options_t *options);
+
 static int validate_data_directory(or_options_t *options);
 static int validate_data_directory(or_options_t *options);
 static int write_configuration_file(const char *fname,
 static int write_configuration_file(const char *fname,
                                     const or_options_t *options);
                                     const or_options_t *options);
@@ -614,9 +617,6 @@ static int or_state_validate(or_state_t *old_options, or_state_t *options,
 static int or_state_load(void);
 static int or_state_load(void);
 static int options_init_logs(or_options_t *options, int validate_only);
 static int options_init_logs(or_options_t *options, int validate_only);
 
 
-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);
 static uint64_t config_parse_memunit(const char *s, int *ok);
 static int config_parse_msec_interval(const char *s, int *ok);
 static int config_parse_msec_interval(const char *s, int *ok);
 static int config_parse_interval(const char *s, int *ok);
 static int config_parse_interval(const char *s, int *ok);
@@ -675,8 +675,8 @@ static or_state_t *global_state = NULL;
 static config_line_t *global_cmdline_options = NULL;
 static config_line_t *global_cmdline_options = NULL;
 /** Contents of most recently read DirPortFrontPage file. */
 /** Contents of most recently read DirPortFrontPage file. */
 static char *global_dirfrontpagecontents = NULL;
 static char *global_dirfrontpagecontents = NULL;
-/** List of port_cfg_t for client-level (SOCKS, DNS, Trans, NATD) ports. */
-static smartlist_t *configured_client_ports = NULL;
+/** List of port_cfg_t for all configured ports. */
+static smartlist_t *configured_ports = NULL;
 
 
 /** Return the contents of our frontpage string, or NULL if not configured. */
 /** Return the contents of our frontpage string, or NULL if not configured. */
 const char *
 const char *
@@ -821,11 +821,11 @@ config_free_all(void)
   config_free_lines(global_cmdline_options);
   config_free_lines(global_cmdline_options);
   global_cmdline_options = NULL;
   global_cmdline_options = NULL;
 
 
-  if (configured_client_ports) {
-    SMARTLIST_FOREACH(configured_client_ports,
+  if (configured_ports) {
+    SMARTLIST_FOREACH(configured_ports,
                       port_cfg_t *, p, tor_free(p));
                       port_cfg_t *, p, tor_free(p));
-    smartlist_free(configured_client_ports);
-    configured_client_ports = NULL;
+    smartlist_free(configured_ports);
+    configured_ports = NULL;
   }
   }
 
 
   tor_free(torrc_fname);
   tor_free(torrc_fname);
@@ -1075,7 +1075,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
 #endif
 #endif
 
 
   if (running_tor) {
   if (running_tor) {
-    int n_client_ports=0;
+    int n_ports=0;
     /* We need to set the connection limit before we can open the listeners. */
     /* We need to set the connection limit before we can open the listeners. */
     if (set_max_file_descriptors((unsigned)options->ConnLimit,
     if (set_max_file_descriptors((unsigned)options->ConnLimit,
                                  &options->_ConnLimit) < 0) {
                                  &options->_ConnLimit) < 0) {
@@ -1091,10 +1091,10 @@ options_act_reversible(const or_options_t *old_options, char **msg)
       libevent_initialized = 1;
       libevent_initialized = 1;
     }
     }
 
 
-    /* Adjust the client port configuration so we can launch listeners. */
-    if (parse_client_ports(options, 0, msg, &n_client_ports)) {
+    /* Adjust the port configuration so we can launch listeners. */
+    if (parse_ports(options, 0, msg, &n_ports)) {
       if (!*msg)
       if (!*msg)
-        *msg = tor_strdup("Unexpected problem parsing client port config");
+        *msg = tor_strdup("Unexpected problem parsing port config");
       goto rollback;
       goto rollback;
     }
     }
 
 
@@ -1555,7 +1555,7 @@ options_act(const or_options_t *old_options)
       int was_relay = 0;
       int was_relay = 0;
       if (options->BridgeRelay) {
       if (options->BridgeRelay) {
         time_t int_start = time(NULL);
         time_t int_start = time(NULL);
-        if (old_options->ORPort == options->ORPort) {
+        if (config_lines_eq(old_options->ORPort, options->ORPort)) {
           int_start += RELAY_BRIDGE_STATS_DELAY;
           int_start += RELAY_BRIDGE_STATS_DELAY;
           was_relay = 1;
           was_relay = 1;
         }
         }
@@ -3034,37 +3034,6 @@ options_init(or_options_t *options)
   config_init(&options_format, options);
   config_init(&options_format, options);
 }
 }
 
 
-/* Check if the port number given in <b>port_option</b> in combination with
- * the specified port in <b>listen_options</b> will result in Tor actually
- * opening a low port (meaning a port lower than 1024). Return 1 if
- * 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(int port_option,
-                         const config_line_t *listen_options)
-{
-#ifdef MS_WINDOWS
-  (void) port_option;
-  (void) listen_options;
-  return 0; /* No port is too low for windows. */
-#else
-  const config_line_t *l;
-  uint16_t p;
-  if (port_option == 0)
-    return 0; /* We're not listening */
-  if (listen_options == NULL)
-    return (port_option < 1024);
-
-  for (l = listen_options; l; l = l->next) {
-    addr_port_lookup(LOG_WARN, l->value, NULL, NULL, &p);
-    if (p<1024) {
-      return 1;
-    }
-  }
-  return 0;
-#endif
-}
-
 /** Set all vars in the configuration object <b>options</b> to their default
 /** Set all vars in the configuration object <b>options</b> to their default
  * values. */
  * values. */
 static void
 static void
@@ -3290,7 +3259,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
   int i;
   int i;
   config_line_t *cl;
   config_line_t *cl;
   const char *uname = get_uname();
   const char *uname = get_uname();
-  int n_client_ports=0;
+  int n_ports=0;
 #define REJECT(arg) \
 #define REJECT(arg) \
   STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
   STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
 #define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
 #define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
@@ -3308,13 +3277,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
         "for details.", uname);
         "for details.", uname);
   }
   }
 
 
-  if (options->ORPort == 0 && options->ORListenAddress != NULL)
-    REJECT("ORPort must be defined if ORListenAddress is defined.");
-
-  if (options->DirPort == 0 && options->DirListenAddress != NULL)
-    REJECT("DirPort must be defined if DirListenAddress is defined.");
-
-  if (parse_client_ports(options, 1, msg, &n_client_ports) < 0)
+  if (parse_ports(options, 1, msg, &n_ports) < 0)
     return -1;
     return -1;
 
 
   if (validate_data_directory(options)<0)
   if (validate_data_directory(options)<0)
@@ -3361,7 +3324,9 @@ 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 (n_client_ports == 0 && options->ORPort == 0 && !options->RendConfigLines)
+  /* XXXX require that the only port not be DirPort? */
+  /* XXXX require that at least one port be listened-upon. */
+  if (n_ports == 0 && !options->RendConfigLines)
     log(LOG_WARN, LD_CONFIG,
     log(LOG_WARN, LD_CONFIG,
         "SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all "
         "SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all "
         "undefined, and there aren't any hidden services configured.  "
         "undefined, and there aren't any hidden services configured.  "
@@ -3377,19 +3342,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
     REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive.");
     REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive.");
   }
   }
 
 
-  if (options->AccountingMax &&
-      (is_listening_on_low_port(options->ORPort, options->ORListenAddress) ||
-       is_listening_on_low_port(options->DirPort, options->DirListenAddress)))
-  {
-    log(LOG_WARN, LD_CONFIG,
-          "You have set AccountingMax to use hibernation. You have also "
-          "chosen a low DirPort or OrPort. This combination can make Tor stop "
-          "working when it tries to re-attach the port after a period of "
-          "hibernation. Please choose a different port or turn off "
-          "hibernation unless you know this combination will work on your "
-          "platform.");
-  }
-
   if (options->ExcludeExitNodes || options->ExcludeNodes) {
   if (options->ExcludeExitNodes || options->ExcludeNodes) {
     options->_ExcludeExitNodesUnion = routerset_new();
     options->_ExcludeExitNodesUnion = routerset_new();
     routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeExitNodes);
     routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeExitNodes);
@@ -3643,7 +3595,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
   if (options->BridgeRelay && options->DirPort) {
   if (options->BridgeRelay && options->DirPort) {
     log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling "
     log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling "
              "DirPort");
              "DirPort");
-    options->DirPort = 0;
+    config_free_lines(options->DirPort);
+    options->DirPort = NULL;
   }
   }
 
 
   if (options->MinUptimeHidServDirectoryV2 < 0) {
   if (options->MinUptimeHidServDirectoryV2 < 0) {
@@ -3866,39 +3819,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
     }
     }
   }
   }
 
 
-  if (options->ControlListenAddress) {
-    int all_are_local = 1;
-    config_line_t *ln;
-    for (ln = options->ControlListenAddress; ln; ln = ln->next) {
-      if (strcmpstart(ln->value, "127."))
-        all_are_local = 0;
-    }
-    if (!all_are_local) {
-      if (!options->HashedControlPassword &&
-          !options->HashedControlSessionPassword &&
-          !options->CookieAuthentication) {
-        log_warn(LD_CONFIG,
-                 "You have a ControlListenAddress set to accept "
-                 "unauthenticated connections from a non-local address.  "
-                 "This means that programs not running on your computer "
-                 "can reconfigure your Tor, without even having to guess a "
-                 "password.  That's so bad that I'm closing your ControlPort "
-                 "for you.  If you need to control your Tor remotely, try "
-                 "enabling authentication and using a tool like stunnel or "
-                 "ssh to encrypt remote access.");
-        options->ControlPort = 0;
-      } else {
-        log_warn(LD_CONFIG, "You have a ControlListenAddress set to accept "
-                 "connections from a non-local address.  This means that "
-                 "programs not running on your computer can reconfigure your "
-                 "Tor.  That's pretty bad, since the controller "
-                 "protocol isn't encrypted!  Maybe you should just listen on "
-                 "127.0.0.1 and use a tool like stunnel or ssh to encrypt "
-                 "remote connections to your control port.");
-      }
-    }
-  }
-
   if (options->ControlPort && !options->HashedControlPassword &&
   if (options->ControlPort && !options->HashedControlPassword &&
       !options->HashedControlSessionPassword &&
       !options->HashedControlSessionPassword &&
       !options->CookieAuthentication) {
       !options->CookieAuthentication) {
@@ -4121,8 +4041,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
         }
         }
     });
     });
 
 
-  if (options->BridgeRelay == 1 && options->ORPort == 0)
-      REJECT("BridgeRelay is 1, ORPort is 0. This is an invalid combination.");
+  if (options->BridgeRelay == 1 && ! options->ORPort)
+      REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid "
+             "combination.");
 
 
   return 0;
   return 0;
 #undef REJECT
 #undef REJECT
@@ -4213,7 +4134,7 @@ options_transition_affects_workers(const or_options_t *old_options,
 {
 {
   if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) ||
   if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) ||
       old_options->NumCPUs != new_options->NumCPUs ||
       old_options->NumCPUs != new_options->NumCPUs ||
-      old_options->ORPort != new_options->ORPort ||
+      !config_lines_eq(old_options->ORPort, new_options->ORPort) ||
       old_options->ServerDNSSearchDomains !=
       old_options->ServerDNSSearchDomains !=
                                        new_options->ServerDNSSearchDomains ||
                                        new_options->ServerDNSSearchDomains ||
       old_options->_SafeLogging != new_options->_SafeLogging ||
       old_options->_SafeLogging != new_options->_SafeLogging ||
@@ -4243,8 +4164,8 @@ options_transition_affects_descriptor(const or_options_t *old_options,
       !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) ||
       !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) ||
       old_options->ExitPolicyRejectPrivate !=
       old_options->ExitPolicyRejectPrivate !=
         new_options->ExitPolicyRejectPrivate ||
         new_options->ExitPolicyRejectPrivate ||
-      old_options->ORPort != new_options->ORPort ||
-      old_options->DirPort != new_options->DirPort ||
+      !config_lines_eq(old_options->ORPort, new_options->ORPort) ||
+      !config_lines_eq(old_options->DirPort, new_options->DirPort) ||
       old_options->ClientOnly != new_options->ClientOnly ||
       old_options->ClientOnly != new_options->ClientOnly ||
       old_options->DisableNetwork != new_options->DisableNetwork ||
       old_options->DisableNetwork != new_options->DisableNetwork ||
       old_options->_PublishServerDescriptor !=
       old_options->_PublishServerDescriptor !=
@@ -5380,12 +5301,53 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname)
   } SMARTLIST_FOREACH_END(port);
   } SMARTLIST_FOREACH_END(port);
 }
 }
 
 
-#define CL_PORT_NO_OPTIONS    (1u<<0)
+/** DOCDOC */
+static void
+warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid)
+{
+  int warned = 0;
+  SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) {
+    if (port->type != CONN_TYPE_CONTROL_LISTENER)
+      continue;
+    if (port->is_unix_addr)
+      continue;
+    if (!tor_addr_is_loopback(&port->addr)) {
+      if (forbid) {
+        if (!warned)
+          log_warn(LD_CONFIG,
+                 "You have a ControlPort set to accept "
+                 "unauthenticated connections from a non-local address.  "
+                 "This means that programs not running on your computer "
+                 "can reconfigure your Tor, without even having to guess a "
+                 "password.  That's so bad that I'm closing your ControlPort "
+                 "for you.  If you need to control your Tor remotely, try "
+                 "enabling authentication and using a tool like stunnel or "
+                 "ssh to encrypt remote access.");
+        warned = 1;
+        port_cfg_free(port);
+        SMARTLIST_DEL_CURRENT(ports, port);
+      } else {
+        log_warn(LD_CONFIG, "You have a ControlPort set to accept "
+                 "connections from a non-local address.  This means that "
+                 "programs not running on your computer can reconfigure your "
+                 "Tor.  That's pretty bad, since the controller "
+                 "protocol isn't encrypted!  Maybe you should just listen on "
+                 "127.0.0.1 and use a tool like stunnel or ssh to encrypt "
+                 "remote connections to your control port.");
+        return; /* No point in checking the rest */
+      }
+    }
+  } SMARTLIST_FOREACH_END(port);
+}
+
+#define CL_PORT_NO_OPTIONS (1u<<0)
 #define CL_PORT_WARN_NONLOCAL (1u<<1)
 #define CL_PORT_WARN_NONLOCAL (1u<<1)
 #define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
 #define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
+#define CL_PORT_SERVER_OPTIONS (1u<<3)
+#define CL_PORT_FORBID_NONLOCAL (1u<<4)
 
 
 /**
 /**
- * Parse port configuration for a single client port type.
+ * Parse port configuration for a single port type.
  *
  *
  * Read entries of the "FooPort" type from the list <b>ports</b>, and
  * Read entries of the "FooPort" type from the list <b>ports</b>, and
  * entries of the "FooListenAddress" type from the list
  * entries of the "FooListenAddress" type from the list
@@ -5405,17 +5367,22 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname)
  * isolation options in the FooPort entries.
  * isolation options in the FooPort entries.
  *
  *
  * If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the
  * If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the
- * ports are not on a local address.
+ * ports are not on a local address.  If CL_PORT_FORBID_NONLOCAL is set,
+ * this is a contrl port with no password set: don't even allow it.
  *
  *
  * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn
  * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn
  * if FooListenAddress is set but FooPort is 0.
  * if FooListenAddress is set but FooPort is 0.
  *
  *
+ * If CL_PORT_SERVER_OPTIONS is set in <b>flags</b>, do not allow stream
+ * isolation options in the FooPort entries; instead allow the
+ * server-port option set.
+ *
  * On success, if <b>out</b> is given, add a new port_cfg_t entry to
  * On success, if <b>out</b> is given, add a new port_cfg_t entry to
  * <b>out</b> for every port that the client should listen on.  Return 0
  * <b>out</b> for every port that the client should listen on.  Return 0
  * on success, -1 on failure.
  * on success, -1 on failure.
  */
  */
 static int
 static int
-parse_client_port_config(smartlist_t *out,
+parse_port_config(smartlist_t *out,
                          const config_line_t *ports,
                          const config_line_t *ports,
                          const config_line_t *listenaddrs,
                          const config_line_t *listenaddrs,
                          const char *portname,
                          const char *portname,
@@ -5426,8 +5393,11 @@ parse_client_port_config(smartlist_t *out,
 {
 {
   smartlist_t *elts;
   smartlist_t *elts;
   int retval = -1;
   int retval = -1;
-  const unsigned allow_client_options = !(flags & CL_PORT_NO_OPTIONS);
+  const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER);
+  const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS;
+  const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS;
   const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
   const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
+  const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL;
   const unsigned allow_spurious_listenaddr =
   const unsigned allow_spurious_listenaddr =
     flags & CL_PORT_ALLOW_EXTRA_LISTENADDR;
     flags & CL_PORT_ALLOW_EXTRA_LISTENADDR;
 
 
@@ -5463,6 +5433,17 @@ parse_client_port_config(smartlist_t *out,
       return -1;
       return -1;
     }
     }
 
 
+    if (use_server_options && out) {
+      /* Add a no_listen port. */
+      port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+      cfg->type = listener_type;
+      cfg->port = mainport;
+      tor_addr_make_unspec(&cfg->addr); /* Server ports default to 0.0.0.0 */
+      cfg->no_listen = 1;
+      cfg->ipv4_only = 1;
+      smartlist_add(out, cfg);
+    }
+
     for (; listenaddrs; listenaddrs = listenaddrs->next) {
     for (; listenaddrs; listenaddrs = listenaddrs->next) {
       tor_addr_t addr;
       tor_addr_t addr;
       uint16_t port = 0;
       uint16_t port = 0;
@@ -5478,12 +5459,17 @@ parse_client_port_config(smartlist_t *out,
         tor_addr_copy(&cfg->addr, &addr);
         tor_addr_copy(&cfg->addr, &addr);
         cfg->session_group = SESSION_GROUP_UNSET;
         cfg->session_group = SESSION_GROUP_UNSET;
         cfg->isolation_flags = ISO_DEFAULT;
         cfg->isolation_flags = ISO_DEFAULT;
+        cfg->no_advertise = 1;
         smartlist_add(out, cfg);
         smartlist_add(out, cfg);
       }
       }
     }
     }
 
 
-    if (warn_nonlocal && out)
-      warn_nonlocal_client_ports(out, portname);
+    if (warn_nonlocal && out) {
+      if (is_control)
+        warn_nonlocal_controller_ports(out, forbid_nonlocal);
+      else
+        warn_nonlocal_client_ports(out, portname);
+    }
     return 0;
     return 0;
   } /* end if (listenaddrs) */
   } /* end if (listenaddrs) */
 
 
@@ -5515,6 +5501,8 @@ parse_client_port_config(smartlist_t *out,
     char *addrport;
     char *addrport;
     uint16_t ptmp=0;
     uint16_t ptmp=0;
     int ok;
     int ok;
+    int no_listen = 0, no_advertise = 0, all_addrs = 0,
+      ipv4_only = 0, ipv6_only = 0;
 
 
     smartlist_split_string(elts, ports->value, NULL,
     smartlist_split_string(elts, ports->value, NULL,
                            SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
                            SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
@@ -5523,7 +5511,7 @@ parse_client_port_config(smartlist_t *out,
       goto err;
       goto err;
     }
     }
 
 
-    if (!allow_client_options && smartlist_len(elts) > 1) {
+    if (allow_no_options && smartlist_len(elts) > 1) {
       log_warn(LD_CONFIG, "Too many options on %sPort line", portname);
       log_warn(LD_CONFIG, "Too many options on %sPort line", portname);
       goto err;
       goto err;
     }
     }
@@ -5562,56 +5550,107 @@ parse_client_port_config(smartlist_t *out,
     }
     }
 
 
     /* Now parse the rest of the options, if any. */
     /* Now parse the rest of the options, if any. */
-    SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
-      int no = 0, isoflag = 0;
-      const char *elt_orig = elt;
-      if (elt_sl_idx == 0)
-        continue; /* Skip addr:port */
-      if (!strcasecmpstart(elt, "SessionGroup=")) {
-        int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
-                                        10, 0, INT_MAX, &ok, NULL);
-        if (!ok) {
-          log_warn(LD_CONFIG, "Invalid %sPort option '%s'",
+    if (use_server_options) {
+      /* This is a server port; parse advertising options */
+      SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
+        if (elt_sl_idx == 0)
+          continue; /* Skip addr:port */
+
+        if (!strcasecmp(elt, "NoAdvertise")) {
+          no_advertise = 1;
+        } else if (!strcasecmp(elt, "NoListen")) {
+          no_listen = 1;
+#if 0
+        /* not implemented yet. */
+        } else if (!strcasecmp(elt, "AllAddrs")) {
+
+          all_addrs = 1;
+#endif
+        } else if (!strcasecmp(elt, "IPv4Only")) {
+          ipv4_only = 1;
+        } else if (!strcasecmp(elt, "IPv6Only")) {
+          ipv6_only = 1;
+        } else {
+          log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
                    portname, escaped(elt));
                    portname, escaped(elt));
-          goto err;
         }
         }
-        if (sessiongroup >= 0) {
-          log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort",
-                   portname);
-          goto err;
-        }
-        sessiongroup = group;
-        continue;
-      }
+      } SMARTLIST_FOREACH_END(elt);
 
 
-      if (!strcasecmpstart(elt, "No")) {
-        no = 1;
-        elt += 2;
+      if (no_advertise && no_listen) {
+        log_warn(LD_CONFIG, "Tried to set both NoListen and NoAdvertise "
+                 "on %sPort line '%s'",
+                 portname, escaped(ports->value));
+        goto err;
       }
       }
-      if (!strcasecmpend(elt, "s"))
-        elt[strlen(elt)-1] = '\0'; /* kill plurals. */
-
-      if (!strcasecmp(elt, "IsolateDestPort")) {
-        isoflag = ISO_DESTPORT;
-      } else if (!strcasecmp(elt, "IsolateDestAddr")) {
-        isoflag = ISO_DESTADDR;
-      } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) {
-        isoflag = ISO_SOCKSAUTH;
-      } else if (!strcasecmp(elt, "IsolateClientProtocol")) {
-        isoflag = ISO_CLIENTPROTO;
-      } else if (!strcasecmp(elt, "IsolateClientAddr")) {
-        isoflag = ISO_CLIENTADDR;
-      } else {
-        log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
-                 portname, escaped(elt_orig));
+      if (ipv4_only && ipv6_only) {
+        log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only "
+                 "on %sPort line '%s'",
+                 portname, escaped(ports->value));
+        goto err;
       }
       }
-
-      if (no) {
-        isolation &= ~isoflag;
-      } else {
-        isolation |= isoflag;
+      if (ipv4_only && tor_addr_family(&addr) == AF_INET6) {
+        log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6",
+                 portname);
+        goto err;
       }
       }
-    } SMARTLIST_FOREACH_END(elt);
+      if (ipv6_only && tor_addr_family(&addr) == AF_INET) {
+        log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4",
+                 portname);
+        goto err;
+      }
+    } else {
+      /* This is a client port; parse isolation options */
+      SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
+        int no = 0, isoflag = 0;
+        const char *elt_orig = elt;
+        if (elt_sl_idx == 0)
+          continue; /* Skip addr:port */
+        if (!strcasecmpstart(elt, "SessionGroup=")) {
+          int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
+                                          10, 0, INT_MAX, &ok, NULL);
+          if (!ok) {
+            log_warn(LD_CONFIG, "Invalid %sPort option '%s'",
+                     portname, escaped(elt));
+            goto err;
+          }
+          if (sessiongroup >= 0) {
+            log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort",
+                     portname);
+            goto err;
+          }
+          sessiongroup = group;
+          continue;
+        }
+
+        if (!strcasecmpstart(elt, "No")) {
+          no = 1;
+          elt += 2;
+        }
+        if (!strcasecmpend(elt, "s"))
+          elt[strlen(elt)-1] = '\0'; /* kill plurals. */
+
+        if (!strcasecmp(elt, "IsolateDestPort")) {
+          isoflag = ISO_DESTPORT;
+        } else if (!strcasecmp(elt, "IsolateDestAddr")) {
+          isoflag = ISO_DESTADDR;
+        } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) {
+          isoflag = ISO_SOCKSAUTH;
+        } else if (!strcasecmp(elt, "IsolateClientProtocol")) {
+          isoflag = ISO_CLIENTPROTO;
+        } else if (!strcasecmp(elt, "IsolateClientAddr")) {
+          isoflag = ISO_CLIENTADDR;
+        } else {
+          log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
+                   portname, escaped(elt_orig));
+        }
+
+        if (no) {
+          isolation &= ~isoflag;
+        } else {
+          isolation |= isoflag;
+        }
+      } SMARTLIST_FOREACH_END(elt);
+    }
 
 
     if (out && port) {
     if (out && port) {
       port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
       port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
@@ -5620,14 +5659,24 @@ parse_client_port_config(smartlist_t *out,
       tor_addr_copy(&cfg->addr, &addr);
       tor_addr_copy(&cfg->addr, &addr);
       cfg->session_group = sessiongroup;
       cfg->session_group = sessiongroup;
       cfg->isolation_flags = isolation;
       cfg->isolation_flags = isolation;
+      cfg->no_listen = no_listen;
+      cfg->no_listen = no_advertise;
+      cfg->all_addrs = all_addrs;
+      cfg->ipv4_only = ipv4_only;
+      cfg->ipv6_only = ipv6_only;
+
       smartlist_add(out, cfg);
       smartlist_add(out, cfg);
     }
     }
     SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
     SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
     smartlist_clear(elts);
     smartlist_clear(elts);
   }
   }
 
 
-  if (warn_nonlocal && out)
-    warn_nonlocal_client_ports(out, portname);
+  if (warn_nonlocal && out) {
+    if (is_control)
+      warn_nonlocal_controller_ports(out, forbid_nonlocal);
+    else
+      warn_nonlocal_client_ports(out, portname);
+  }
 
 
   retval = 0;
   retval = 0;
  err:
  err:
@@ -5636,6 +5685,27 @@ parse_client_port_config(smartlist_t *out,
   return retval;
   return retval;
 }
 }
 
 
+/** DOCDOC */
+static int
+parse_socket_config(smartlist_t *out, const config_line_t *cfg,
+                    int listener_type)
+{
+
+  if (!out)
+    return 0;
+
+  for ( ; cfg; cfg = cfg->next) {
+    size_t len = strlen(cfg->value);
+    port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1);
+    port->is_unix_addr = 1;
+    memcpy(port->unix_addr, cfg->value, len+1);
+    port->type = listener_type;
+    smartlist_add(out, port);
+  }
+
+  return 0;
+}
+
 /** Parse all client port types (Socks, DNS, Trans, NATD) from
 /** Parse all client port types (Socks, DNS, Trans, NATD) from
  * <b>options</b>. On success, set *<b>n_ports_out</b> to the number of
  * <b>options</b>. On success, set *<b>n_ports_out</b> to the number of
  * ports that are listed and return 0.  On failure, set *<b>msg</b> to a
  * ports that are listed and return 0.  On failure, set *<b>msg</b> to a
@@ -5645,8 +5715,8 @@ parse_client_port_config(smartlist_t *out,
  * new list of ports parsed from <b>options</b>.
  * new list of ports parsed from <b>options</b>.
  **/
  **/
 static int
 static int
-parse_client_ports(const or_options_t *options, int validate_only,
-                   char **msg, int *n_ports_out)
+parse_ports(const or_options_t *options, int validate_only,
+            char **msg, int *n_ports_out)
 {
 {
   smartlist_t *ports;
   smartlist_t *ports;
   int retval = -1;
   int retval = -1;
@@ -5655,7 +5725,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
 
 
   *n_ports_out = 0;
   *n_ports_out = 0;
 
 
-  if (parse_client_port_config(ports,
+  if (parse_port_config(ports,
              options->SocksPort, options->SocksListenAddress,
              options->SocksPort, options->SocksListenAddress,
              "Socks", CONN_TYPE_AP_LISTENER,
              "Socks", CONN_TYPE_AP_LISTENER,
              "127.0.0.1", 9050,
              "127.0.0.1", 9050,
@@ -5663,7 +5733,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
     *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration");
     *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration");
     goto err;
     goto err;
   }
   }
-  if (parse_client_port_config(ports,
+  if (parse_port_config(ports,
                                options->DNSPort, options->DNSListenAddress,
                                options->DNSPort, options->DNSListenAddress,
                                "DNS", CONN_TYPE_AP_DNS_LISTENER,
                                "DNS", CONN_TYPE_AP_DNS_LISTENER,
                                "127.0.0.1", 0,
                                "127.0.0.1", 0,
@@ -5671,7 +5741,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
     *msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration");
     *msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration");
     goto err;
     goto err;
   }
   }
-  if (parse_client_port_config(ports,
+  if (parse_port_config(ports,
                                options->TransPort, options->TransListenAddress,
                                options->TransPort, options->TransListenAddress,
                                "Trans", CONN_TYPE_AP_TRANS_LISTENER,
                                "Trans", CONN_TYPE_AP_TRANS_LISTENER,
                                "127.0.0.1", 0,
                                "127.0.0.1", 0,
@@ -5679,7 +5749,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
     *msg = tor_strdup("Invalid TransPort/TransListenAddress configuration");
     *msg = tor_strdup("Invalid TransPort/TransListenAddress configuration");
     goto err;
     goto err;
   }
   }
-  if (parse_client_port_config(ports,
+  if (parse_port_config(ports,
                                options->NATDPort, options->NATDListenAddress,
                                options->NATDPort, options->NATDListenAddress,
                                "NATD", CONN_TYPE_AP_NATD_LISTENER,
                                "NATD", CONN_TYPE_AP_NATD_LISTENER,
                                "127.0.0.1", 0,
                                "127.0.0.1", 0,
@@ -5687,16 +5757,63 @@ parse_client_ports(const or_options_t *options, int validate_only,
     *msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration");
     *msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration");
     goto err;
     goto err;
   }
   }
+  {
+    unsigned control_port_flags = CL_PORT_NO_OPTIONS | CL_PORT_WARN_NONLOCAL;
+    const int any_passwords = (options->HashedControlPassword ||
+                               options->HashedControlSessionPassword ||
+                               options->CookieAuthentication);
+    if (! any_passwords)
+      control_port_flags |= CL_PORT_FORBID_NONLOCAL;
+
+    if (parse_port_config(ports,
+                          options->ControlPort, options->ControlListenAddress,
+                          "Control", CONN_TYPE_CONTROL_LISTENER,
+                          "127.0.0.1", 0,
+                          control_port_flags) < 0) {
+      *msg = tor_strdup("Invalid ControlPort/ControlListenAddress "
+                        "configuration");
+      goto err;
+    }
+    if (parse_socket_config(ports,
+                            options->ControlSocket,
+                            CONN_TYPE_CONTROL_LISTENER) < 0) {
+      *msg = tor_strdup("Invalid ControlSocket configuration");
+      goto err;
+    }
+  }
+  if (! options->ClientOnly) {
+    if (parse_port_config(ports,
+                          options->ORPort, options->ORListenAddress,
+                          "OR", CONN_TYPE_OR_LISTENER,
+                          "0.0.0.0", 0,
+                          CL_PORT_SERVER_OPTIONS) < 0) {
+      *msg = tor_strdup("Invalid ORPort/ORListenAddress configuration");
+      goto err;
+    }
+    if (parse_port_config(ports,
+                          options->DirPort, options->DirListenAddress,
+                          "Dir", CONN_TYPE_DIR_LISTENER,
+                          "0.0.0.0", 0,
+                          CL_PORT_SERVER_OPTIONS) < 0) {
+      *msg = tor_strdup("Invalid DirPort/DirListenAddress configuration");
+      goto err;
+    }
+  }
+
+  if (check_server_ports(ports, options) < 0) {
+    *msg = tor_strdup("Misconfigured server ports");
+    goto err;
+  }
 
 
   *n_ports_out = smartlist_len(ports);
   *n_ports_out = smartlist_len(ports);
 
 
   if (!validate_only) {
   if (!validate_only) {
-    if (configured_client_ports) {
-      SMARTLIST_FOREACH(configured_client_ports,
+    if (configured_ports) {
+      SMARTLIST_FOREACH(configured_ports,
                         port_cfg_t *, p, port_cfg_free(p));
                         port_cfg_t *, p, port_cfg_free(p));
-      smartlist_free(configured_client_ports);
+      smartlist_free(configured_ports);
     }
     }
-    configured_client_ports = ports;
+    configured_ports = ports;
     ports = NULL; /* prevent free below. */
     ports = NULL; /* prevent free below. */
   }
   }
 
 
@@ -5709,14 +5826,107 @@ parse_client_ports(const or_options_t *options, int validate_only,
   return retval;
   return retval;
 }
 }
 
 
+/** DOCDOC */
+static int
+check_server_ports(const smartlist_t *ports,
+                   const or_options_t *options)
+{
+  int n_orport_advertised = 0;
+  int n_orport_advertised_ipv4 = 0;
+  int n_orport_listeners = 0;
+  int n_dirport_advertised = 0;
+  int n_dirport_listeners = 0;
+  int n_low_port = 0;
+  int r = 0;
+
+  SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+    if (port->type == CONN_TYPE_DIR_LISTENER) {
+      if (! port->no_advertise)
+        ++n_dirport_advertised;
+      if (! port->no_listen)
+        ++n_dirport_listeners;
+    } else if (port->type == CONN_TYPE_OR_LISTENER) {
+      if (! port->no_advertise) {
+        ++n_orport_advertised;
+        if (tor_addr_family(&port->addr) == AF_INET ||
+            (tor_addr_family(&port->addr) == AF_UNSPEC && !port->ipv6_only))
+          ++n_orport_advertised_ipv4;
+      }
+      if (! port->no_listen)
+        ++n_orport_listeners;
+    } else {
+      continue;
+    }
+#ifndef MS_WINDOWS
+    if (!port->no_advertise && port->port < 1024)
+      ++n_low_port;
+#endif
+  } SMARTLIST_FOREACH_END(port);
+
+  if (n_orport_advertised && !n_orport_listeners) {
+    log_warn(LD_CONFIG, "We are advertising an ORPort, but not actually "
+             "listening on one.");
+    r = -1;
+  }
+  if (n_dirport_advertised && !n_dirport_listeners) {
+    log_warn(LD_CONFIG, "We are advertising a DirPort, but not actually "
+             "listening on one.");
+    r = -1;
+  }
+  if (n_dirport_advertised > 1) {
+    log_warn(LD_CONFIG, "Can't advertise more than one DirPort.");
+    r = -1;
+  }
+  if (n_orport_advertised && !n_orport_advertised_ipv4 &&
+      !options->BridgeRelay) {
+    log_warn(LD_CONFIG, "Configured non-bridge only to listen on an IPv6 "
+             "address.");
+    r = -1;
+  }
+
+  if (n_low_port && options->AccountingMax) {
+    log(LOG_WARN, LD_CONFIG,
+          "You have set AccountingMax to use hibernation. You have also "
+          "chosen a low DirPort or OrPort. This combination can make Tor stop "
+          "working when it tries to re-attach the port after a period of "
+          "hibernation. Please choose a different port or turn off "
+          "hibernation unless you know this combination will work on your "
+          "platform.");
+  }
+
+  return r;
+}
+
 /** Return a list of port_cfg_t for client ports parsed from the
 /** Return a list of port_cfg_t for client ports parsed from the
  * options. */
  * options. */
 const smartlist_t *
 const smartlist_t *
-get_configured_client_ports(void)
+get_configured_ports(void)
 {
 {
-  if (!configured_client_ports)
-    configured_client_ports = smartlist_create();
-  return configured_client_ports;
+  if (!configured_ports)
+    configured_ports = smartlist_create();
+  return configured_ports;
+}
+
+/** Return the first advertised port of type <b>listener_type</b> in
+    <b>address_family</b>.  */
+int
+get_first_advertised_port_by_type_af(int listener_type, int address_family)
+{
+  if (!configured_ports)
+    return 0;
+  SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) {
+    if (cfg->type == listener_type &&
+        !cfg->no_advertise &&
+        (tor_addr_family(&cfg->addr) == address_family ||
+         tor_addr_family(&cfg->addr) == AF_UNSPEC)) {
+      if (tor_addr_family(&cfg->addr) != AF_UNSPEC ||
+          (address_family == AF_INET && !cfg->ipv6_only) ||
+          (address_family == AF_INET6 && !cfg->ipv4_only)) {
+        return cfg->port;
+      }
+    }
+  } SMARTLIST_FOREACH_END(cfg);
+  return 0;
 }
 }
 
 
 /** Adjust the value of options->DataDirectory, or fill it in if it's
 /** Adjust the value of options->DataDirectory, or fill it in if it's
@@ -6094,7 +6304,7 @@ init_libevent(const or_options_t *options)
   suppress_libevent_log_msg(NULL);
   suppress_libevent_log_msg(NULL);
 
 
   tor_check_libevent_version(tor_libevent_get_method(),
   tor_check_libevent_version(tor_libevent_get_method(),
-                             get_options()->ORPort != 0,
+                             get_options()->ORPort != NULL,
                              &badness);
                              &badness);
   if (badness) {
   if (badness) {
     const char *v = tor_libevent_get_version_str();
     const char *v = tor_libevent_get_version_str();

+ 7 - 1
src/or/config.h

@@ -64,7 +64,13 @@ or_state_t *get_or_state(void);
 int did_last_state_file_write_fail(void);
 int did_last_state_file_write_fail(void);
 int or_state_save(time_t now);
 int or_state_save(time_t now);
 
 
-const smartlist_t *get_configured_client_ports(void);
+const smartlist_t *get_configured_ports(void);
+int get_first_advertised_port_by_type_af(int listener_type,
+                                         int address_family);
+#define get_primary_or_port() \
+  (get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, AF_INET))
+#define get_primary_dir_port() \
+  (get_first_advertised_port_by_type_af(CONN_TYPE_DIR_LISTENER, AF_INET))
 
 
 int options_need_geoip_info(const or_options_t *options,
 int options_need_geoip_info(const or_options_t *options,
                             const char **reason_out);
                             const char **reason_out);

+ 12 - 109
src/or/connection.c

@@ -880,7 +880,8 @@ connection_create_listener(const struct sockaddr *listensockaddr,
     return NULL;
     return NULL;
   }
   }
 
 
-  if (listensockaddr->sa_family == AF_INET) {
+  if (listensockaddr->sa_family == AF_INET ||
+      listensockaddr->sa_family == AF_INET6) {
     int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
     int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
     if (is_tcp)
     if (is_tcp)
       start_reading = 1;
       start_reading = 1;
@@ -890,7 +891,7 @@ connection_create_listener(const struct sockaddr *listensockaddr,
     log_notice(LD_NET, "Opening %s on %s:%d",
     log_notice(LD_NET, "Opening %s on %s:%d",
                conn_type_to_string(type), fmt_addr(&addr), usePort);
                conn_type_to_string(type), fmt_addr(&addr), usePort);
 
 
-    s = tor_open_socket(PF_INET,
+    s = tor_open_socket(tor_addr_family(&addr),
                         is_tcp ? SOCK_STREAM : SOCK_DGRAM,
                         is_tcp ? SOCK_STREAM : SOCK_DGRAM,
                         is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
                         is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
     if (!SOCKET_OK(s)) {
     if (!SOCKET_OK(s)) {
@@ -1814,6 +1815,9 @@ retry_listener_ports(smartlist_t *old_conns,
           (conn->socket_family == AF_UNIX && ! wanted->is_unix_addr))
           (conn->socket_family == AF_UNIX && ! wanted->is_unix_addr))
         continue;
         continue;
 
 
+      if (wanted->no_listen)
+        continue; /* We don't want to open a listener for this one */
+
       if (wanted->is_unix_addr) {
       if (wanted->is_unix_addr) {
         if (conn->socket_family == AF_UNIX &&
         if (conn->socket_family == AF_UNIX &&
             !strcmp(wanted->unix_addr, conn->address)) {
             !strcmp(wanted->unix_addr, conn->address)) {
@@ -1852,6 +1856,8 @@ retry_listener_ports(smartlist_t *old_conns,
     connection_t *conn;
     connection_t *conn;
     int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port;
     int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port;
     tor_assert(real_port <= UINT16_MAX);
     tor_assert(real_port <= UINT16_MAX);
+    if (port->no_listen)
+      continue;
 
 
     if (port->is_unix_addr) {
     if (port->is_unix_addr) {
       listensockaddr = (struct sockaddr *)
       listensockaddr = (struct sockaddr *)
@@ -1888,82 +1894,6 @@ retry_listener_ports(smartlist_t *old_conns,
   return r;
   return r;
 }
 }
 
 
-/**
- * Launch any configured listener connections of type <b>type</b>.  (A
- * listener is configured if <b>port_option</b> is non-zero.  If any
- * ListenAddress configuration options are given in <b>cfg</b>, create a
- * connection binding to each one.  Otherwise, create a single
- * connection binding to the address <b>default_addr</b>.)
- *
- * We assume that we're starting with a list of existing listener connection_t
- * pointers in <b>old_conns</b>: we do not launch listeners that are already
- * in that list.  Instead, we just remove them from the list.
- *
- * All new connections we launch are added to <b>new_conns</b>.
- */
-static int
-retry_listeners(smartlist_t *old_conns,
-                int type, const config_line_t *cfg,
-                int port_option, const char *default_addr,
-                smartlist_t *new_conns,
-                int is_sockaddr_un)
-{
-  smartlist_t *ports = smartlist_create();
-  tor_addr_t dflt_addr;
-  int retval = 0;
-
-  if (default_addr) {
-    tor_addr_parse(&dflt_addr, default_addr);
-  } else {
-    tor_addr_make_unspec(&dflt_addr);
-  }
-
-  if (port_option) {
-    if (!cfg) {
-      port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t));
-      tor_addr_copy(&port->addr, &dflt_addr);
-      port->port = port_option;
-      port->type = type;
-      smartlist_add(ports, port);
-    } else {
-      const config_line_t *c;
-      for (c = cfg; c; c = c->next) {
-        port_cfg_t *port;
-        tor_addr_t addr;
-        uint16_t portval = 0;
-        if (is_sockaddr_un) {
-          size_t len = strlen(c->value);
-          port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1);
-          port->is_unix_addr = 1;
-          memcpy(port->unix_addr, c->value, len+1);
-        } else {
-          if (tor_addr_port_lookup(c->value, &addr, &portval) < 0) {
-            log_warn(LD_CONFIG, "Can't parse/resolve %s %s",
-                     c->key, c->value);
-            retval = -1;
-            continue;
-          }
-          port = tor_malloc_zero(sizeof(port_cfg_t));
-          tor_addr_copy(&port->addr, &addr);
-        }
-        port->type = type;
-        port->port = portval ? portval : port_option;
-        smartlist_add(ports, port);
-      }
-    }
-  }
-
-  if (retval == -1)
-    goto cleanup;
-
-  retval = retry_listener_ports(old_conns, ports, new_conns);
-
- cleanup:
-  SMARTLIST_FOREACH(ports, port_cfg_t *, p, tor_free(p));
-  smartlist_free(ports);
-  return retval;
-}
-
 /** Launch listeners for each port you should have open.  Only launch
 /** Launch listeners for each port you should have open.  Only launch
  * listeners who are not already open, and only close listeners we no longer
  * listeners who are not already open, and only close listeners we no longer
  * want.
  * want.
@@ -1986,37 +1916,9 @@ retry_all_listeners(smartlist_t *replaced_conns,
       smartlist_add(listeners, conn);
       smartlist_add(listeners, conn);
   } SMARTLIST_FOREACH_END(conn);
   } SMARTLIST_FOREACH_END(conn);
 
 
-  if (! options->ClientOnly && ! options->DisableNetwork) {
-    if (retry_listeners(listeners,
-                        CONN_TYPE_OR_LISTENER, options->ORListenAddress,
-                        options->ORPort, "0.0.0.0",
-                        new_conns, 0) < 0)
-      retval = -1;
-    if (retry_listeners(listeners,
-                        CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
-                        options->DirPort, "0.0.0.0",
-                        new_conns, 0) < 0)
-      retval = -1;
-  }
-
-  if (!options->DisableNetwork) {
-    if (retry_listener_ports(listeners,
-                             get_configured_client_ports(),
-                             new_conns) < 0)
-      retval = -1;
-  }
-
-  if (retry_listeners(listeners,
-                      CONN_TYPE_CONTROL_LISTENER,
-                      options->ControlListenAddress,
-                      options->ControlPort, "127.0.0.1",
-                      new_conns, 0) < 0)
-    retval = -1;
-  if (retry_listeners(listeners,
-                      CONN_TYPE_CONTROL_LISTENER,
-                      options->ControlSocket,
-                      options->ControlSocket ? 1 : 0, NULL,
-                      new_conns, 1) < 0)
+  if (retry_listener_ports(listeners,
+                           get_configured_ports(),
+                           new_conns) < 0)
     retval = -1;
     retval = -1;
 
 
   /* Any members that were still in 'listeners' don't correspond to
   /* Any members that were still in 'listeners' don't correspond to
@@ -2034,6 +1936,7 @@ retry_all_listeners(smartlist_t *replaced_conns,
 
 
   smartlist_free(listeners);
   smartlist_free(listeners);
 
 
+  /* XXXprop186 should take all advertised ports into account */
   if (old_or_port != router_get_advertised_or_port(options) ||
   if (old_or_port != router_get_advertised_or_port(options) ||
       old_dir_port != router_get_advertised_dir_port(options, 0)) {
       old_dir_port != router_get_advertised_dir_port(options, 0)) {
     /* Our chosen ORPort or DirPort is not what it used to be: the
     /* Our chosen ORPort or DirPort is not what it used to be: the

+ 2 - 2
src/or/connection_edge.c

@@ -2593,7 +2593,7 @@ connection_ap_make_link(connection_t *partner,
            want_onehop ? "direct" : "anonymized",
            want_onehop ? "direct" : "anonymized",
            safe_str_client(address), port);
            safe_str_client(address), port);
 
 
-  conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
+  conn = entry_connection_new(CONN_TYPE_AP, tor_addr_family(&partner->addr));
   base_conn = ENTRY_TO_CONN(conn);
   base_conn = ENTRY_TO_CONN(conn);
   base_conn->linked = 1; /* so that we can add it safely below. */
   base_conn->linked = 1; /* so that we can add it safely below. */
 
 
@@ -3199,7 +3199,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn)
 
 
   exitconn->_base.state = EXIT_CONN_STATE_OPEN;
   exitconn->_base.state = EXIT_CONN_STATE_OPEN;
 
 
-  dirconn = dir_connection_new(AF_INET);
+  dirconn = dir_connection_new(tor_addr_family(&exitconn->_base.addr));
 
 
   tor_addr_copy(&dirconn->_base.addr, &exitconn->_base.addr);
   tor_addr_copy(&dirconn->_base.addr, &exitconn->_base.addr);
   dirconn->_base.port = 0;
   dirconn->_base.port = 0;

+ 12 - 9
src/or/connection_or.c

@@ -630,7 +630,9 @@ connection_or_update_token_buckets(smartlist_t *conns,
 
 
 /** If we don't necessarily know the router we're connecting to, but we
 /** If we don't necessarily know the router we're connecting to, but we
  * have an addr/port/id_digest, then fill in as much as we can. Start
  * have an addr/port/id_digest, then fill in as much as we can. Start
- * by checking to see if this describes a router we know. */
+ * by checking to see if this describes a router we know.
+ * <b>started_here</b> is 1 if we are the initiator of <b>conn</b> and
+ * 0 if it's an incoming connection.  */
 void
 void
 connection_or_init_conn_from_address(or_connection_t *conn,
 connection_or_init_conn_from_address(or_connection_t *conn,
                                      const tor_addr_t *addr, uint16_t port,
                                      const tor_addr_t *addr, uint16_t port,
@@ -645,10 +647,11 @@ connection_or_init_conn_from_address(or_connection_t *conn,
   tor_addr_copy(&conn->_base.addr, addr);
   tor_addr_copy(&conn->_base.addr, addr);
   tor_addr_copy(&conn->real_addr, addr);
   tor_addr_copy(&conn->real_addr, addr);
   if (r) {
   if (r) {
-    tor_addr_t node_addr;
-    node_get_addr(r, &node_addr);
-    /* XXXX proposal 118 will make this more complex. */
-    if (tor_addr_eq(&conn->_base.addr, &node_addr))
+    tor_addr_port_t node_ap;
+    node_get_pref_orport(r, &node_ap);
+    /* XXXX proposal 186 is making this more complex.  For now, a conn
+       is canonical when it uses the _preferred_ address. */
+    if (tor_addr_eq(&conn->_base.addr, &node_ap.addr))
       conn->is_canonical = 1;
       conn->is_canonical = 1;
     if (!started_here) {
     if (!started_here) {
       /* Override the addr/port, so our log messages will make sense.
       /* Override the addr/port, so our log messages will make sense.
@@ -661,12 +664,12 @@ connection_or_init_conn_from_address(or_connection_t *conn,
        * right IP address and port 56244, that wouldn't be as helpful. now we
        * right IP address and port 56244, that wouldn't be as helpful. now we
        * log the "right" port too, so we know if it's moria1 or moria2.
        * log the "right" port too, so we know if it's moria1 or moria2.
        */
        */
-      tor_addr_copy(&conn->_base.addr, &node_addr);
-      conn->_base.port = node_get_orport(r);
+      tor_addr_copy(&conn->_base.addr, &node_ap.addr);
+      conn->_base.port = node_ap.port;
     }
     }
     conn->nickname = tor_strdup(node_get_nickname(r));
     conn->nickname = tor_strdup(node_get_nickname(r));
     tor_free(conn->_base.address);
     tor_free(conn->_base.address);
-    conn->_base.address = tor_dup_addr(&node_addr);
+    conn->_base.address = tor_dup_addr(&node_ap.addr);
   } else {
   } else {
     const char *n;
     const char *n;
     /* If we're an authoritative directory server, we may know a
     /* If we're an authoritative directory server, we may know a
@@ -1033,7 +1036,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
     return NULL;
     return NULL;
   }
   }
 
 
-  conn = or_connection_new(AF_INET);
+  conn = or_connection_new(tor_addr_family(&addr));
 
 
   /* set up conn so it's got all the data we need to remember */
   /* set up conn so it's got all the data we need to remember */
   connection_or_init_conn_from_address(conn, &addr, port, id_digest, 1);
   connection_or_init_conn_from_address(conn, &addr, port, id_digest, 1);

+ 1 - 1
src/or/control.c

@@ -2402,7 +2402,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
   /* now circ refers to something that is ready to be extended */
   /* now circ refers to something that is ready to be extended */
   SMARTLIST_FOREACH(nodes, const node_t *, node,
   SMARTLIST_FOREACH(nodes, const node_t *, node,
   {
   {
-    extend_info_t *info = extend_info_from_node(node);
+    extend_info_t *info = extend_info_from_node(node, 0);
     tor_assert(info); /* True, since node_has_descriptor(node) == true */
     tor_assert(info); /* True, since node_has_descriptor(node) == true */
     circuit_append_new_exit(circ, info);
     circuit_append_new_exit(circ, info);
     extend_info_free(info);
     extend_info_free(info);

+ 6 - 4
src/or/directory.c

@@ -919,7 +919,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
     return;
     return;
   }
   }
 
 
-  conn = dir_connection_new(AF_INET);
+  conn = dir_connection_new(tor_addr_family(&addr));
 
 
   /* set up conn so it's got all the data we need to remember */
   /* set up conn so it's got all the data we need to remember */
   tor_addr_copy(&conn->_base.addr, &addr);
   tor_addr_copy(&conn->_base.addr, &addr);
@@ -1619,9 +1619,11 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
   if (!reason) reason = tor_strdup("[no reason given]");
   if (!reason) reason = tor_strdup("[no reason given]");
 
 
   log_debug(LD_DIR,
   log_debug(LD_DIR,
-            "Received response from directory server '%s:%d': %d %s",
+            "Received response from directory server '%s:%d': %d %s "
+            "(purpose: %d)",
             conn->_base.address, conn->_base.port, status_code,
             conn->_base.address, conn->_base.port, status_code,
-            escaped(reason));
+            escaped(reason),
+            conn->_base.purpose);
 
 
   /* now check if it's got any hints for us about our IP address. */
   /* now check if it's got any hints for us about our IP address. */
   if (conn->dirconn_direct) {
   if (conn->dirconn_direct) {
@@ -3729,7 +3731,7 @@ download_status_reset(download_status_t *dls)
   const int *schedule;
   const int *schedule;
   size_t schedule_len;
   size_t schedule_len;
 
 
-  find_dl_schedule_and_len(dls, get_options()->DirPort,
+  find_dl_schedule_and_len(dls, get_options()->DirPort != NULL,
                            &schedule, &schedule_len);
                            &schedule, &schedule_len);
 
 
   dls->n_download_failures = 0;
   dls->n_download_failures = 0;

+ 1 - 1
src/or/directory.h

@@ -80,7 +80,7 @@ time_t download_status_increment_failure(download_status_t *dls,
  * the optional status code <b>sc</b>. */
  * the optional status code <b>sc</b>. */
 #define download_status_failed(dls, sc)                                 \
 #define download_status_failed(dls, sc)                                 \
   download_status_increment_failure((dls), (sc), NULL,                  \
   download_status_increment_failure((dls), (sc), NULL,                  \
-                                    get_options()->DirPort, time(NULL))
+                                    get_options()->DirPort!=NULL, time(NULL))
 
 
 void download_status_reset(download_status_t *dls);
 void download_status_reset(download_status_t *dls);
 static int download_status_is_ready(download_status_t *dls, time_t now,
 static int download_status_is_ready(download_status_t *dls, time_t now,

+ 4 - 4
src/or/dirserv.c

@@ -1212,7 +1212,7 @@ directory_fetches_from_authorities(const or_options_t *options)
     return 1; /* we don't know our IP address; ask an authority. */
     return 1; /* we don't know our IP address; ask an authority. */
   refuseunknown = ! router_my_exit_policy_is_reject_star() &&
   refuseunknown = ! router_my_exit_policy_is_reject_star() &&
     should_refuse_unknown_exits(options);
     should_refuse_unknown_exits(options);
-  if (options->DirPort == 0 && !refuseunknown)
+  if (options->DirPort == NULL && !refuseunknown)
     return 0;
     return 0;
   if (!server_mode(options) || !advertised_server_mode())
   if (!server_mode(options) || !advertised_server_mode())
     return 0;
     return 0;
@@ -1248,7 +1248,7 @@ directory_fetches_dir_info_later(const or_options_t *options)
 int
 int
 directory_caches_v2_dir_info(const or_options_t *options)
 directory_caches_v2_dir_info(const or_options_t *options)
 {
 {
-  return options->DirPort != 0;
+  return options->DirPort != NULL;
 }
 }
 
 
 /** Return 1 if we want to keep descriptors, networkstatuses, etc around
 /** Return 1 if we want to keep descriptors, networkstatuses, etc around
@@ -1273,7 +1273,7 @@ directory_caches_dir_info(const or_options_t *options)
 int
 int
 directory_permits_begindir_requests(const or_options_t *options)
 directory_permits_begindir_requests(const or_options_t *options)
 {
 {
-  return options->BridgeRelay != 0 || options->DirPort != 0;
+  return options->BridgeRelay != 0 || options->DirPort != NULL;
 }
 }
 
 
 /** Return 1 if we want to allow controllers to ask us directory
 /** Return 1 if we want to allow controllers to ask us directory
@@ -1282,7 +1282,7 @@ directory_permits_begindir_requests(const or_options_t *options)
 int
 int
 directory_permits_controller_requests(const or_options_t *options)
 directory_permits_controller_requests(const or_options_t *options)
 {
 {
-  return options->DirPort != 0;
+  return options->DirPort != NULL;
 }
 }
 
 
 /** Return 1 if we have no need to fetch new descriptors. This generally
 /** Return 1 if we have no need to fetch new descriptors. This generally

+ 3 - 2
src/or/main.c

@@ -1511,9 +1511,10 @@ run_scheduled_events(time_t now)
       options->PortForwarding &&
       options->PortForwarding &&
       is_server) {
       is_server) {
 #define PORT_FORWARDING_CHECK_INTERVAL 5
 #define PORT_FORWARDING_CHECK_INTERVAL 5
+    /* XXXXX this should take a list of ports, not just two! */
     tor_check_port_forwarding(options->PortForwardingHelper,
     tor_check_port_forwarding(options->PortForwardingHelper,
-                              options->DirPort,
-                              options->ORPort,
+                              get_primary_dir_port(),
+                              get_primary_or_port(),
                               now);
                               now);
     time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
     time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
   }
   }

+ 88 - 25
src/or/nodelist.c

@@ -646,24 +646,70 @@ node_exit_policy_rejects_all(const node_t *node)
     return 1;
     return 1;
 }
 }
 
 
-/** Copy the address for <b>node</b> into *<b>addr_out</b>. */
-int
-node_get_addr(const node_t *node, tor_addr_t *addr_out)
+/** Return list of tor_addr_port_t with all OR ports (in the sense IP
+ * addr + TCP port) for <b>node</b>.  Caller must free all elements
+ * using tor_free() and free the list using smartlist_free().
+ *
+ * XXX this is potentially a memory fragmentation hog -- if on
+ * critical path consider the option of having the caller allocate the
+ * memory
+ */
+smartlist_t *
+node_get_all_orports(const node_t *node)
+{
+  smartlist_t *sl = smartlist_create();
+
+  if (node->ri != NULL) {
+    if (node->ri->addr != 0) {
+      tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
+      tor_addr_from_ipv4h(&ap->addr, node->ri->addr);
+      ap->port = node->ri->or_port;
+      smartlist_add(sl, ap);
+    }
+    if (!tor_addr_is_null(&node->ri->ipv6_addr)) {
+      tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
+      tor_addr_copy(&ap->addr, &node->ri->ipv6_addr);
+      ap->port = node->ri->or_port;
+      smartlist_add(sl, ap);
+    }
+  } else if (node->rs != NULL) {
+      tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
+      tor_addr_from_ipv4h(&ap->addr, node->rs->addr);
+      ap->port = node->rs->or_port;
+      smartlist_add(sl, ap);
+  }
+
+  return sl;
+}
+
+/** Copy the primary (IPv4) OR port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>.  */
+void
+node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
 {
 {
   if (node->ri) {
   if (node->ri) {
-    tor_addr_from_ipv4h(addr_out, node->ri->addr);
-    return 0;
-  } else if (node->rs) {
-    tor_addr_from_ipv4h(addr_out, node->rs->addr);
-    return 0;
+    router_get_prim_orport(node->ri, ap_out);
   }
   }
-  return -1;
+  else if (node->rs) {
+    tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr);
+    ap_out->port = node->rs->or_port;
+  }
+}
+
+/** Wrapper around node_get_prim_orport for backward
+    compatibility.  */
+void
+node_get_addr(const node_t *node, tor_addr_t *addr_out)
+{
+  tor_addr_port_t ap;
+  node_get_prim_orport(node, &ap);
+  tor_addr_copy(addr_out, &ap.addr);
 }
 }
 
 
 /** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't
 /** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't
  * seem to have one.  */
  * seem to have one.  */
 uint32_t
 uint32_t
-node_get_addr_ipv4h(const node_t *node)
+node_get_prim_addr_ipv4h(const node_t *node)
 {
 {
   if (node->ri) {
   if (node->ri) {
     return node->ri->addr;
     return node->ri->addr;
@@ -673,9 +719,38 @@ node_get_addr_ipv4h(const node_t *node)
   return 0;
   return 0;
 }
 }
 
 
-/** Copy a string representation of the IP address for <b>node</b> into the
- * <b>len</b>-byte buffer at <b>buf</b>.
- */
+/** Copy the preferred OR port (IP address and TCP port) for
+ * <b>node</b> into <b>ap_out</b>.  */
+void
+node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out)
+{
+  if (node->ri) {
+    router_get_pref_orport(node->ri, ap_out);
+  } else if (node->rs) {
+    /* No IPv6 in routerstatus_t yet.  XXXprop186 ok for private
+       bridges but needs fixing */
+    tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr);
+    ap_out->port = node->rs->or_port;
+  }
+}
+
+/** Copy the preferred IPv6 OR port (address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
+{
+  if (node->ri) {
+    router_get_pref_ipv6_orport(node->ri, ap_out);
+  } else if (node->rs) {
+    /* No IPv6 in routerstatus_t yet.  XXXprop186 ok for private
+       bridges but needs fixing */
+    tor_addr_make_unspec(&ap_out->addr);
+    ap_out->port = 0;
+  }
+}
+
+/** Copy a string representation of an IP address for <b>node</b> into
+ * the <b>len</b>-byte buffer at <b>buf</b>.  */
 void
 void
 node_get_address_string(const node_t *node, char *buf, size_t len)
 node_get_address_string(const node_t *node, char *buf, size_t len)
 {
 {
@@ -701,18 +776,6 @@ node_get_declared_uptime(const node_t *node)
     return -1;
     return -1;
 }
 }
 
 
-/** Return <b>node</b>'s declared or_port */
-uint16_t
-node_get_orport(const node_t *node)
-{
-  if (node->ri)
-    return node->ri->or_port;
-  else if (node->rs)
-    return node->rs->or_port;
-  else
-    return 0;
-}
-
 /** Return <b>node</b>'s platform string, or NULL if we don't know it. */
 /** Return <b>node</b>'s platform string, or NULL if we don't know it. */
 const char *
 const char *
 node_get_platform(const node_t *node)
 node_get_platform(const node_t *node)

+ 10 - 3
src/or/nodelist.h

@@ -37,10 +37,13 @@ int node_get_purpose(const node_t *node);
   (node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE)
   (node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE)
 int node_is_me(const node_t *node);
 int node_is_me(const node_t *node);
 int node_exit_policy_rejects_all(const node_t *node);
 int node_exit_policy_rejects_all(const node_t *node);
-int node_get_addr(const node_t *node, tor_addr_t *addr_out);
-uint32_t node_get_addr_ipv4h(const node_t *node);
+smartlist_t *node_get_all_orports(const node_t *node);
+void node_get_prim_orport(const node_t *node, tor_addr_port_t *addr_port_out);
+void node_get_pref_orport(const node_t *node, tor_addr_port_t *addr_port_out);
+void node_get_pref_ipv6_orport(const node_t *node,
+                               tor_addr_port_t *addr_port_out);
+uint32_t node_get_prim_addr_ipv4h(const node_t *node);
 int node_allows_single_hop_exits(const node_t *node);
 int node_allows_single_hop_exits(const node_t *node);
-uint16_t node_get_orport(const node_t *node);
 const char *node_get_nickname(const node_t *node);
 const char *node_get_nickname(const node_t *node);
 const char *node_get_platform(const node_t *node);
 const char *node_get_platform(const node_t *node);
 void node_get_address_string(const node_t *node, char *cp, size_t len);
 void node_get_address_string(const node_t *node, char *cp, size_t len);
@@ -50,6 +53,10 @@ const smartlist_t *node_get_declared_family(const node_t *node);
 
 
 smartlist_t *nodelist_get_list(void);
 smartlist_t *nodelist_get_list(void);
 
 
+/* Temporary during transition to multiple addresses.  */
+void node_get_addr(const node_t *node, tor_addr_t *addr_out);
+#define node_get_addr_ipv4h(n) node_get_prim_addr_ipv4h((n))
+
 /* XXXX These need to move out of routerlist.c */
 /* XXXX These need to move out of routerlist.c */
 void nodelist_refresh_countries(void);
 void nodelist_refresh_countries(void);
 void node_set_country(node_t *node);
 void node_set_country(node_t *node);

+ 19 - 3
src/or/or.h

@@ -1722,6 +1722,13 @@ typedef struct {
   uint16_t or_port; /**< Port for TLS connections. */
   uint16_t or_port; /**< Port for TLS connections. */
   uint16_t dir_port; /**< Port for HTTP directory connections. */
   uint16_t dir_port; /**< Port for HTTP directory connections. */
 
 
+  /* DOCDOC */
+  /* XXXXX187 Actually these should probably be part of a list of addresses,
+   * not just a special case.  Use abstractions to access these; don't do it
+   * directly. */
+  tor_addr_t ipv6_addr;
+  uint16_t ipv6_orport;
+
   crypto_pk_env_t *onion_pkey; /**< Public RSA key for onions. */
   crypto_pk_env_t *onion_pkey; /**< Public RSA key for onions. */
   crypto_pk_env_t *identity_pkey;  /**< Public RSA key for signing. */
   crypto_pk_env_t *identity_pkey;  /**< Public RSA key for signing. */
 
 
@@ -1753,6 +1760,8 @@ typedef struct {
   /** True if, after we have added this router, we should re-launch
   /** True if, after we have added this router, we should re-launch
    * tests for it. */
    * tests for it. */
   unsigned int needs_retest_if_added:1;
   unsigned int needs_retest_if_added:1;
+  /** True if ipv6_addr:ipv6_orport is preferred.  */
+  unsigned int ipv6_preferred:1;
 
 
 /** Tor can use this router for general positions in circuits; we got it
 /** Tor can use this router for general positions in circuits; we got it
  * from a directory server as usual, or we're an authority and a server
  * from a directory server as usual, or we're an authority and a server
@@ -2833,6 +2842,13 @@ typedef struct port_cfg_t {
   int session_group; /**< A session group, or -1 if this port is not in a
   int session_group; /**< A session group, or -1 if this port is not in a
                       * session group. */
                       * session group. */
 
 
+  /* Server port types (or, dir) only: */
+  unsigned int no_advertise : 1;
+  unsigned int no_listen : 1;
+  unsigned int all_addrs : 1;
+  unsigned int ipv4_only : 1;
+  unsigned int ipv6_only : 1;
+
   /* Unix sockets only: */
   /* Unix sockets only: */
   /** Path for an AF_UNIX address */
   /** Path for an AF_UNIX address */
   char unix_addr[FLEXIBLE_ARRAY_MEMBER];
   char unix_addr[FLEXIBLE_ARRAY_MEMBER];
@@ -2948,17 +2964,17 @@ typedef struct {
   int DirAllowPrivateAddresses;
   int DirAllowPrivateAddresses;
   char *User; /**< Name of user to run Tor as. */
   char *User; /**< Name of user to run Tor as. */
   char *Group; /**< Name of group to run Tor as. */
   char *Group; /**< Name of group to run Tor as. */
-  int ORPort; /**< Port to listen on for OR connections. */
+  config_line_t *ORPort; /**< Ports to listen on for OR connections. */
   config_line_t *SocksPort; /**< Ports to listen on for SOCKS connections. */
   config_line_t *SocksPort; /**< Ports to listen on for SOCKS connections. */
   /** Ports to listen on for transparent pf/netfilter connections. */
   /** Ports to listen on for transparent pf/netfilter connections. */
   config_line_t *TransPort;
   config_line_t *TransPort;
   config_line_t *NATDPort; /**< Ports to listen on for transparent natd
   config_line_t *NATDPort; /**< Ports to listen on for transparent natd
                             * connections. */
                             * connections. */
-  int ControlPort; /**< Port to listen on for control connections. */
+  config_line_t *ControlPort; /**< Port to listen on for control connections. */
   config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
   config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
                                  * for control connections. */
                                  * for control connections. */
   int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */
   int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */
-  int DirPort; /**< Port to listen on for directory connections. */
+  config_line_t *DirPort; /**< Port to listen on for directory connections. */
   config_line_t *DNSPort; /**< Port to listen on for DNS requests. */
   config_line_t *DNSPort; /**< Port to listen on for DNS requests. */
   int AssumeReachable; /**< Whether to publish our descriptor regardless. */
   int AssumeReachable; /**< Whether to publish our descriptor regardless. */
   int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */
   int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */

+ 1 - 1
src/or/rendclient.c

@@ -1059,7 +1059,7 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
       smartlist_del(usable_nodes, i);
       smartlist_del(usable_nodes, i);
       goto again;
       goto again;
     }
     }
-    new_extend_info = extend_info_from_node(node);
+    new_extend_info = extend_info_from_node(node, 0);
     if (!new_extend_info) {
     if (!new_extend_info) {
       log_info(LD_REND, "We don't have a descriptor for the intro-point relay "
       log_info(LD_REND, "We don't have a descriptor for the intro-point relay "
                "'%s'; trying another.",
                "'%s'; trying another.",

+ 2 - 2
src/or/rendservice.c

@@ -1142,7 +1142,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
       goto err;
       goto err;
     }
     }
 
 
-    extend_info = extend_info_from_node(node);
+    extend_info = extend_info_from_node(node, 0);
   }
   }
 
 
   if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
   if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
@@ -2151,7 +2151,7 @@ rend_services_introduce(void)
       intro_point_set_changed = 1;
       intro_point_set_changed = 1;
       smartlist_add(intro_nodes, (void*)node);
       smartlist_add(intro_nodes, (void*)node);
       intro = tor_malloc_zero(sizeof(rend_intro_point_t));
       intro = tor_malloc_zero(sizeof(rend_intro_point_t));
-      intro->extend_info = extend_info_from_node(node);
+      intro->extend_info = extend_info_from_node(node, 0);
       intro->intro_key = crypto_new_pk_env();
       intro->intro_key = crypto_new_pk_env();
       tor_assert(!crypto_pk_generate_key(intro->intro_key));
       tor_assert(!crypto_pk_generate_key(intro->intro_key));
       intro->time_published = -1;
       intro->time_published = -1;

+ 104 - 9
src/or/router.c

@@ -893,7 +893,8 @@ consider_testing_reachability(int test_or, int test_dir)
     log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
     log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
              !orport_reachable ? "reachability" : "bandwidth",
              !orport_reachable ? "reachability" : "bandwidth",
              me->address, me->or_port);
              me->address, me->or_port);
-    ei = extend_info_from_router(me);
+    /* XXX IPv6 self testing IPv6 orports will need pref_addr */
+    ei = extend_info_from_router(me, 0);
     circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
     circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
                             CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
                             CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
     extend_info_free(ei);
     extend_info_free(ei);
@@ -1083,7 +1084,7 @@ int
 server_mode(const or_options_t *options)
 server_mode(const or_options_t *options)
 {
 {
   if (options->ClientOnly) return 0;
   if (options->ClientOnly) return 0;
-  return (options->ORPort != 0 || options->ORListenAddress);
+  return (options->ORPort || options->ORListenAddress);
 }
 }
 
 
 /** Return true iff we are trying to be a non-bridge server.
 /** Return true iff we are trying to be a non-bridge server.
@@ -1134,7 +1135,14 @@ int
 proxy_mode(const or_options_t *options)
 proxy_mode(const or_options_t *options)
 {
 {
   (void)options;
   (void)options;
-  return smartlist_len(get_configured_client_ports()) > 0;
+  SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) {
+    if (p->type == CONN_TYPE_AP_LISTENER ||
+        p->type == CONN_TYPE_AP_TRANS_LISTENER ||
+        p->type == CONN_TYPE_AP_DNS_LISTENER ||
+        p->type == CONN_TYPE_AP_NATD_LISTENER)
+      return 1;
+  } SMARTLIST_FOREACH_END(p);
+  return 0;
 }
 }
 
 
 /** Decide if we're a publishable server. We are a publishable server if:
 /** Decide if we're a publishable server. We are a publishable server if:
@@ -1193,17 +1201,21 @@ consider_publishable_server(int force)
 
 
 /** Return the port that we should advertise as our ORPort; this is either
 /** 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
  * the one configured in the ORPort option, or the one we actually bound to
- * if ORPort is "auto". */
+ * if ORPort is "auto".
+ */
 uint16_t
 uint16_t
 router_get_advertised_or_port(const or_options_t *options)
 router_get_advertised_or_port(const or_options_t *options)
 {
 {
-  if (options->ORPort == CFG_AUTO_PORT) {
+  int port = get_primary_or_port();
+  (void)options;
+
+  if (port == CFG_AUTO_PORT) {
     connection_t *c = connection_get_by_type(CONN_TYPE_OR_LISTENER);
     connection_t *c = connection_get_by_type(CONN_TYPE_OR_LISTENER);
     if (c)
     if (c)
       return c->port;
       return c->port;
     return 0;
     return 0;
   }
   }
-  return options->ORPort;
+  return port;
 }
 }
 
 
 /** Return the port that we should advertise as our DirPort;
 /** Return the port that we should advertise as our DirPort;
@@ -1214,15 +1226,18 @@ router_get_advertised_or_port(const or_options_t *options)
 uint16_t
 uint16_t
 router_get_advertised_dir_port(const or_options_t *options, uint16_t dirport)
 router_get_advertised_dir_port(const or_options_t *options, uint16_t dirport)
 {
 {
-  if (!options->DirPort)
+  int dirport_configured = get_primary_dir_port();
+  (void)options;
+
+  if (!dirport_configured)
     return dirport;
     return dirport;
-  if (options->DirPort == CFG_AUTO_PORT) {
+  if (dirport_configured == CFG_AUTO_PORT) {
     connection_t *c = connection_get_by_type(CONN_TYPE_DIR_LISTENER);
     connection_t *c = connection_get_by_type(CONN_TYPE_DIR_LISTENER);
     if (c)
     if (c)
       return c->port;
       return c->port;
     return 0;
     return 0;
   }
   }
-  return options->DirPort;
+  return dirport_configured;
 }
 }
 
 
 /*
 /*
@@ -1479,6 +1494,24 @@ router_rebuild_descriptor(int force)
   ri->cache_info.published_on = time(NULL);
   ri->cache_info.published_on = time(NULL);
   ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from
   ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from
                                                         * main thread */
                                                         * main thread */
+  if (options->BridgeRelay) {
+    /* For now, only bridges advertise an ipv6 or-address.  And only one. */
+    const port_cfg_t *ipv6_orport = NULL;
+    SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) {
+      if (p->type == CONN_TYPE_OR_LISTENER &&
+          ! p->no_advertise &&
+          ! p->ipv4_only &&
+          tor_addr_family(&p->addr) == AF_INET6 &&
+          ! tor_addr_is_internal(&p->addr, 1)) {
+        ipv6_orport = p;
+        break;
+      }
+    } SMARTLIST_FOREACH_END(p);
+    if (ipv6_orport) {
+      tor_addr_copy(&ri->ipv6_addr, &ipv6_orport->addr);
+      ri->ipv6_orport = ipv6_orport->port;
+    }
+  }
   ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key());
   ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key());
   if (crypto_pk_get_digest(ri->identity_pkey,
   if (crypto_pk_get_digest(ri->identity_pkey,
                            ri->cache_info.identity_digest)<0) {
                            ri->cache_info.identity_digest)<0) {
@@ -1888,6 +1921,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
   int result=0;
   int result=0;
   addr_policy_t *tmpe;
   addr_policy_t *tmpe;
   char *family_line;
   char *family_line;
+  char *extra_or_address = NULL;
   const or_options_t *options = get_options();
   const or_options_t *options = get_options();
 
 
   /* Make sure the identity key matches the one in the routerinfo. */
   /* Make sure the identity key matches the one in the routerinfo. */
@@ -1940,9 +1974,22 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
                   router->cache_info.extra_info_digest, DIGEST_LEN);
                   router->cache_info.extra_info_digest, DIGEST_LEN);
   }
   }
 
 
+  if (router->ipv6_orport &&
+      tor_addr_family(&router->ipv6_addr) == AF_INET6) {
+    char addr[TOR_ADDR_BUF_LEN];
+    const char *a;
+    a = tor_addr_to_str(addr, &router->ipv6_addr, sizeof(addr), 1);
+    if (a) {
+      tor_asprintf(&extra_or_address,
+                   "or-address %s:%d\n", a, router->ipv6_orport);
+      log_notice(LD_OR, "My line is <%s>", extra_or_address);
+    }
+  }
+
   /* Generate the easy portion of the router descriptor. */
   /* Generate the easy portion of the router descriptor. */
   result = tor_snprintf(s, maxlen,
   result = tor_snprintf(s, maxlen,
                     "router %s %s %d 0 %d\n"
                     "router %s %s %d 0 %d\n"
+                    "%s"
                     "platform %s\n"
                     "platform %s\n"
                     "opt protocols Link 1 2 Circuit 1\n"
                     "opt protocols Link 1 2 Circuit 1\n"
                     "published %s\n"
                     "published %s\n"
@@ -1957,6 +2004,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
     router->address,
     router->address,
     router->or_port,
     router->or_port,
     decide_to_advertise_dirport(options, router->dir_port),
     decide_to_advertise_dirport(options, router->dir_port),
+    extra_or_address ? extra_or_address : "",
     router->platform,
     router->platform,
     published,
     published,
     fingerprint,
     fingerprint,
@@ -1977,6 +2025,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
   tor_free(family_line);
   tor_free(family_line);
   tor_free(onion_pkey);
   tor_free(onion_pkey);
   tor_free(identity_pkey);
   tor_free(identity_pkey);
+  tor_free(extra_or_address);
 
 
   if (result < 0) {
   if (result < 0) {
     log_warn(LD_BUG,"descriptor snprintf #1 ran out of room!");
     log_warn(LD_BUG,"descriptor snprintf #1 ran out of room!");
@@ -2072,6 +2121,52 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
   return (int)written+1;
   return (int)written+1;
 }
 }
 
 
+/** Copy the primary (IPv4) OR port (IP address and TCP port) for
+ * <b>router</b> into *<b>ap_out</b>. */
+void
+router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out)
+{
+  tor_assert(ap_out != NULL);
+  tor_addr_from_ipv4h(&ap_out->addr, router->addr);
+  ap_out->port = router->or_port;
+}
+
+/** Return 1 if we prefer the IPv6 address and OR TCP port of
+ * <b>router</b>, else 0.
+ *
+ *  We prefer the IPv6 address if the router has one and
+ *  i) the routerinfo_t says so
+ *  or
+ *  ii) the router has no IPv4 address.  */
+int
+router_ipv6_preferred(const routerinfo_t *router)
+{
+  return (!tor_addr_is_null(&router->ipv6_addr)
+          && (router->ipv6_preferred || router->addr == 0));
+}
+
+/** Copy the preferred OR port (IP address and TCP port) for
+ * <b>router</b> into *<b>addr_out</b>.  */
+void
+router_get_pref_orport(const routerinfo_t *router, tor_addr_port_t *ap_out)
+{
+  if (router_ipv6_preferred(router))
+    router_get_pref_ipv6_orport(router, ap_out);
+  else
+    router_get_prim_orport(router, ap_out);
+}
+
+/** Copy the preferred IPv6 OR port (IP address and TCP port) for
+ * <b>router</b> into *<b>ap_out</b>. */
+void
+router_get_pref_ipv6_orport(const routerinfo_t *router,
+                            tor_addr_port_t *ap_out)
+{
+  tor_assert(ap_out != NULL);
+  tor_addr_copy(&ap_out->addr, &router->ipv6_addr);
+  ap_out->port = router->ipv6_orport;
+}
+
 /** Load the contents of <b>filename</b>, find the last line starting with
 /** Load the contents of <b>filename</b>, find the last line starting with
  * <b>end_line</b>, ensure that its timestamp is not more than 25 hours in
  * <b>end_line</b>, ensure that its timestamp is not more than 25 hours in
  * the past or more than 1 hour in the future with respect to <b>now</b>,
  * the past or more than 1 hour in the future with respect to <b>now</b>,

+ 7 - 0
src/or/router.h

@@ -85,6 +85,13 @@ int router_pick_published_address(const or_options_t *options, uint32_t *addr);
 int router_rebuild_descriptor(int force);
 int router_rebuild_descriptor(int force);
 int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
 int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
                                  crypto_pk_env_t *ident_key);
                                  crypto_pk_env_t *ident_key);
+void router_get_prim_orport(const routerinfo_t *router,
+                            tor_addr_port_t *addr_port_out);
+void router_get_pref_orport(const routerinfo_t *router,
+                            tor_addr_port_t *addr_port_out);
+void router_get_pref_ipv6_orport(const routerinfo_t *router,
+                                 tor_addr_port_t *addr_port_out);
+int router_ipv6_preferred(const routerinfo_t *router);
 int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo,
 int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo,
                              crypto_pk_env_t *ident_key);
                              crypto_pk_env_t *ident_key);
 int is_legal_nickname(const char *s);
 int is_legal_nickname(const char *s);

+ 38 - 0
src/or/routerparse.c

@@ -64,6 +64,7 @@ typedef enum {
   K_DIR_OPTIONS,
   K_DIR_OPTIONS,
   K_CLIENT_VERSIONS,
   K_CLIENT_VERSIONS,
   K_SERVER_VERSIONS,
   K_SERVER_VERSIONS,
+  K_OR_ADDRESS,
   K_P,
   K_P,
   K_R,
   K_R,
   K_S,
   K_S,
@@ -286,6 +287,7 @@ static token_rule_t routerdesc_token_table[] = {
 
 
   T01("family",              K_FAMILY,              ARGS,    NO_OBJ ),
   T01("family",              K_FAMILY,              ARGS,    NO_OBJ ),
   T01("caches-extra-info",   K_CACHES_EXTRA_INFO,   NO_ARGS, NO_OBJ ),
   T01("caches-extra-info",   K_CACHES_EXTRA_INFO,   NO_ARGS, NO_OBJ ),
+  T0N("or-address",          K_OR_ADDRESS,          GE(1),   NO_OBJ ),
 
 
   T0N("opt",                 K_OPT,             CONCAT_ARGS, OBJ_OK ),
   T0N("opt",                 K_OPT,             CONCAT_ARGS, OBJ_OK ),
   T1( "bandwidth",           K_BANDWIDTH,           GE(3),   NO_OBJ ),
   T1( "bandwidth",           K_BANDWIDTH,           GE(3),   NO_OBJ ),
@@ -541,6 +543,7 @@ static int router_get_hashes_impl(const char *s, size_t s_len,
                                   const char *start_str, const char *end_str,
                                   const char *start_str, const char *end_str,
                                   char end_char);
                                   char end_char);
 static void token_clear(directory_token_t *tok);
 static void token_clear(directory_token_t *tok);
+static smartlist_t *find_all_by_keyword(smartlist_t *s, directory_keyword k);
 static smartlist_t *find_all_exitpolicy(smartlist_t *s);
 static smartlist_t *find_all_exitpolicy(smartlist_t *s);
 static directory_token_t *_find_by_keyword(smartlist_t *s,
 static directory_token_t *_find_by_keyword(smartlist_t *s,
                                            directory_keyword keyword,
                                            directory_keyword keyword,
@@ -1506,6 +1509,27 @@ router_parse_entry_from_string(const char *s, const char *end,
              "older Tors.");
              "older Tors.");
     goto err;
     goto err;
   }
   }
+  {
+    smartlist_t *or_addresses = find_all_by_keyword(tokens, K_OR_ADDRESS);
+    if (or_addresses) {
+      SMARTLIST_FOREACH_BEGIN(or_addresses, directory_token_t *, t) {
+        tor_addr_t a;
+        maskbits_t bits;
+        uint16_t port_min, port_max;
+        /* XXXX Prop186 the full spec allows much more than this. */
+        if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min,
+                                      &port_max) == AF_INET6 &&
+            bits == 128 &&
+            port_min == port_max) {
+          /* Okay, this is one we can understand. */
+          tor_addr_copy(&router->ipv6_addr, &a);
+          router->ipv6_orport = port_min;
+          break;
+        }
+      } SMARTLIST_FOREACH_END(t);
+      smartlist_free(or_addresses);
+    }
+  }
   exit_policy_tokens = find_all_exitpolicy(tokens);
   exit_policy_tokens = find_all_exitpolicy(tokens);
   if (!smartlist_len(exit_policy_tokens)) {
   if (!smartlist_len(exit_policy_tokens)) {
     log_warn(LD_DIR, "No exit policy tokens in descriptor.");
     log_warn(LD_DIR, "No exit policy tokens in descriptor.");
@@ -4134,6 +4158,20 @@ _find_by_keyword(smartlist_t *s, directory_keyword keyword,
   return tok;
   return tok;
 }
 }
 
 
+/** DOCDOC */
+static smartlist_t *
+find_all_by_keyword(smartlist_t *s, directory_keyword k)
+{
+  smartlist_t *out = NULL;
+  SMARTLIST_FOREACH(s, directory_token_t *, t,
+                    if (t->tp == k) {
+                      if (!out)
+                        out = smartlist_create();
+                      smartlist_add(out, t);
+                    });
+  return out;
+}
+
 /** Return a newly allocated smartlist of all accept or reject tokens in
 /** Return a newly allocated smartlist of all accept or reject tokens in
  * <b>s</b>.
  * <b>s</b>.
  */
  */

+ 3 - 1
src/or/transports.c

@@ -12,6 +12,7 @@
 #include "circuitbuild.h"
 #include "circuitbuild.h"
 #include "transports.h"
 #include "transports.h"
 #include "util.h"
 #include "util.h"
+#include "router.h"
 
 
 #ifdef MS_WINDOWS
 #ifdef MS_WINDOWS
 static void set_managed_proxy_environment(LPVOID *envp,
 static void set_managed_proxy_environment(LPVOID *envp,
@@ -1079,7 +1080,8 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
     bindaddr = get_bindaddr_for_proxy(mp);
     bindaddr = get_bindaddr_for_proxy(mp);
 
 
     /* XXX temp */
     /* XXX temp */
-    tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d", options->ORPort);
+    tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d",
+                 router_get_advertised_or_port(options));
     tor_asprintf(tmp++, "TOR_PT_SERVER_BINDADDR=%s", bindaddr);
     tor_asprintf(tmp++, "TOR_PT_SERVER_BINDADDR=%s", bindaddr);
     tor_asprintf(tmp++, "TOR_PT_SERVER_TRANSPORTS=%s", transports_to_launch);
     tor_asprintf(tmp++, "TOR_PT_SERVER_TRANSPORTS=%s", transports_to_launch);
     /* XXX temp*/
     /* XXX temp*/

+ 3 - 0
src/test/test_dir.c

@@ -96,6 +96,8 @@ test_dir_formats(void)
   r1->cache_info.published_on = 0;
   r1->cache_info.published_on = 0;
   r1->or_port = 9000;
   r1->or_port = 9000;
   r1->dir_port = 9003;
   r1->dir_port = 9003;
+  tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
+  r1->ipv6_orport = 9999;
   r1->onion_pkey = crypto_pk_dup_key(pk1);
   r1->onion_pkey = crypto_pk_dup_key(pk1);
   r1->identity_pkey = crypto_pk_dup_key(pk2);
   r1->identity_pkey = crypto_pk_dup_key(pk2);
   r1->bandwidthrate = 1000;
   r1->bandwidthrate = 1000;
@@ -141,6 +143,7 @@ test_dir_formats(void)
   test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
   test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
 
 
   strlcpy(buf2, "router Magri 18.244.0.1 9000 0 9003\n"
   strlcpy(buf2, "router Magri 18.244.0.1 9000 0 9003\n"
+          "or-address [1:2:3:4::]:9999\n"
           "platform Tor "VERSION" on ", sizeof(buf2));
           "platform Tor "VERSION" on ", sizeof(buf2));
   strlcat(buf2, get_uname(), sizeof(buf2));
   strlcat(buf2, get_uname(), sizeof(buf2));
   strlcat(buf2, "\n"
   strlcat(buf2, "\n"