소스 검색

Merge branch 'ipv6_exits'

Nick Mathewson 11 년 전
부모
커밋
ca0c71551e

+ 18 - 0
changes/ipv6_exits

@@ -0,0 +1,18 @@
+  o Major features:
+
+    - Tor now has (alpha) support for exiting to IPv6 addresses. To
+      enable it as an exit node, make sure that you have IPv6
+      connectivity, set the IPv6Exit flag to 1. Also make sure your
+      exit policy reads as you would like: the address * applies to
+      all address families, whereas *4 is IPv4 address only, and *6
+      is IPv6 addresses only.  On the client side, you'll need to
+      wait till the authorities have upgraded, wait for enough exits
+      to support IPv6, apply the "IPv6Traffic" flag to a SocksPort,
+      and use Socks5. Closes ticket 5547, implements proposal 117 as
+      revised in proposal 208.
+
+      We DO NOT recommend that clients with actual anonymity needs
+      start using IPv6 over Tor yet: not enough exits support it
+      yet, and there are some DNS-caching related issues that need
+      to be solved first.
+

+ 3 - 0
changes/split_addressmap

@@ -0,0 +1,3 @@
+  o Code simplification and refactoring:
+    - Move the client-side address-map/virtual-address/DNS-cache code
+      out of connection_edge.c into a new addressmap.c module.

+ 18 - 2
doc/tor.1.txt

@@ -864,7 +864,7 @@ The following options are useful only for clients (that is, if
     the same circuit. Currently, two addresses are "too close" if they lie in
     the same /16 range. (Default: 1)
 
-**SOCKSPort** \['address':]__port__|**auto** [_isolation flags_]::
+**SOCKSPort** \['address':]__port__|**auto** [_flags_] [_isolation flags_]::
     Open this port to listen for connections from SOCKS-speaking
     applications. Set this to 0 if you don't want to allow application
     connections via SOCKS. Set it to "auto" to have Tor pick a port for
@@ -897,7 +897,19 @@ The following options are useful only for clients (that is, if
         on this port to share circuits with streams from every other
         port with the same session group.  (By default, streams received
         on different SOCKSPorts, TransPorts, etc are always isolated from one
-        another. This option overrides that behavior.)
+        another. This option overrides that behavior.) +
++
+    Other recognized _flags_ for a SOCKSPort are:
+    **NoIPv4Traffic**;;
+        Tell exits to not connect to IPv4 addresses in response to SOCKS
+        requests on this connection.
+    **IPv6Traffic**;;
+        Tell exits to allow IPv6 addresses in response to SOCKS requests on
+        this connection, so long as SOCKS5 is in use.  (SOCKS4 can't handle
+        IPv6.)
+    **PreferIPv6**;;
+        Tells exits that, if a host has both an IPv4 and an IPv6 address,
+        we would prefer to connect to it via IPv6. (IPv4 is the default.)
 
 **SOCKSListenAddress** __IP__[:__PORT__]::
     Bind to this address to listen for connections from Socks-speaking
@@ -1275,6 +1287,10 @@ is non-zero):
     at the beginning of your exit policy. See above entry on ExitPolicy.
     (Default: 1)
 
+**IPv6Exit** **0**|**1**::
+    If set, and we are an exit node, allow clients to use us for IPv6
+    traffic. (Default: 0)
+
 **MaxOnionsPending** __NUM__::
     If you have more than this number of onionskins queued for decrypt, reject
     new ones. (Default: 100)

+ 39 - 2
src/common/address.c

@@ -181,6 +181,16 @@ tor_addr_make_unspec(tor_addr_t *a)
   a->family = AF_UNSPEC;
 }
 
+/** Set address <a>a</b> to the null address in address family <b>family</b>.
+ * The null address for AF_INET is 0.0.0.0.  The null address for AF_INET6 is
+ * [::].  AF_UNSPEC is all null. */
+void
+tor_addr_make_null(tor_addr_t *a, sa_family_t family)
+{
+  memset(a, 0, sizeof(*a));
+  a->family = family;
+}
+
 /** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set
  * *<b>addr</b> to the proper IP address and family. The <b>family</b>
  * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a
@@ -559,9 +569,22 @@ tor_addr_to_PTR_name(char *out, size_t outlen,
  *
  *  Return an address family on success, or -1 if an invalid address string is
  *  provided.
+ *
+ *  If 'flags & TAPMP_EXTENDED_STAR' is false, then the wildcard address '*'
+ *  yield an IPv4 wildcard.
+ *
+ *  If 'flags & TAPMP_EXTENDED_STAR' is true, then the wildcard address '*'
+ *  yields an AF_UNSPEC wildcard address, and the following change is made
+ *  in the grammar above:
+ *   Address ::= IPv4Address / "[" IPv6Address "]" / "*" / "*4" / "*6"
+ *  with the new "*4" and "*6" productions creating a wildcard to match
+ *  IPv4 or IPv6 addresses.
+ *
  */
 int
-tor_addr_parse_mask_ports(const char *s, tor_addr_t *addr_out,
+tor_addr_parse_mask_ports(const char *s,
+                          unsigned flags,
+                          tor_addr_t *addr_out,
                           maskbits_t *maskbits_out,
                           uint16_t *port_min_out, uint16_t *port_max_out)
 {
@@ -618,9 +641,23 @@ tor_addr_parse_mask_ports(const char *s, tor_addr_t *addr_out,
   memset(addr_out, 0, sizeof(tor_addr_t));
 
   if (!strcmp(address, "*")) {
-    family = AF_INET; /* AF_UNSPEC ???? XXXX_IP6 */
+    if (flags & TAPMP_EXTENDED_STAR) {
+      family = AF_UNSPEC;
+      tor_addr_make_unspec(addr_out);
+    } else {
+      family = AF_INET;
+      tor_addr_from_ipv4h(addr_out, 0);
+    }
+    any_flag = 1;
+  } else if (!strcmp(address, "*4") && (flags & TAPMP_EXTENDED_STAR)) {
+    family = AF_INET;
     tor_addr_from_ipv4h(addr_out, 0);
     any_flag = 1;
+  } else if (!strcmp(address, "*6") && (flags & TAPMP_EXTENDED_STAR)) {
+    static char nil_bytes[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
+    family = AF_INET6;
+    tor_addr_from_ipv6_bytes(addr_out, nil_bytes);
+    any_flag = 1;
   } else if (tor_inet_pton(AF_INET6, address, &in6_tmp) > 0) {
     family = AF_INET6;
     tor_addr_from_in6(addr_out, &in6_tmp);

+ 3 - 1
src/common/address.h

@@ -55,6 +55,7 @@ socklen_t tor_addr_to_sockaddr(const tor_addr_t *a, uint16_t port,
 int tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa,
                            uint16_t *port_out);
 void tor_addr_make_unspec(tor_addr_t *a);
+void tor_addr_make_null(tor_addr_t *a, sa_family_t family);
 char *tor_sockaddr_to_str(const struct sockaddr *sa);
 
 /** Return an in6_addr* equivalent to <b>a</b>, or NULL if <b>a</b> is not
@@ -183,7 +184,8 @@ int tor_addr_parse_PTR_name(tor_addr_t *result, const char *address,
 
 int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out,
                         uint16_t *port_out);
-int tor_addr_parse_mask_ports(const char *s,
+#define TAPMP_EXTENDED_STAR 1
+int tor_addr_parse_mask_ports(const char *s, unsigned flags,
                               tor_addr_t *addr_out, maskbits_t *mask_out,
                               uint16_t *port_min_out, uint16_t *port_max_out);
 const char * tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len,

+ 974 - 0
src/or/addressmap.c

@@ -0,0 +1,974 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "addressmap.h"
+#include "circuituse.h"
+#include "config.h"
+#include "connection_edge.h"
+#include "control.h"
+#include "dns.h"
+#include "routerset.h"
+#include "nodelist.h"
+
+/** A client-side struct to remember requests to rewrite addresses
+ * to new addresses. These structs are stored in the hash table
+ * "addressmap" below.
+ *
+ * There are 5 ways to set an address mapping:
+ * - A MapAddress command from the controller [permanent]
+ * - An AddressMap directive in the torrc [permanent]
+ * - When a TrackHostExits torrc directive is triggered [temporary]
+ * - When a DNS resolve succeeds [temporary]
+ * - When a DNS resolve fails [temporary]
+ *
+ * When an addressmap request is made but one is already registered,
+ * the new one is replaced only if the currently registered one has
+ * no "new_address" (that is, it's in the process of DNS resolve),
+ * or if the new one is permanent (expires==0 or 1).
+ *
+ * (We overload the 'expires' field, using "0" for mappings set via
+ * the configuration file, "1" for mappings set from the control
+ * interface, and other values for DNS and TrackHostExit mappings that can
+ * expire.)
+ *
+ * A mapping may be 'wildcarded'.  If "src_wildcard" is true, then
+ * any address that ends with a . followed by the key for this entry will
+ * get remapped by it.  If "dst_wildcard" is also true, then only the
+ * matching suffix of such addresses will get replaced by new_address.
+ */
+typedef struct {
+  char *new_address;
+  time_t expires;
+  addressmap_entry_source_t source:3;
+  unsigned src_wildcard:1;
+  unsigned dst_wildcard:1;
+  short num_resolve_failures;
+} addressmap_entry_t;
+
+/** Entry for mapping addresses to which virtual address we mapped them to. */
+typedef struct {
+  char *ipv4_address;
+  char *hostname_address;
+} virtaddress_entry_t;
+
+/** A hash table to store client-side address rewrite instructions. */
+static strmap_t *addressmap=NULL;
+/**
+ * Table mapping addresses to which virtual address, if any, we
+ * assigned them to.
+ *
+ * We maintain the following invariant: if [A,B] is in
+ * virtaddress_reversemap, then B must be a virtual address, and [A,B]
+ * must be in addressmap.  We do not require that the converse hold:
+ * if it fails, then we could end up mapping two virtual addresses to
+ * the same address, which is no disaster.
+ **/
+static strmap_t *virtaddress_reversemap=NULL;
+
+/** Initialize addressmap. */
+void
+addressmap_init(void)
+{
+  addressmap = strmap_new();
+  virtaddress_reversemap = strmap_new();
+}
+
+/** Free the memory associated with the addressmap entry <b>_ent</b>. */
+static void
+addressmap_ent_free(void *_ent)
+{
+  addressmap_entry_t *ent;
+  if (!_ent)
+    return;
+
+  ent = _ent;
+  tor_free(ent->new_address);
+  tor_free(ent);
+}
+
+/** Free storage held by a virtaddress_entry_t* entry in <b>ent</b>. */
+static void
+addressmap_virtaddress_ent_free(void *_ent)
+{
+  virtaddress_entry_t *ent;
+  if (!_ent)
+    return;
+
+  ent = _ent;
+  tor_free(ent->ipv4_address);
+  tor_free(ent->hostname_address);
+  tor_free(ent);
+}
+
+/** Free storage held by a virtaddress_entry_t* entry in <b>ent</b>. */
+static void
+addressmap_virtaddress_remove(const char *address, addressmap_entry_t *ent)
+{
+  if (ent && ent->new_address &&
+      address_is_in_virtual_range(ent->new_address)) {
+    virtaddress_entry_t *ve =
+      strmap_get(virtaddress_reversemap, ent->new_address);
+    /*log_fn(LOG_NOTICE,"remove reverse mapping for %s",ent->new_address);*/
+    if (ve) {
+      if (!strcmp(address, ve->ipv4_address))
+        tor_free(ve->ipv4_address);
+      if (!strcmp(address, ve->hostname_address))
+        tor_free(ve->hostname_address);
+      if (!ve->ipv4_address && !ve->hostname_address) {
+        tor_free(ve);
+        strmap_remove(virtaddress_reversemap, ent->new_address);
+      }
+    }
+  }
+}
+
+/** Remove <b>ent</b> (which must be mapped to by <b>address</b>) from the
+ * client address maps. */
+static void
+addressmap_ent_remove(const char *address, addressmap_entry_t *ent)
+{
+  addressmap_virtaddress_remove(address, ent);
+  addressmap_ent_free(ent);
+}
+
+/** Unregister all TrackHostExits mappings from any address to
+ * *.exitname.exit. */
+void
+clear_trackexithost_mappings(const char *exitname)
+{
+  char *suffix = NULL;
+  if (!addressmap || !exitname)
+    return;
+  tor_asprintf(&suffix, ".%s.exit", exitname);
+  tor_strlower(suffix);
+
+  STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) {
+    if (ent->source == ADDRMAPSRC_TRACKEXIT &&
+        !strcmpend(ent->new_address, suffix)) {
+      addressmap_ent_remove(address, ent);
+      MAP_DEL_CURRENT(address);
+    }
+  } STRMAP_FOREACH_END;
+
+  tor_free(suffix);
+}
+
+/** Remove all TRACKEXIT mappings from the addressmap for which the target
+ * host is unknown or no longer allowed, or for which the source address
+ * is no longer in trackexithosts. */
+void
+addressmap_clear_excluded_trackexithosts(const or_options_t *options)
+{
+  const routerset_t *allow_nodes = options->ExitNodes;
+  const routerset_t *exclude_nodes = options->ExcludeExitNodesUnion_;
+
+  if (!addressmap)
+    return;
+  if (routerset_is_empty(allow_nodes))
+    allow_nodes = NULL;
+  if (allow_nodes == NULL && routerset_is_empty(exclude_nodes))
+    return;
+
+  STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) {
+    size_t len;
+    const char *target = ent->new_address, *dot;
+    char *nodename;
+    const node_t *node;
+
+    if (!target) {
+      /* DNS resolving in progress */
+      continue;
+    } else if (strcmpend(target, ".exit")) {
+      /* Not a .exit mapping */
+      continue;
+    } else if (ent->source != ADDRMAPSRC_TRACKEXIT) {
+      /* Not a trackexit mapping. */
+      continue;
+    }
+    len = strlen(target);
+    if (len < 6)
+      continue; /* malformed. */
+    dot = target + len - 6; /* dot now points to just before .exit */
+    while (dot > target && *dot != '.')
+      dot--;
+    if (*dot == '.') dot++;
+    nodename = tor_strndup(dot, len-5-(dot-target));;
+    node = node_get_by_nickname(nodename, 0);
+    tor_free(nodename);
+    if (!node ||
+        (allow_nodes && !routerset_contains_node(allow_nodes, node)) ||
+        routerset_contains_node(exclude_nodes, node) ||
+        !hostname_in_track_host_exits(options, address)) {
+      /* We don't know this one, or we want to be rid of it. */
+      addressmap_ent_remove(address, ent);
+      MAP_DEL_CURRENT(address);
+    }
+  } STRMAP_FOREACH_END;
+}
+
+/** Remove all AUTOMAP mappings from the addressmap for which the
+ * source address no longer matches AutomapHostsSuffixes, which is
+ * no longer allowed by AutomapHostsOnResolve, or for which the
+ * target address is no longer in the virtual network. */
+void
+addressmap_clear_invalid_automaps(const or_options_t *options)
+{
+  int clear_all = !options->AutomapHostsOnResolve;
+  const smartlist_t *suffixes = options->AutomapHostsSuffixes;
+
+  if (!addressmap)
+    return;
+
+  if (!suffixes)
+    clear_all = 1; /* This should be impossible, but let's be sure. */
+
+  STRMAP_FOREACH_MODIFY(addressmap, src_address, addressmap_entry_t *, ent) {
+    int remove = clear_all;
+    if (ent->source != ADDRMAPSRC_AUTOMAP)
+      continue; /* not an automap mapping. */
+
+    if (!remove) {
+      int suffix_found = 0;
+      SMARTLIST_FOREACH(suffixes, const char *, suffix, {
+          if (!strcasecmpend(src_address, suffix)) {
+            suffix_found = 1;
+            break;
+          }
+      });
+      if (!suffix_found)
+        remove = 1;
+    }
+
+    if (!remove && ! address_is_in_virtual_range(ent->new_address))
+      remove = 1;
+
+    if (remove) {
+      addressmap_ent_remove(src_address, ent);
+      MAP_DEL_CURRENT(src_address);
+    }
+  } STRMAP_FOREACH_END;
+}
+
+/** Remove all entries from the addressmap that were set via the
+ * configuration file or the command line. */
+void
+addressmap_clear_configured(void)
+{
+  addressmap_get_mappings(NULL, 0, 0, 0);
+}
+
+/** Remove all entries from the addressmap that are set to expire, ever. */
+void
+addressmap_clear_transient(void)
+{
+  addressmap_get_mappings(NULL, 2, TIME_MAX, 0);
+}
+
+/** Clean out entries from the addressmap cache that were
+ * added long enough ago that they are no longer valid.
+ */
+void
+addressmap_clean(time_t now)
+{
+  addressmap_get_mappings(NULL, 2, now, 0);
+}
+
+/** Free all the elements in the addressmap, and free the addressmap
+ * itself. */
+void
+addressmap_free_all(void)
+{
+  strmap_free(addressmap, addressmap_ent_free);
+  addressmap = NULL;
+
+  strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free);
+  virtaddress_reversemap = NULL;
+}
+
+/** Try to find a match for AddressMap expressions that use
+ *  wildcard notation such as '*.c.d *.e.f' (so 'a.c.d' will map to 'a.e.f') or
+ *  '*.c.d a.b.c' (so 'a.c.d' will map to a.b.c).
+ *  Return the matching entry in AddressMap or NULL if no match is found.
+ *  For expressions such as '*.c.d *.e.f', truncate <b>address</b> 'a.c.d'
+ *  to 'a' before we return the matching AddressMap entry.
+ *
+ * This function does not handle the case where a pattern of the form "*.c.d"
+ * matches the address c.d -- that's done by the main addressmap_rewrite
+ * function.
+ */
+static addressmap_entry_t *
+addressmap_match_superdomains(char *address)
+{
+  addressmap_entry_t *val;
+  char *cp;
+
+  cp = address;
+  while ((cp = strchr(cp, '.'))) {
+    /* cp now points to a suffix of address that begins with a . */
+    val = strmap_get_lc(addressmap, cp+1);
+    if (val && val->src_wildcard) {
+      if (val->dst_wildcard)
+        *cp = '\0';
+      return val;
+    }
+    ++cp;
+  }
+  return NULL;
+}
+
+/** Look at address, and rewrite it until it doesn't want any
+ * more rewrites; but don't get into an infinite loop.
+ * Don't write more than maxlen chars into address.  Return true if the
+ * address changed; false otherwise.  Set *<b>expires_out</b> to the
+ * expiry time of the result, or to <b>time_max</b> if the result does
+ * not expire.
+ *
+ * If <b>exit_source_out</b> is non-null, we set it as follows.  If we the
+ * address starts out as a non-exit address, and we remap it to an .exit
+ * address at any point, then set *<b>exit_source_out</b> to the
+ * address_entry_source_t of the first such rule.  Set *<b>exit_source_out</b>
+ * to ADDRMAPSRC_NONE if there is no such rewrite, or if the original address
+ * was a .exit.
+ */
+int
+addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out,
+                   addressmap_entry_source_t *exit_source_out)
+{
+  addressmap_entry_t *ent;
+  int rewrites;
+  time_t expires = TIME_MAX;
+  addressmap_entry_source_t exit_source = ADDRMAPSRC_NONE;
+  char *addr_orig = tor_strdup(address);
+  char *log_addr_orig = NULL;
+
+  for (rewrites = 0; rewrites < 16; rewrites++) {
+    int exact_match = 0;
+    log_addr_orig = tor_strdup(escaped_safe_str_client(address));
+
+    ent = strmap_get(addressmap, address);
+
+    if (!ent || !ent->new_address) {
+      ent = addressmap_match_superdomains(address);
+    } else {
+      if (ent->src_wildcard && !ent->dst_wildcard &&
+          !strcasecmp(address, ent->new_address)) {
+        /* This is a rule like *.example.com example.com, and we just got
+         * "example.com" */
+        goto done;
+      }
+
+      exact_match = 1;
+    }
+
+    if (!ent || !ent->new_address) {
+      goto done;
+    }
+
+    if (ent->dst_wildcard && !exact_match) {
+      strlcat(address, ".", maxlen);
+      strlcat(address, ent->new_address, maxlen);
+    } else {
+      strlcpy(address, ent->new_address, maxlen);
+    }
+
+    if (!strcmpend(address, ".exit") &&
+        strcmpend(addr_orig, ".exit") &&
+        exit_source == ADDRMAPSRC_NONE) {
+      exit_source = ent->source;
+    }
+
+    log_info(LD_APP, "Addressmap: rewriting %s to %s",
+             log_addr_orig, escaped_safe_str_client(address));
+    if (ent->expires > 1 && ent->expires < expires)
+      expires = ent->expires;
+
+    tor_free(log_addr_orig);
+  }
+  log_warn(LD_CONFIG,
+           "Loop detected: we've rewritten %s 16 times! Using it as-is.",
+           escaped_safe_str_client(address));
+  /* it's fine to rewrite a rewrite, but don't loop forever */
+
+ done:
+  tor_free(addr_orig);
+  tor_free(log_addr_orig);
+  if (exit_source_out)
+    *exit_source_out = exit_source;
+  if (expires_out)
+    *expires_out = TIME_MAX;
+  return (rewrites > 0);
+}
+
+/** If we have a cached reverse DNS entry for the address stored in the
+ * <b>maxlen</b>-byte buffer <b>address</b> (typically, a dotted quad) then
+ * rewrite to the cached value and return 1.  Otherwise return 0.  Set
+ * *<b>expires_out</b> to the expiry time of the result, or to <b>time_max</b>
+ * if the result does not expire. */
+int
+addressmap_rewrite_reverse(char *address, size_t maxlen, time_t *expires_out)
+{
+  char *s, *cp;
+  addressmap_entry_t *ent;
+  int r = 0;
+  tor_asprintf(&s, "REVERSE[%s]", address);
+  ent = strmap_get(addressmap, s);
+  if (ent) {
+    cp = tor_strdup(escaped_safe_str_client(ent->new_address));
+    log_info(LD_APP, "Rewrote reverse lookup %s -> %s",
+             escaped_safe_str_client(s), cp);
+    tor_free(cp);
+    strlcpy(address, ent->new_address, maxlen);
+    r = 1;
+  }
+
+  if (expires_out)
+    *expires_out = (ent && ent->expires > 1) ? ent->expires : TIME_MAX;
+
+  tor_free(s);
+  return r;
+}
+
+/** Return 1 if <b>address</b> is already registered, else return 0. If address
+ * is already registered, and <b>update_expires</b> is non-zero, then update
+ * the expiry time on the mapping with update_expires if it is a
+ * mapping created by TrackHostExits. */
+int
+addressmap_have_mapping(const char *address, int update_expiry)
+{
+  addressmap_entry_t *ent;
+  if (!(ent=strmap_get_lc(addressmap, address)))
+    return 0;
+  if (update_expiry && ent->source==ADDRMAPSRC_TRACKEXIT)
+    ent->expires=time(NULL) + update_expiry;
+  return 1;
+}
+
+/** Register a request to map <b>address</b> to <b>new_address</b>,
+ * which will expire on <b>expires</b> (or 0 if never expires from
+ * config file, 1 if never expires from controller, 2 if never expires
+ * (virtual address mapping) from the controller.)
+ *
+ * <b>new_address</b> should be a newly dup'ed string, which we'll use or
+ * free as appropriate. We will leave address alone.
+ *
+ * If <b>wildcard_addr</b> is true, then the mapping will match any address
+ * equal to <b>address</b>, or any address ending with a period followed by
+ * <b>address</b>.  If <b>wildcard_addr</b> and <b>wildcard_new_addr</b> are
+ * both true, the mapping will rewrite addresses that end with
+ * ".<b>address</b>" into ones that end with ".<b>new_address</b>."
+ *
+ * If <b>new_address</b> is NULL, or <b>new_address</b> is equal to
+ * <b>address</b> and <b>wildcard_addr</b> is equal to
+ * <b>wildcard_new_addr</b>, remove any mappings that exist from
+ * <b>address</b>.
+ *
+ *
+ * It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is
+ * not set. */
+void
+addressmap_register(const char *address, char *new_address, time_t expires,
+                    addressmap_entry_source_t source,
+                    const int wildcard_addr,
+                    const int wildcard_new_addr)
+{
+  addressmap_entry_t *ent;
+
+  if (wildcard_new_addr)
+    tor_assert(wildcard_addr);
+
+  ent = strmap_get(addressmap, address);
+  if (!new_address || (!strcasecmp(address,new_address) &&
+                       wildcard_addr == wildcard_new_addr)) {
+    /* Remove the mapping, if any. */
+    tor_free(new_address);
+    if (ent) {
+      addressmap_ent_remove(address,ent);
+      strmap_remove(addressmap, address);
+    }
+    return;
+  }
+  if (!ent) { /* make a new one and register it */
+    ent = tor_malloc_zero(sizeof(addressmap_entry_t));
+    strmap_set(addressmap, address, ent);
+  } else if (ent->new_address) { /* we need to clean up the old mapping. */
+    if (expires > 1) {
+      log_info(LD_APP,"Temporary addressmap ('%s' to '%s') not performed, "
+               "since it's already mapped to '%s'",
+      safe_str_client(address),
+      safe_str_client(new_address),
+      safe_str_client(ent->new_address));
+      tor_free(new_address);
+      return;
+    }
+    if (address_is_in_virtual_range(ent->new_address) &&
+        expires != 2) {
+      /* XXX This isn't the perfect test; we want to avoid removing
+       * mappings set from the control interface _as virtual mapping */
+      addressmap_virtaddress_remove(address, ent);
+    }
+    tor_free(ent->new_address);
+  } /* else { we have an in-progress resolve with no mapping. } */
+
+  ent->new_address = new_address;
+  ent->expires = expires==2 ? 1 : expires;
+  ent->num_resolve_failures = 0;
+  ent->source = source;
+  ent->src_wildcard = wildcard_addr ? 1 : 0;
+  ent->dst_wildcard = wildcard_new_addr ? 1 : 0;
+
+  log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'",
+           safe_str_client(address),
+           safe_str_client(ent->new_address));
+  control_event_address_mapped(address, ent->new_address, expires, NULL);
+}
+
+/** An attempt to resolve <b>address</b> failed at some OR.
+ * Increment the number of resolve failures we have on record
+ * for it, and then return that number.
+ */
+int
+client_dns_incr_failures(const char *address)
+{
+  addressmap_entry_t *ent = strmap_get(addressmap, address);
+  if (!ent) {
+    ent = tor_malloc_zero(sizeof(addressmap_entry_t));
+    ent->expires = time(NULL) + MAX_DNS_ENTRY_AGE;
+    strmap_set(addressmap,address,ent);
+  }
+  if (ent->num_resolve_failures < SHORT_MAX)
+    ++ent->num_resolve_failures; /* don't overflow */
+  log_info(LD_APP, "Address %s now has %d resolve failures.",
+           safe_str_client(address),
+           ent->num_resolve_failures);
+  return ent->num_resolve_failures;
+}
+
+/** If <b>address</b> is in the client DNS addressmap, reset
+ * the number of resolve failures we have on record for it.
+ * This is used when we fail a stream because it won't resolve:
+ * otherwise future attempts on that address will only try once.
+ */
+void
+client_dns_clear_failures(const char *address)
+{
+  addressmap_entry_t *ent = strmap_get(addressmap, address);
+  if (ent)
+    ent->num_resolve_failures = 0;
+}
+
+/** Record the fact that <b>address</b> resolved to <b>name</b>.
+ * We can now use this in subsequent streams via addressmap_rewrite()
+ * so we can more correctly choose an exit that will allow <b>address</b>.
+ *
+ * If <b>exitname</b> is defined, then append the addresses with
+ * ".exitname.exit" before registering the mapping.
+ *
+ * If <b>ttl</b> is nonnegative, the mapping will be valid for
+ * <b>ttl</b>seconds; otherwise, we use the default.
+ */
+static void
+client_dns_set_addressmap_impl(origin_circuit_t *on_circ,
+                               const char *address, const char *name,
+                               const char *exitname,
+                               int ttl)
+{
+  char *extendedaddress=NULL, *extendedval=NULL;
+  (void)on_circ;
+
+  tor_assert(address);
+  tor_assert(name);
+
+  if (ttl<0)
+    ttl = DEFAULT_DNS_TTL;
+  else
+    ttl = dns_clip_ttl(ttl);
+
+  if (exitname) {
+    /* XXXX fails to ever get attempts to get an exit address of
+     * google.com.digest[=~]nickname.exit; we need a syntax for this that
+     * won't make strict RFC952-compliant applications (like us) barf. */
+    tor_asprintf(&extendedaddress,
+                 "%s.%s.exit", address, exitname);
+    tor_asprintf(&extendedval,
+                 "%s.%s.exit", name, exitname);
+  } else {
+    tor_asprintf(&extendedaddress,
+                 "%s", address);
+    tor_asprintf(&extendedval,
+                 "%s", name);
+  }
+  addressmap_register(extendedaddress, extendedval,
+                      time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0);
+  tor_free(extendedaddress);
+}
+
+/** Record the fact that <b>address</b> resolved to <b>val</b>.
+ * We can now use this in subsequent streams via addressmap_rewrite()
+ * so we can more correctly choose an exit that will allow <b>address</b>.
+ *
+ * If <b>exitname</b> is defined, then append the addresses with
+ * ".exitname.exit" before registering the mapping.
+ *
+ * If <b>ttl</b> is nonnegative, the mapping will be valid for
+ * <b>ttl</b>seconds; otherwise, we use the default.
+ */
+void
+client_dns_set_addressmap(origin_circuit_t *on_circ,
+                          const char *address,
+                          const tor_addr_t *val,
+                          const char *exitname,
+                          int ttl)
+{
+  tor_addr_t addr_tmp;
+  char valbuf[TOR_ADDR_BUF_LEN];
+
+  tor_assert(address);
+  tor_assert(val);
+
+  if (tor_addr_parse(&addr_tmp, address) == 0)
+    return; /* If address was an IP address already, don't add a mapping. */
+
+  /* XXXXX For now, don't cache IPv6 addresses. */
+  if (tor_addr_family(val) != AF_INET)
+    return;
+
+  if (! tor_addr_to_str(valbuf, val, sizeof(valbuf), 1))
+    return;
+
+  client_dns_set_addressmap_impl(on_circ, address, valbuf, exitname, ttl);
+}
+
+/** Add a cache entry noting that <b>address</b> (ordinarily a dotted quad)
+ * resolved via a RESOLVE_PTR request to the hostname <b>v</b>.
+ *
+ * If <b>exitname</b> is defined, then append the addresses with
+ * ".exitname.exit" before registering the mapping.
+ *
+ * If <b>ttl</b> is nonnegative, the mapping will be valid for
+ * <b>ttl</b>seconds; otherwise, we use the default.
+ */
+void
+client_dns_set_reverse_addressmap(origin_circuit_t *on_circ,
+                                  const char *address, const char *v,
+                                  const char *exitname,
+                                  int ttl)
+{
+  char *s = NULL;
+  tor_asprintf(&s, "REVERSE[%s]", address);
+  client_dns_set_addressmap_impl(on_circ, s, v, exitname, ttl);
+  tor_free(s);
+}
+
+/* By default, we hand out 127.192.0.1 through 127.254.254.254.
+ * These addresses should map to localhost, so even if the
+ * application accidentally tried to connect to them directly (not
+ * via Tor), it wouldn't get too far astray.
+ *
+ * These options are configured by parse_virtual_addr_network().
+ */
+/** Which network should we use for virtual IPv4 addresses?  Only the first
+ * bits of this value are fixed. */
+static uint32_t virtual_addr_network = 0x7fc00000u;
+/** How many bits of <b>virtual_addr_network</b> are fixed? */
+static maskbits_t virtual_addr_netmask_bits = 10;
+/** What's the next virtual address we will hand out? */
+static uint32_t next_virtual_addr    = 0x7fc00000u;
+
+/** Read a netmask of the form 127.192.0.0/10 from "val", and check whether
+ * it's a valid set of virtual addresses to hand out in response to MAPADDRESS
+ * requests.  Return 0 on success; set *msg (if provided) to a newly allocated
+ * string and return -1 on failure.  If validate_only is false, sets the
+ * actual virtual address range to the parsed value. */
+int
+parse_virtual_addr_network(const char *val, int validate_only,
+                           char **msg)
+{
+  uint32_t addr;
+  uint16_t port_min, port_max;
+  maskbits_t bits;
+
+  if (parse_addr_and_port_range(val, &addr, &bits, &port_min, &port_max)) {
+    if (msg) *msg = tor_strdup("Error parsing VirtualAddressNetwork");
+    return -1;
+  }
+
+  if (port_min != 1 || port_max != 65535) {
+    if (msg) *msg = tor_strdup("Can't specify ports on VirtualAddressNetwork");
+    return -1;
+  }
+
+  if (bits > 16) {
+    if (msg) *msg = tor_strdup("VirtualAddressNetwork expects a /16 "
+                               "network or larger");
+    return -1;
+  }
+
+  if (validate_only)
+    return 0;
+
+  virtual_addr_network = (uint32_t)( addr & (0xfffffffful << (32-bits)) );
+  virtual_addr_netmask_bits = bits;
+
+  if (addr_mask_cmp_bits(next_virtual_addr, addr, bits))
+    next_virtual_addr = addr;
+
+  return 0;
+}
+
+/**
+ * Return true iff <b>addr</b> is likely to have been returned by
+ * client_dns_get_unused_address.
+ **/
+int
+address_is_in_virtual_range(const char *address)
+{
+  struct in_addr in;
+  tor_assert(address);
+  if (!strcasecmpend(address, ".virtual")) {
+    return 1;
+  } else if (tor_inet_aton(address, &in)) {
+    uint32_t addr = ntohl(in.s_addr);
+    if (!addr_mask_cmp_bits(addr, virtual_addr_network,
+                            virtual_addr_netmask_bits))
+      return 1;
+  }
+  return 0;
+}
+
+/** Increment the value of next_virtual_addr; reset it to the start of the
+ * virtual address range if it wraps around.
+ */
+static INLINE void
+increment_virtual_addr(void)
+{
+  ++next_virtual_addr;
+  if (addr_mask_cmp_bits(next_virtual_addr, virtual_addr_network,
+                         virtual_addr_netmask_bits))
+    next_virtual_addr = virtual_addr_network;
+}
+
+/** Return a newly allocated string holding an address of <b>type</b>
+ * (one of RESOLVED_TYPE_{IPV4|HOSTNAME}) that has not yet been mapped,
+ * and that is very unlikely to be the address of any real host.
+ *
+ * May return NULL if we have run out of virtual addresses.
+ */
+static char *
+addressmap_get_virtual_address(int type)
+{
+  char buf[64];
+  tor_assert(addressmap);
+
+  if (type == RESOLVED_TYPE_HOSTNAME) {
+    char rand[10];
+    do {
+      crypto_rand(rand, sizeof(rand));
+      base32_encode(buf,sizeof(buf),rand,sizeof(rand));
+      strlcat(buf, ".virtual", sizeof(buf));
+    } while (strmap_get(addressmap, buf));
+    return tor_strdup(buf);
+  } else if (type == RESOLVED_TYPE_IPV4) {
+    // This is an imperfect estimate of how many addresses are available, but
+    // that's ok.
+    struct in_addr in;
+    uint32_t available = 1u << (32-virtual_addr_netmask_bits);
+    while (available) {
+      /* Don't hand out any .0 or .255 address. */
+      while ((next_virtual_addr & 0xff) == 0 ||
+             (next_virtual_addr & 0xff) == 0xff) {
+        increment_virtual_addr();
+        if (! --available) {
+          log_warn(LD_CONFIG, "Ran out of virtual addresses!");
+          return NULL;
+        }
+      }
+      in.s_addr = htonl(next_virtual_addr);
+      tor_inet_ntoa(&in, buf, sizeof(buf));
+      if (!strmap_get(addressmap, buf)) {
+        increment_virtual_addr();
+        break;
+      }
+
+      increment_virtual_addr();
+      --available;
+      // log_info(LD_CONFIG, "%d addrs available", (int)available);
+      if (! available) {
+        log_warn(LD_CONFIG, "Ran out of virtual addresses!");
+        return NULL;
+      }
+    }
+    return tor_strdup(buf);
+  } else {
+    log_warn(LD_BUG, "Called with unsupported address type (%d)", type);
+    return NULL;
+  }
+}
+
+/** A controller has requested that we map some address of type
+ * <b>type</b> to the address <b>new_address</b>.  Choose an address
+ * that is unlikely to be used, and map it, and return it in a newly
+ * allocated string.  If another address of the same type is already
+ * mapped to <b>new_address</b>, try to return a copy of that address.
+ *
+ * The string in <b>new_address</b> may be freed or inserted into a map
+ * as appropriate.  May return NULL if are out of virtual addresses.
+ **/
+const char *
+addressmap_register_virtual_address(int type, char *new_address)
+{
+  char **addrp;
+  virtaddress_entry_t *vent;
+  int vent_needs_to_be_added = 0;
+
+  tor_assert(new_address);
+  tor_assert(addressmap);
+  tor_assert(virtaddress_reversemap);
+
+  vent = strmap_get(virtaddress_reversemap, new_address);
+  if (!vent) {
+    vent = tor_malloc_zero(sizeof(virtaddress_entry_t));
+    vent_needs_to_be_added = 1;
+  }
+
+  addrp = (type == RESOLVED_TYPE_IPV4) ?
+    &vent->ipv4_address : &vent->hostname_address;
+  if (*addrp) {
+    addressmap_entry_t *ent = strmap_get(addressmap, *addrp);
+    if (ent && ent->new_address &&
+        !strcasecmp(new_address, ent->new_address)) {
+      tor_free(new_address);
+      tor_assert(!vent_needs_to_be_added);
+      return tor_strdup(*addrp);
+    } else
+      log_warn(LD_BUG,
+               "Internal confusion: I thought that '%s' was mapped to by "
+               "'%s', but '%s' really maps to '%s'. This is a harmless bug.",
+               safe_str_client(new_address),
+               safe_str_client(*addrp),
+               safe_str_client(*addrp),
+               ent?safe_str_client(ent->new_address):"(nothing)");
+  }
+
+  tor_free(*addrp);
+  *addrp = addressmap_get_virtual_address(type);
+  if (!*addrp) {
+    tor_free(vent);
+    tor_free(new_address);
+    return NULL;
+  }
+  log_info(LD_APP, "Registering map from %s to %s", *addrp, new_address);
+  if (vent_needs_to_be_added)
+    strmap_set(virtaddress_reversemap, new_address, vent);
+  addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP, 0, 0);
+
+#if 0
+  {
+    /* Try to catch possible bugs */
+    addressmap_entry_t *ent;
+    ent = strmap_get(addressmap, *addrp);
+    tor_assert(ent);
+    tor_assert(!strcasecmp(ent->new_address,new_address));
+    vent = strmap_get(virtaddress_reversemap, new_address);
+    tor_assert(vent);
+    tor_assert(!strcasecmp(*addrp,
+                           (type == RESOLVED_TYPE_IPV4) ?
+                           vent->ipv4_address : vent->hostname_address));
+    log_info(LD_APP, "Map from %s to %s okay.",
+             safe_str_client(*addrp),
+             safe_str_client(new_address));
+  }
+#endif
+
+  return *addrp;
+}
+
+/** Return 1 if <b>address</b> has funny characters in it like colons. Return
+ * 0 if it's fine, or if we're configured to allow it anyway.  <b>client</b>
+ * should be true if we're using this address as a client; false if we're
+ * using it as a server.
+ */
+int
+address_is_invalid_destination(const char *address, int client)
+{
+  if (client) {
+    if (get_options()->AllowNonRFC953Hostnames)
+      return 0;
+  } else {
+    if (get_options()->ServerDNSAllowNonRFC953Hostnames)
+      return 0;
+  }
+
+  /* It might be an IPv6 address! */
+  {
+    tor_addr_t a;
+    if (tor_addr_parse(&a, address) >= 0)
+      return 0;
+  }
+
+  while (*address) {
+    if (TOR_ISALNUM(*address) ||
+        *address == '-' ||
+        *address == '.' ||
+        *address == '_') /* Underscore is not allowed, but Windows does it
+                          * sometimes, just to thumb its nose at the IETF. */
+      ++address;
+    else
+      return 1;
+  }
+  return 0;
+}
+
+/** Iterate over all address mappings which have expiry times between
+ * min_expires and max_expires, inclusive.  If sl is provided, add an
+ * "old-addr new-addr expiry" string to sl for each mapping, omitting
+ * the expiry time if want_expiry is false. If sl is NULL, remove the
+ * mappings.
+ */
+void
+addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
+                        time_t max_expires, int want_expiry)
+{
+   strmap_iter_t *iter;
+   const char *key;
+   void *val_;
+   addressmap_entry_t *val;
+
+   if (!addressmap)
+     addressmap_init();
+
+   for (iter = strmap_iter_init(addressmap); !strmap_iter_done(iter); ) {
+     strmap_iter_get(iter, &key, &val_);
+     val = val_;
+     if (val->expires >= min_expires && val->expires <= max_expires) {
+       if (!sl) {
+         iter = strmap_iter_next_rmv(addressmap,iter);
+         addressmap_ent_remove(key, val);
+         continue;
+       } else if (val->new_address) {
+         const char *src_wc = val->src_wildcard ? "*." : "";
+         const char *dst_wc = val->dst_wildcard ? "*." : "";
+         if (want_expiry) {
+           if (val->expires < 3 || val->expires == TIME_MAX)
+             smartlist_add_asprintf(sl, "%s%s %s%s NEVER",
+                                    src_wc, key, dst_wc, val->new_address);
+           else {
+             char time[ISO_TIME_LEN+1];
+             format_iso_time(time, val->expires);
+             smartlist_add_asprintf(sl, "%s%s %s%s \"%s\"",
+                                    src_wc, key, dst_wc, val->new_address,
+                                    time);
+           }
+         } else {
+           smartlist_add_asprintf(sl, "%s%s %s%s",
+                                  src_wc, key, dst_wc, val->new_address);
+         }
+       }
+     }
+     iter = strmap_iter_next(addressmap,iter);
+   }
+}
+

+ 44 - 0
src/or/addressmap.h

@@ -0,0 +1,44 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_ADDRESSMAP_H
+#define TOR_ADDRESSMAP_H
+
+void addressmap_init(void);
+void addressmap_clear_excluded_trackexithosts(const or_options_t *options);
+void addressmap_clear_invalid_automaps(const or_options_t *options);
+void addressmap_clean(time_t now);
+void addressmap_clear_configured(void);
+void addressmap_clear_transient(void);
+void addressmap_free_all(void);
+int addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out,
+                       addressmap_entry_source_t *exit_source_out);
+int addressmap_rewrite_reverse(char *address, size_t maxlen,
+                               time_t *expires_out);
+int addressmap_have_mapping(const char *address, int update_timeout);
+
+void addressmap_register(const char *address, char *new_address,
+                         time_t expires, addressmap_entry_source_t source,
+                         const int address_wildcard,
+                         const int new_address_wildcard);
+int parse_virtual_addr_network(const char *val, int validate_only,
+                               char **msg);
+int client_dns_incr_failures(const char *address);
+void client_dns_clear_failures(const char *address);
+void client_dns_set_addressmap(origin_circuit_t *on_circ,
+                               const char *address, const tor_addr_t *val,
+                               const char *exitname, int ttl);
+const char *addressmap_register_virtual_address(int type, char *new_address);
+void addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
+                             time_t max_expires, int want_expiry);
+int address_is_in_virtual_range(const char *addr);
+void clear_trackexithost_mappings(const char *exitname);
+void client_dns_set_reverse_addressmap(origin_circuit_t *on_circ,
+                                       const char *address, const char *v,
+                                       const char *exitname, int ttl);
+
+#endif
+

+ 1 - 0
src/or/buffers.c

@@ -12,6 +12,7 @@
  **/
 #define BUFFERS_PRIVATE
 #include "or.h"
+#include "addressmap.h"
 #include "buffers.h"
 #include "config.h"
 #include "connection_edge.h"

+ 1 - 0
src/or/circuituse.c

@@ -10,6 +10,7 @@
  **/
 
 #include "or.h"
+#include "addressmap.h"
 #include "channel.h"
 #include "circuitbuild.h"
 #include "circuitlist.h"

+ 41 - 13
src/or/config.c

@@ -12,6 +12,7 @@
 #define CONFIG_PRIVATE
 
 #include "or.h"
+#include "addressmap.h"
 #include "channel.h"
 #include "circuitbuild.h"
 #include "circuitlist.h"
@@ -275,6 +276,7 @@ static config_var_t option_vars_[] = {
   V(HTTPProxyAuthenticator,      STRING,   NULL),
   V(HTTPSProxy,                  STRING,   NULL),
   V(HTTPSProxyAuthenticator,     STRING,   NULL),
+  V(IPv6Exit,                    BOOL,     "0"),
   VAR("ServerTransportPlugin",   LINELIST, ServerTransportPlugin,  NULL),
   V(Socks4Proxy,                 STRING,   NULL),
   V(Socks5Proxy,                 STRING,   NULL),
@@ -3168,6 +3170,7 @@ options_transition_affects_descriptor(const or_options_t *old_options,
       !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) ||
       old_options->ExitPolicyRejectPrivate !=
         new_options->ExitPolicyRejectPrivate ||
+      old_options->IPv6Exit != new_options->IPv6Exit ||
       !config_lines_eq(old_options->ORPort_lines,
                        new_options->ORPort_lines) ||
       !config_lines_eq(old_options->DirPort_lines,
@@ -4438,6 +4441,7 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid)
 #define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
 #define CL_PORT_SERVER_OPTIONS (1u<<3)
 #define CL_PORT_FORBID_NONLOCAL (1u<<4)
+#define CL_PORT_TAKES_HOSTNAMES (1u<<5)
 
 /**
  * Parse port configuration for a single port type.
@@ -4470,6 +4474,9 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid)
  * isolation options in the FooPort entries; instead allow the
  * server-port option set.
  *
+ * If CL_PORT_TAKES_HOSTNAMES is set in <b>flags</b>, allow the options
+ * {No,}IPv{4,6}Traffic.
+ *
  * 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
  * on success, -1 on failure.
@@ -4493,6 +4500,7 @@ parse_port_config(smartlist_t *out,
   const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL;
   const unsigned allow_spurious_listenaddr =
     flags & CL_PORT_ALLOW_EXTRA_LISTENADDR;
+  const unsigned takes_hostnames = flags & CL_PORT_TAKES_HOSTNAMES;
   int got_zero_port=0, got_nonzero_port=0;
 
   /* FooListenAddress is deprecated; let's make it work like it used to work,
@@ -4534,7 +4542,7 @@ parse_port_config(smartlist_t *out,
       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;
+      cfg->bind_ipv4_only = 1;
       smartlist_add(out, cfg);
     }
 
@@ -4596,7 +4604,8 @@ parse_port_config(smartlist_t *out,
     uint16_t ptmp=0;
     int ok;
     int no_listen = 0, no_advertise = 0, all_addrs = 0,
-      ipv4_only = 0, ipv6_only = 0;
+      bind_ipv4_only = 0, bind_ipv6_only = 0,
+      ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0;
 
     smartlist_split_string(elts, ports->value, NULL,
                            SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
@@ -4661,9 +4670,9 @@ parse_port_config(smartlist_t *out,
           all_addrs = 1;
 #endif
         } else if (!strcasecmp(elt, "IPv4Only")) {
-          ipv4_only = 1;
+          bind_ipv4_only = 1;
         } else if (!strcasecmp(elt, "IPv6Only")) {
-          ipv6_only = 1;
+          bind_ipv6_only = 1;
         } else {
           log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
                    portname, escaped(elt));
@@ -4676,18 +4685,18 @@ parse_port_config(smartlist_t *out,
                  portname, escaped(ports->value));
         goto err;
       }
-      if (ipv4_only && ipv6_only) {
+      if (bind_ipv4_only && bind_ipv6_only) {
         log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only "
                  "on %sPort line '%s'",
                  portname, escaped(ports->value));
         goto err;
       }
-      if (ipv4_only && tor_addr_family(&addr) == AF_INET6) {
+      if (bind_ipv4_only && tor_addr_family(&addr) == AF_INET6) {
         log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6",
                  portname);
         goto err;
       }
-      if (ipv6_only && tor_addr_family(&addr) == AF_INET) {
+      if (bind_ipv6_only && tor_addr_family(&addr) == AF_INET) {
         log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4",
                  portname);
         goto err;
@@ -4720,6 +4729,20 @@ parse_port_config(smartlist_t *out,
           no = 1;
           elt += 2;
         }
+
+        if (takes_hostnames) {
+          if (!strcasecmp(elt, "IPv4Traffic")) {
+            ipv4_traffic = ! no;
+            continue;
+          } else if (!strcasecmp(elt, "IPv6Traffic")) {
+            ipv6_traffic = ! no;
+            continue;
+          } else if (!strcasecmp(elt, "PreferIPv6")) {
+            prefer_ipv6 = ! no;
+            continue;
+          }
+        }
+
         if (!strcasecmpend(elt, "s"))
           elt[strlen(elt)-1] = '\0'; /* kill plurals. */
 
@@ -4761,8 +4784,11 @@ parse_port_config(smartlist_t *out,
       cfg->no_advertise = no_advertise;
       cfg->no_listen = no_listen;
       cfg->all_addrs = all_addrs;
-      cfg->ipv4_only = ipv4_only;
-      cfg->ipv6_only = ipv6_only;
+      cfg->bind_ipv4_only = bind_ipv4_only;
+      cfg->bind_ipv6_only = bind_ipv6_only;
+      cfg->ipv4_traffic = ipv4_traffic;
+      cfg->ipv6_traffic = ipv6_traffic;
+      cfg->prefer_ipv6 = prefer_ipv6;
 
       smartlist_add(out, cfg);
     }
@@ -4855,7 +4881,8 @@ parse_ports(or_options_t *options, int validate_only,
              options->SocksPort_lines, options->SocksListenAddress,
              "Socks", CONN_TYPE_AP_LISTENER,
              "127.0.0.1", 9050,
-             CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR) < 0) {
+             CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR|
+             CL_PORT_TAKES_HOSTNAMES) < 0) {
     *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration");
     goto err;
   }
@@ -4995,7 +5022,8 @@ check_server_ports(const smartlist_t *ports,
       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))
+            (tor_addr_family(&port->addr) == AF_UNSPEC &&
+                !port->bind_ipv6_only))
           ++n_orport_advertised_ipv4;
       }
       if (! port->no_listen)
@@ -5125,8 +5153,8 @@ get_first_advertised_port_by_type_af(int listener_type, int address_family)
         (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)) {
+          (address_family == AF_INET && !cfg->bind_ipv6_only) ||
+          (address_family == AF_INET6 && !cfg->bind_ipv4_only)) {
         return cfg->port;
       }
     }

+ 11 - 0
src/or/connection.c

@@ -1115,6 +1115,14 @@ connection_listener_new(const struct sockaddr *listensockaddr,
       lis_conn->session_group = global_next_session_group--;
     }
   }
+  if (type == CONN_TYPE_AP_LISTENER) {
+    lis_conn->socks_ipv4_traffic = port_cfg->ipv4_traffic;
+    lis_conn->socks_ipv6_traffic = port_cfg->ipv6_traffic;
+    lis_conn->socks_prefer_ipv6 = port_cfg->prefer_ipv6;
+  } else {
+    lis_conn->socks_ipv4_traffic = 1;
+    lis_conn->socks_ipv6_traffic = 1;
+  }
 
   if (connection_add(conn) < 0) { /* no space, forget it */
     log_warn(LD_NET,"connection_add for listener failed. Giving up.");
@@ -1348,6 +1356,9 @@ connection_init_accepted_conn(connection_t *conn,
       TO_ENTRY_CONN(conn)->session_group = listener->session_group;
       TO_ENTRY_CONN(conn)->nym_epoch = get_signewnym_epoch();
       TO_ENTRY_CONN(conn)->socks_request->listener_type = listener->base_.type;
+      TO_ENTRY_CONN(conn)->ipv4_traffic_ok = listener->socks_ipv4_traffic;
+      TO_ENTRY_CONN(conn)->ipv6_traffic_ok = listener->socks_ipv6_traffic;
+      TO_ENTRY_CONN(conn)->prefer_ipv6_traffic = listener->socks_prefer_ipv6;
       switch (TO_CONN(listener)->type) {
         case CONN_TYPE_AP_LISTENER:
           conn->state = AP_CONN_STATE_SOCKS_WAIT;

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 53 - 959
src/or/connection_edge.c


+ 45 - 24
src/or/connection_edge.h

@@ -67,30 +67,6 @@ int connection_ap_process_transparent(entry_connection_t *conn);
 
 int address_is_invalid_destination(const char *address, int client);
 
-void addressmap_init(void);
-void addressmap_clear_excluded_trackexithosts(const or_options_t *options);
-void addressmap_clear_invalid_automaps(const or_options_t *options);
-void addressmap_clean(time_t now);
-void addressmap_clear_configured(void);
-void addressmap_clear_transient(void);
-void addressmap_free_all(void);
-int addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out,
-                       addressmap_entry_source_t *exit_source_out);
-int addressmap_have_mapping(const char *address, int update_timeout);
-
-void addressmap_register(const char *address, char *new_address,
-                         time_t expires, addressmap_entry_source_t source,
-                         const int address_wildcard,
-                         const int new_address_wildcard);
-int parse_virtual_addr_network(const char *val, int validate_only,
-                               char **msg);
-int client_dns_incr_failures(const char *address);
-void client_dns_clear_failures(const char *address);
-void client_dns_set_addressmap(const char *address, uint32_t val,
-                               const char *exitname, int ttl);
-const char *addressmap_register_virtual_address(int type, char *new_address);
-void addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
-                             time_t max_expires, int want_expiry);
 int connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
                                                 origin_circuit_t *circ,
                                                 crypt_path_t *cpath);
@@ -115,5 +91,50 @@ int connection_edge_update_circuit_isolation(const entry_connection_t *conn,
                                              int dry_run);
 void circuit_clear_isolation(origin_circuit_t *circ);
 
+/** @name Begin-cell flags
+ *
+ * These flags are used in RELAY_BEGIN cells to change the default behavior
+ * of the cell.
+ *
+ * @{
+ **/
+/** When this flag is set, the client is willing to get connected to IPv6
+ * addresses */
+#define BEGIN_FLAG_IPV6_OK        (1u<<0)
+/** When this flag is set, the client DOES NOT support connecting to IPv4
+ * addresses.  (The sense of this flag is inverted from IPV6_OK, so that the
+ * old default behavior of Tor is equivalent to having all flags set to 0.)
+ **/
+#define BEGIN_FLAG_IPV4_NOT_OK    (1u<<1)
+/** When this flag is set, if we find both an IPv4 and an IPv6 address,
+ * we use the IPv6 address.  Otherwise we use the IPv4 address. */
+#define BEGIN_FLAG_IPV6_PREFERRED (1u<<2)
+/**@}*/
+
+#ifdef CONNECTION_EDGE_PRIVATE
+
+/** A parsed BEGIN or BEGIN_DIR cell */
+typedef struct begin_cell_t {
+  /** The address the client has asked us to connect to, or NULL if this is
+   * a BEGIN_DIR cell*/
+  char *address;
+  /** The flags specified in the BEGIN cell's body.  One or more of
+   * BEGIN_FLAG_*. */
+  uint32_t flags;
+  /** The client's requested port. */
+  uint16_t port;
+  /** The client's requested Stream ID */
+  uint16_t stream_id;
+  /** True iff this is a BEGIN_DIR cell. */
+  unsigned is_begindir : 1;
+} begin_cell_t;
+
+int begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
+                     uint8_t *end_reason_out);
+int connected_cell_format_payload(uint8_t *payload_out,
+                                  const tor_addr_t *addr,
+                                  uint32_t ttl);
+#endif
+
 #endif
 

+ 1 - 0
src/or/control.c

@@ -11,6 +11,7 @@
 #define CONTROL_PRIVATE
 
 #include "or.h"
+#include "addressmap.h"
 #include "buffers.h"
 #include "channel.h"
 #include "channeltls.h"

+ 5 - 3
src/or/dirserv.c

@@ -73,8 +73,10 @@ static const struct consensus_method_range_t {
   int high;
 } microdesc_consensus_methods[] = {
   {MIN_METHOD_FOR_MICRODESC, MIN_METHOD_FOR_A_LINES - 1},
-  {MIN_METHOD_FOR_A_LINES, MAX_SUPPORTED_CONSENSUS_METHOD},
-  {-1, -1}};
+  {MIN_METHOD_FOR_A_LINES, MIN_METHOD_FOR_P6_LINES - 1},
+  {MIN_METHOD_FOR_P6_LINES, MAX_SUPPORTED_CONSENSUS_METHOD},
+  {-1, -1}
+};
 
 static void directory_remove_invalid(void);
 static cached_dir_t *dirserv_regenerate_directory(void);
@@ -2237,7 +2239,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
     }
 
     if (desc) {
-      summary = policy_summarize(desc->exit_policy);
+      summary = policy_summarize(desc->exit_policy, AF_INET);
       r = tor_snprintf(cp, buf_len - (cp-buf), "p %s\n", summary);
       if (r<0) {
         log_warn(LD_BUG, "Not enough space in buffer.");

+ 14 - 7
src/or/dirvote.c

@@ -3534,12 +3534,8 @@ dirvote_get_vote(const char *fp, int flags)
   return NULL;
 }
 
-/** Construct and return a new microdescriptor from a routerinfo <b>ri</b>.
- *
- * XXX Right now, there is only one way to generate microdescriptors from
- * router descriptors.  This may change in future consensus methods.  If so,
- * we'll need an internal way to remember which method we used, and ask for a
- * particular method.
+/** Construct and return a new microdescriptor from a routerinfo <b>ri</b>
+ * according to <b>consensus_method</b>.
  **/
 microdesc_t *
 dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
@@ -3552,7 +3548,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
 
   if (crypto_pk_write_public_key_to_string(ri->onion_pkey, &key, &keylen)<0)
     goto done;
-  summary = policy_summarize(ri->exit_policy);
+  summary = policy_summarize(ri->exit_policy, AF_INET);
   if (ri->declared_family)
     family = smartlist_join_strings(ri->declared_family, " ", 0, NULL);
 
@@ -3569,6 +3565,17 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
   if (summary && strcmp(summary, "reject 1-65535"))
     smartlist_add_asprintf(chunks, "p %s\n", summary);
 
+  if (consensus_method >= MIN_METHOD_FOR_P6_LINES &&
+      ri->ipv6_exit_policy) {
+    /* XXXX024 This doesn't match proposal 208, which says these should
+     * be taken unchanged from the routerinfo.  That's bogosity, IMO:
+     * the proposal should have said to do this instead.*/
+    char *p6 = write_short_policy(ri->ipv6_exit_policy);
+    if (p6 && strcmp(p6, "reject 1-65535"))
+      smartlist_add_asprintf(chunks, "p6 %s\n", p6);
+    tor_free(p6);
+  }
+
   output = smartlist_join_strings(chunks, "", 0, NULL);
 
   {

+ 4 - 1
src/or/dirvote.h

@@ -20,7 +20,7 @@
 #define MIN_VOTE_INTERVAL 300
 
 /** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 14
+#define MAX_SUPPORTED_CONSENSUS_METHOD 15
 
 /** Lowest consensus method that contains a 'directory-footer' marker */
 #define MIN_METHOD_FOR_FOOTER 9
@@ -45,6 +45,9 @@
 /** Lowest consensus method that contains "a" lines. */
 #define MIN_METHOD_FOR_A_LINES 14
 
+/** Lowest consensus method where microdescs may include a "p6" line. */
+#define MIN_METHOD_FOR_P6_LINES 15
+
 void dirvote_free_all(void);
 
 /* vote manipulation */

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 503 - 166
src/or/dns.c


+ 1 - 0
src/or/dns.h

@@ -24,6 +24,7 @@ void dns_cancel_pending_resolve(const char *question);
 int dns_resolve(edge_connection_t *exitconn);
 void dns_launch_correctness_checks(void);
 int dns_seems_to_be_broken(void);
+int dns_seems_to_be_broken_for_ipv6(void);
 void dns_reset_correctness_checks(void);
 void dump_dns_mem_usage(int severity);
 

+ 6 - 4
src/or/dnsserv.c

@@ -89,6 +89,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
       continue;
     switch (req->questions[i]->type) {
       case EVDNS_TYPE_A:
+      case EVDNS_TYPE_AAAA:
       case EVDNS_TYPE_PTR:
         q = req->questions[i];
       default:
@@ -101,7 +102,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
     evdns_server_request_respond(req, DNS_ERR_NOTIMPL);
     return;
   }
-  if (q->type != EVDNS_TYPE_A) {
+  if (q->type != EVDNS_TYPE_A && q->type != EVDNS_TYPE_AAAA) {
     tor_assert(q->type == EVDNS_TYPE_PTR);
   }
 
@@ -125,7 +126,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
   TO_CONN(conn)->port = port;
   TO_CONN(conn)->address = tor_dup_addr(&tor_addr);
 
-  if (q->type == EVDNS_TYPE_A)
+  if (q->type == EVDNS_TYPE_A || q->type == EVDNS_TYPE_AAAA)
     entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
   else
     entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
@@ -289,8 +290,9 @@ dnsserv_resolved(entry_connection_t *conn,
    * or more of the questions in the request); then, call
    * evdns_server_request_respond. */
   if (answer_type == RESOLVED_TYPE_IPV6) {
-    log_info(LD_APP, "Got an IPv6 answer; that's not implemented.");
-    err = DNS_ERR_NOTIMPL;
+    evdns_server_request_add_aaaa_reply(req,
+                                        name,
+                                        1, answer, ttl);
   } else if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4 &&
              conn->socks_request->command == SOCKS_COMMAND_RESOLVE) {
     evdns_server_request_add_a_reply(req,

+ 2 - 0
src/or/include.am

@@ -16,6 +16,7 @@ evdns_source=src/ext/eventdns.c
 endif
 
 src_or_libtor_a_SOURCES = \
+	src/or/addressmap.c				\
 	src/or/buffers.c				\
 	src/or/channel.c				\
 	src/or/channeltls.c				\
@@ -91,6 +92,7 @@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypto.a
 	@TOR_LIB_WS32@ @TOR_LIB_GDI@
 
 ORHEADERS = \
+	src/or/addressmap.h				\
 	src/or/buffers.h				\
 	src/or/channel.h				\
 	src/or/channeltls.h				\

+ 1 - 0
src/or/main.c

@@ -12,6 +12,7 @@
 
 #define MAIN_PRIVATE
 #include "or.h"
+#include "addressmap.h"
 #include "buffers.h"
 #include "channel.h"
 #include "channeltls.h"

+ 1 - 0
src/or/microdesc.c

@@ -583,6 +583,7 @@ microdesc_free(microdesc_t *md)
     smartlist_free(md->family);
   }
   short_policy_free(md->exit_policy);
+  short_policy_free(md->ipv6_exit_policy);
 
   tor_free(md);
 }

+ 49 - 5
src/or/or.h

@@ -1225,6 +1225,18 @@ typedef struct listener_connection_t {
   uint8_t isolation_flags;
   /**@}*/
 
+  /** For a SOCKS listener, these fields describe whether we should
+   * allow IPv4 and IPv6 addresses from our exit nodes, respectively.
+   *
+   * @{
+   */
+  unsigned int socks_ipv4_traffic : 1;
+  unsigned int socks_ipv6_traffic : 1;
+  /** @} */
+  /** For a socks listener: should we tell the exit that we prefer IPv6
+   * addresses? */
+  unsigned int socks_prefer_ipv6 : 1;
+
 } listener_connection_t;
 
 /** Minimum length of the random part of an AUTH_CHALLENGE cell. */
@@ -1414,6 +1426,8 @@ typedef struct edge_connection_t {
 
   uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit
                          * connection.  Exit connections only. */
+  uint32_t begincell_flags; /** Flags sent or received in the BEGIN cell
+                             * for this connection */
 
   streamid_t stream_id; /**< The stream ID used for this edge connection on its
                          * circuit */
@@ -1429,6 +1443,8 @@ typedef struct edge_connection_t {
 
   /** True iff this connection is for a DNS request only. */
   unsigned int is_dns_request:1;
+  /** True iff this connection is for a PTR DNS request. (exit only) */
+  unsigned int is_reverse_dns_lookup:1;
 
   unsigned int edge_has_sent_end:1; /**< For debugging; only used on edge
                          * connections.  Set once we've set the stream end,
@@ -1520,6 +1536,15 @@ typedef struct entry_connection_t {
    */
   unsigned int may_use_optimistic_data : 1;
 
+  /** Should we permit IPv4 and IPv6 traffic to use this connection?
+   *
+   * @{ */
+  unsigned int ipv4_traffic_ok : 1;
+  unsigned int ipv6_traffic_ok : 1;
+  /** @} */
+  /** Should we say we prefer IPv6 traffic? */
+  unsigned int prefer_ipv6_traffic : 1;
+
 } entry_connection_t;
 
 /** Subtype of connection_t for an "directory connection" -- that is, an HTTP
@@ -1730,7 +1755,15 @@ typedef struct addr_policy_t {
   maskbits_t maskbits; /**< Accept/reject all addresses <b>a</b> such that the
                  * first <b>maskbits</b> bits of <b>a</b> match
                  * <b>addr</b>. */
-  tor_addr_t addr; /**< Base address to accept or reject. */
+  /** Base address to accept or reject.
+   *
+   * Note that wildcards are treated
+   * differntly depending on address family. An AF_UNSPEC address means
+   * "All addresses, IPv4 or IPv6." An AF_INET address with maskbits==0 means
+   * "All IPv4 addresses" and an AF_INET6 address with maskbits == 0 means
+   * "All IPv6 addresses".
+  **/
+  tor_addr_t addr;
   uint16_t prt_min; /**< Lowest port number to accept/reject. */
   uint16_t prt_max; /**< Highest port number to accept/reject. */
 } addr_policy_t;
@@ -1870,7 +1903,10 @@ typedef struct {
   /** How many bytes/s is this router known to handle? */
   uint32_t bandwidthcapacity;
   smartlist_t *exit_policy; /**< What streams will this OR permit
-                             * to exit?  NULL for 'reject *:*'. */
+                             * to exit on IPv4?  NULL for 'reject *:*'. */
+  /** What streams will this OR permit to exit on IPv6?
+   * NULL for 'reject *:*' */
+  struct short_policy_t *ipv6_exit_policy;
   long uptime; /**< How many seconds the router claims to have been up */
   smartlist_t *declared_family; /**< Nicknames of router which this router
                                  * claims are its family. */
@@ -2076,8 +2112,11 @@ typedef struct microdesc_t {
   uint16_t ipv6_orport;
   /** As routerinfo_t.family */
   smartlist_t *family;
-  /** Exit policy summary */
+  /** IPv4 exit policy summary */
   short_policy_t *exit_policy;
+  /** IPv6 exit policy summary */
+  short_policy_t *ipv6_exit_policy;
+
 } microdesc_t;
 
 /** A node_t represents a Tor router.
@@ -3026,8 +3065,11 @@ typedef struct port_cfg_t {
   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;
+  unsigned int bind_ipv4_only : 1;
+  unsigned int bind_ipv6_only : 1;
+  unsigned int ipv4_traffic : 1;
+  unsigned int ipv6_traffic : 1;
+  unsigned int prefer_ipv6 : 1;
 
   /* Unix sockets only: */
   /** Path for an AF_UNIX address */
@@ -3729,6 +3771,8 @@ typedef struct {
   int PathBiasScaleFactor;
   /** @} */
 
+  int IPv6Exit; /**< Do we support exiting to IPv6 addresses? */
+
 } or_options_t;
 
 /** Persistent state for an onion router, as saved to disk. */

+ 147 - 38
src/or/policies.c

@@ -59,8 +59,10 @@ typedef struct policy_summary_item_t {
 static const char *private_nets[] = {
   "0.0.0.0/8", "169.254.0.0/16",
   "127.0.0.0/8", "192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12",
-  // "fc00::/7", "fe80::/10", "fec0::/10", "::/127",
-  NULL };
+  "[::]/8",
+  "[fc00::]/7", "[fe80::]/10", "[fec0::]/10", "[ff00::]/8", "[::]/127",
+  NULL
+};
 
 /** Replace all "private" entries in *<b>policy</b> with their expanded
  * equivalents. */
@@ -87,7 +89,8 @@ policy_expand_private(smartlist_t **policy)
        memcpy(&newpolicy, p, sizeof(addr_policy_t));
        newpolicy.is_private = 0;
        newpolicy.is_canonical = 0;
-       if (tor_addr_parse_mask_ports(private_nets[i], &newpolicy.addr,
+       if (tor_addr_parse_mask_ports(private_nets[i], 0,
+                               &newpolicy.addr,
                                &newpolicy.maskbits, &port_min, &port_max)<0) {
          tor_assert(0);
        }
@@ -100,6 +103,49 @@ policy_expand_private(smartlist_t **policy)
   *policy = tmp;
 }
 
+/** Expand each of the AF_UNSPEC elements in *<b>policy</b> (which indicate
+ * protocol-neutral wildcards) into a pair of wildcard elements: one IPv4-
+ * specific and one IPv6-specific. */
+void
+policy_expand_unspec(smartlist_t **policy)
+{
+  smartlist_t *tmp;
+  if (!*policy)
+    return;
+
+  tmp = smartlist_new();
+  SMARTLIST_FOREACH_BEGIN(*policy, addr_policy_t *, p) {
+    sa_family_t family = tor_addr_family(&p->addr);
+    if (family == AF_INET6 || family == AF_INET || p->is_private) {
+      smartlist_add(tmp, p);
+    } else if (family == AF_UNSPEC) {
+      addr_policy_t newpolicy_ipv4;
+      addr_policy_t newpolicy_ipv6;
+      memcpy(&newpolicy_ipv4, p, sizeof(addr_policy_t));
+      memcpy(&newpolicy_ipv6, p, sizeof(addr_policy_t));
+      newpolicy_ipv4.is_canonical = 0;
+      newpolicy_ipv6.is_canonical = 0;
+      if (p->maskbits != 0) {
+        log_warn(LD_BUG, "AF_UNSPEC policy with maskbits==%d", p->maskbits);
+        newpolicy_ipv4.maskbits = 0;
+        newpolicy_ipv6.maskbits = 0;
+      }
+      tor_addr_from_ipv4h(&newpolicy_ipv4.addr, 0);
+      tor_addr_from_ipv6_bytes(&newpolicy_ipv6.addr,
+                               "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
+      smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy_ipv4));
+      smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy_ipv6));
+      addr_policy_free(p);
+    } else {
+      log_warn(LD_BUG, "Funny-looking address policy with family %d", family);
+      smartlist_add(tmp, p);
+    }
+  } SMARTLIST_FOREACH_END(p);
+
+  smartlist_free(*policy);
+  *policy = tmp;
+}
+
 /**
  * Given a linked list of config lines containing "allow" and "deny"
  * tokens, parse them and append the result to <b>dest</b>. Return -1
@@ -144,6 +190,7 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
     addr_policy_list_free(result);
   } else {
     policy_expand_private(&result);
+    policy_expand_unspec(&result);
 
     if (*dest) {
       smartlist_add_all(*dest, result);
@@ -390,6 +437,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
   *msg = NULL;
 
   if (policies_parse_exit_policy(options->ExitPolicy, &addr_policy,
+                                 options->IPv6Exit,
                                  options->ExitPolicyRejectPrivate, NULL,
                                  !options->BridgeRelay))
     REJECT("Error in ExitPolicy entry.");
@@ -734,6 +782,10 @@ compare_tor_addr_to_addr_policy(const tor_addr_t *addr, uint16_t port,
 static int
 addr_policy_covers(addr_policy_t *a, addr_policy_t *b)
 {
+  if (tor_addr_family(&a->addr) != tor_addr_family(&b->addr)) {
+    /* You can't cover a different family. */
+    return 0;
+  }
   /* We can ignore accept/reject, since "accept *:80, reject *:80" reduces
    * to "accept *:80". */
   if (a->maskbits > b->maskbits) {
@@ -789,20 +841,32 @@ append_exit_policy_string(smartlist_t **policy, const char *more)
 static void
 exit_policy_remove_redundancies(smartlist_t *dest)
 {
-  addr_policy_t *ap, *tmp, *victim;
+  addr_policy_t *ap, *tmp;
   int i, j;
 
-  /* Step one: find a *:* entry and cut off everything after it. */
-  for (i = 0; i < smartlist_len(dest); ++i) {
-    ap = smartlist_get(dest, i);
-    if (ap->maskbits == 0 && ap->prt_min <= 1 && ap->prt_max >= 65535) {
-      /* This is a catch-all line -- later lines are unreachable. */
-      while (i+1 < smartlist_len(dest)) {
-        victim = smartlist_get(dest, i+1);
-        smartlist_del(dest, i+1);
-        addr_policy_free(victim);
+  /* Step one: kill every ipv4 thing after *4:*, every IPv6 thing after *6:*
+   */
+  {
+    int kill_v4=0, kill_v6=0;
+    for (i = 0; i < smartlist_len(dest); ++i) {
+      sa_family_t family;
+      ap = smartlist_get(dest, i);
+      family = tor_addr_family(&ap->addr);
+      if ((family == AF_INET && kill_v4) ||
+          (family == AF_INET6 && kill_v6)) {
+        smartlist_del_keeporder(dest, i--);
+        addr_policy_free(ap);
+        continue;
+      }
+
+      if (ap->maskbits == 0 && ap->prt_min <= 1 && ap->prt_max >= 65535) {
+        /* This is a catch-all line -- later lines are unreachable. */
+        if (family == AF_INET) {
+          kill_v4 = 1;
+        } else if (family == AF_INET6) {
+          kill_v6 = 1;
+        }
       }
-      break;
     }
   }
 
@@ -868,12 +932,20 @@ exit_policy_remove_redundancies(smartlist_t *dest)
  * policy afterwards. If <b>rejectprivate</b> is true, prepend
  * "reject private:*" to the policy. Return -1 if we can't parse cfg,
  * else return 0.
+ *
+ * This function is used to parse the exit policy from our torrc. For
+ * the functions used to parse the exit policy from a router descriptor,
+ * see router_add_exit_policy.
  */
 int
 policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
+                           int ipv6_exit,
                            int rejectprivate, const char *local_address,
                            int add_default_policy)
 {
+  if (!ipv6_exit) {
+    append_exit_policy_string(dest, "reject *6:*");
+  }
   if (rejectprivate) {
     append_exit_policy_string(dest, "reject private:*");
     if (local_address) {
@@ -884,10 +956,12 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
   }
   if (parse_addr_policy(cfg, dest, -1))
     return -1;
-  if (add_default_policy)
+  if (add_default_policy) {
     append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
-  else
-    append_exit_policy_string(dest, "reject *:*");
+  } else {
+    append_exit_policy_string(dest, "reject *4:*");
+    append_exit_policy_string(dest, "reject *6:*");
+  }
   exit_policy_remove_redundancies(*dest);
 
   return 0;
@@ -898,7 +972,8 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
 void
 policies_exit_policy_append_reject_star(smartlist_t **dest)
 {
-  append_exit_policy_string(dest, "reject *:*");
+  append_exit_policy_string(dest, "reject *4:*");
+  append_exit_policy_string(dest, "reject *6:*");
 }
 
 /** Replace the exit policy of <b>node</b> with reject *:* */
@@ -974,18 +1049,23 @@ exit_policy_is_general_exit(smartlist_t *policy)
 /** Return false if <b>policy</b> might permit access to some addr:port;
  * otherwise if we are certain it rejects everything, return true. */
 int
-policy_is_reject_star(const smartlist_t *policy)
+policy_is_reject_star(const smartlist_t *policy, sa_family_t family)
 {
   if (!policy) /*XXXX disallow NULL policies? */
     return 1;
-  SMARTLIST_FOREACH(policy, addr_policy_t *, p, {
-    if (p->policy_type == ADDR_POLICY_ACCEPT)
+  SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, p) {
+    if (p->policy_type == ADDR_POLICY_ACCEPT &&
+        (tor_addr_family(&p->addr) == family ||
+         tor_addr_family(&p->addr) == AF_UNSPEC)) {
       return 0;
-    else if (p->policy_type == ADDR_POLICY_REJECT &&
-             p->prt_min <= 1 && p->prt_max == 65535 &&
-             p->maskbits == 0)
+    } else if (p->policy_type == ADDR_POLICY_REJECT &&
+               p->prt_min <= 1 && p->prt_max == 65535 &&
+               p->maskbits == 0 &&
+               (tor_addr_family(&p->addr) == family ||
+                tor_addr_family(&p->addr) == AF_UNSPEC)) {
       return 1;
-  });
+    }
+  } SMARTLIST_FOREACH_END(p);
   return 1;
 }
 
@@ -1000,17 +1080,26 @@ policy_write_item(char *buf, size_t buflen, addr_policy_t *policy,
   const char *addrpart;
   int result;
   const int is_accept = policy->policy_type == ADDR_POLICY_ACCEPT;
-  const int is_ip6 = tor_addr_family(&policy->addr) == AF_INET6;
+  const sa_family_t family = tor_addr_family(&policy->addr);
+  const int is_ip6 = (family == AF_INET6);
 
   tor_addr_to_str(addrbuf, &policy->addr, sizeof(addrbuf), 1);
 
   /* write accept/reject 1.2.3.4 */
-  if (policy->is_private)
+  if (policy->is_private) {
     addrpart = "private";
-  else if (policy->maskbits == 0)
-    addrpart = "*";
-  else
+  } else if (policy->maskbits == 0) {
+    if (format_for_desc)
+      addrpart = "*";
+    else if (family == AF_INET6)
+      addrpart = "*6";
+    else if (family == AF_INET)
+      addrpart = "*4";
+    else
+      addrpart = "*";
+  } else {
     addrpart = addrbuf;
+  }
 
   result = tor_snprintf(buf, buflen, "%s%s %s",
                         is_accept ? "accept" : "reject",
@@ -1192,8 +1281,8 @@ policy_summary_add_item(smartlist_t *summary, addr_policy_t *p)
      for (i = 0; private_nets[i]; ++i) {
        tor_addr_t addr;
        maskbits_t maskbits;
-       if (tor_addr_parse_mask_ports(private_nets[i], &addr,
-                                  &maskbits, NULL, NULL)<0) {
+       if (tor_addr_parse_mask_ports(private_nets[i], 0, &addr,
+                                     &maskbits, NULL, NULL)<0) {
          tor_assert(0);
        }
        if (tor_addr_compare(&p->addr, &addr, CMP_EXACT) == 0 &&
@@ -1219,7 +1308,7 @@ policy_summary_add_item(smartlist_t *summary, addr_policy_t *p)
  * is an exception to the shorter-representation-wins rule).
  */
 char *
-policy_summarize(smartlist_t *policy)
+policy_summarize(smartlist_t *policy, sa_family_t family)
 {
   smartlist_t *summary = policy_summary_create();
   smartlist_t *accepts, *rejects;
@@ -1231,9 +1320,16 @@ policy_summarize(smartlist_t *policy)
   tor_assert(policy);
 
   /* Create the summary list */
-  SMARTLIST_FOREACH(policy, addr_policy_t *, p, {
+  SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, p) {
+    sa_family_t f = tor_addr_family(&p->addr);
+    if (f != AF_INET && f != AF_INET6) {
+      log_warn(LD_BUG, "Weird family when summarizing address policy");
+    }
+    if (f != family)
+      continue;
+    /* XXXX-ipv6 More family work is needed */
     policy_summary_add_item(summary, p);
-  });
+  } SMARTLIST_FOREACH_END(p);
 
   /* Now create two lists of strings, one for accepted and one
    * for rejected ports.  We take care to merge ranges so that
@@ -1530,16 +1626,29 @@ compare_tor_addr_to_node_policy(const tor_addr_t *addr, uint16_t port,
   if (node->rejects_all)
     return ADDR_POLICY_REJECTED;
 
-  if (node->ri)
+  if (addr && tor_addr_family(addr) == AF_INET6) {
+    const short_policy_t *p = NULL;
+    if (node->ri)
+      p = node->ri->ipv6_exit_policy;
+    else if (node->md)
+      p = node->md->ipv6_exit_policy;
+    if (p)
+      return compare_tor_addr_to_short_policy(addr, port, p);
+    else
+      return ADDR_POLICY_REJECTED;
+  }
+
+  if (node->ri) {
     return compare_tor_addr_to_addr_policy(addr, port, node->ri->exit_policy);
-  else if (node->md) {
+  } else if (node->md) {
     if (node->md->exit_policy == NULL)
       return ADDR_POLICY_REJECTED;
     else
       return compare_tor_addr_to_short_policy(addr, port,
                                               node->md->exit_policy);
-  } else
+  } else {
     return ADDR_POLICY_PROBABLY_REJECTED;
+  }
 }
 
 /** Implementation for GETINFO control command: knows the answer for questions

+ 4 - 2
src/or/policies.h

@@ -31,6 +31,7 @@ int authdir_policy_badexit_address(uint32_t addr, uint16_t port);
 
 int validate_addr_policies(const or_options_t *options, char **msg);
 void policy_expand_private(smartlist_t **policy);
+void policy_expand_unspec(smartlist_t **policy);
 int policies_parse_from_options(const or_options_t *options);
 
 addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent);
@@ -42,12 +43,13 @@ addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
                               uint16_t port, const node_t *node);
 
 int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
+                               int ipv6exit,
                                int rejectprivate, const char *local_address,
                                int add_default_policy);
 void policies_exit_policy_append_reject_star(smartlist_t **dest);
 void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter);
 int exit_policy_is_general_exit(smartlist_t *policy);
-int policy_is_reject_star(const smartlist_t *policy);
+int policy_is_reject_star(const smartlist_t *policy, sa_family_t family);
 int getinfo_helper_policies(control_connection_t *conn,
                             const char *question, char **answer,
                             const char **errmsg);
@@ -58,7 +60,7 @@ void addr_policy_list_free(smartlist_t *p);
 void addr_policy_free(addr_policy_t *p);
 void policies_free_all(void);
 
-char *policy_summarize(smartlist_t *policy);
+char *policy_summarize(smartlist_t *policy, sa_family_t family);
 
 short_policy_t *parse_short_policy(const char *summary);
 char *write_short_policy(const short_policy_t *policy);

+ 113 - 30
src/or/relay.c

@@ -12,6 +12,7 @@
 
 #define RELAY_PRIVATE
 #include "or.h"
+#include "addressmap.h"
 #include "buffers.h"
 #include "channel.h"
 #include "circuitbuild.h"
@@ -704,27 +705,45 @@ connection_ap_process_end_not_open(
     switch (reason) {
       case END_STREAM_REASON_EXITPOLICY:
         if (rh->length >= 5) {
-          uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
-          int ttl;
-          if (!addr) {
+          tor_addr_t addr;
+          int ttl = -1;
+          tor_addr_make_unspec(&addr);
+          if (rh->length == 5 || rh->length == 9) {
+            tor_addr_from_ipv4n(&addr,
+                                get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
+            if (rh->length == 9)
+              ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5));
+          } else if (rh->length == 17 || rh->length == 21) {
+            tor_addr_from_ipv6_bytes(&addr,
+                                (char*)(cell->payload+RELAY_HEADER_SIZE+1));
+            if (rh->length == 21)
+              ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+17));
+          }
+          if (tor_addr_is_null(&addr)) {
             log_info(LD_APP,"Address '%s' resolved to 0.0.0.0. Closing,",
                      safe_str(conn->socks_request->address));
             connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
             return 0;
           }
-          if (rh->length >= 9)
-            ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5));
-          else
-            ttl = -1;
 
+          if ((tor_addr_family(&addr) == AF_INET && !conn->ipv4_traffic_ok) ||
+              (tor_addr_family(&addr) == AF_INET6 && !conn->ipv6_traffic_ok)) {
+            log_fn(LOG_PROTOCOL_WARN, LD_APP,
+                   "Got an EXITPOLICY failure on a connection with a "
+                   "mismatched family. Closing.");
+            connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+            return 0;
+          }
           if (get_options()->ClientDNSRejectInternalAddresses &&
-              is_internal_IP(addr, 0)) {
+              tor_addr_is_internal(&addr, 0)) {
             log_info(LD_APP,"Address '%s' resolved to internal. Closing,",
                      safe_str(conn->socks_request->address));
             connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
             return 0;
           }
-          client_dns_set_addressmap(conn->socks_request->address, addr,
+
+          client_dns_set_addressmap(circ,
+                                    conn->socks_request->address, &addr,
                                     conn->chosen_exit_name, ttl);
         }
         /* check if he *ought* to have allowed it */
@@ -827,20 +846,60 @@ connection_ap_process_end_not_open(
 }
 
 /** Helper: change the socks_request-&gt;address field on conn to the
- * dotted-quad representation of <b>new_addr</b> (given in host order),
+ * dotted-quad representation of <b>new_addr</b>,
  * and send an appropriate REMAP event. */
 static void
-remap_event_helper(entry_connection_t *conn, uint32_t new_addr)
+remap_event_helper(entry_connection_t *conn, const tor_addr_t *new_addr)
 {
-  struct in_addr in;
-
-  in.s_addr = htonl(new_addr);
-  tor_inet_ntoa(&in, conn->socks_request->address,
-                sizeof(conn->socks_request->address));
+  tor_addr_to_str(conn->socks_request->address, new_addr,
+                  sizeof(conn->socks_request->address),
+                  1);
   control_event_stream_status(conn, STREAM_EVENT_REMAP,
                               REMAP_STREAM_SOURCE_EXIT);
 }
 
+/** Extract the contents of a connected cell in <b>cell</b>, whose relay
+ * header has already been parsed into <b>rh</b>. On success, set
+ * <b>addr_out</b> to the address we're connected to, and <b>ttl_out</b> to
+ * the ttl of that address, in seconds, and return 0.  On failure, return
+ * -1. */
+int
+connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
+                     tor_addr_t *addr_out, int *ttl_out)
+{
+  uint32_t bytes;
+  const uint8_t *payload = cell->payload + RELAY_HEADER_SIZE;
+
+  tor_addr_make_unspec(addr_out);
+  *ttl_out = -1;
+  if (rh->length == 0)
+    return 0;
+  if (rh->length < 4)
+    return -1;
+  bytes = ntohl(get_uint32(payload));
+
+  /* If bytes is 0, this is maybe a v6 address. Otherwise it's a v4 address */
+  if (bytes != 0) {
+    /* v4 address */
+    tor_addr_from_ipv4h(addr_out, bytes);
+    if (rh->length >= 8) {
+      bytes = ntohl(get_uint32(payload + 4));
+      if (bytes <= INT32_MAX)
+        *ttl_out = bytes;
+    }
+  } else {
+    if (rh->length < 25) /* 4 bytes of 0s, 1 addr, 16 ipv4, 4 ttl. */
+      return -1;
+    if (get_uint8(payload + 4) != 6)
+      return -1;
+    tor_addr_from_ipv6_bytes(addr_out, (char*)(payload + 5));
+    bytes = ntohl(get_uint32(payload + 21));
+    if (bytes <= INT32_MAX)
+      *ttl_out = (int) bytes;
+  }
+  return 0;
+}
+
 /** An incoming relay cell has arrived from circuit <b>circ</b> to
  * stream <b>conn</b>.
  *
@@ -871,6 +930,8 @@ connection_edge_process_relay_cell_not_open(
 
   if (conn->base_.type == CONN_TYPE_AP &&
       rh->command == RELAY_COMMAND_CONNECTED) {
+    tor_addr_t addr;
+    int ttl;
     entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
     tor_assert(CIRCUIT_IS_ORIGIN(circ));
     if (conn->base_.state != AP_CONN_STATE_CONNECT_WAIT) {
@@ -881,26 +942,41 @@ connection_edge_process_relay_cell_not_open(
     conn->base_.state = AP_CONN_STATE_OPEN;
     log_info(LD_APP,"'connected' received after %d seconds.",
              (int)(time(NULL) - conn->base_.timestamp_lastread));
-    if (rh->length >= 4) {
-      uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE));
-      int ttl;
-      if (!addr || (get_options()->ClientDNSRejectInternalAddresses &&
-                    is_internal_IP(addr, 0))) {
+    if (connected_cell_parse(rh, cell, &addr, &ttl) < 0) {
+      log_fn(LOG_PROTOCOL_WARN, LD_APP,
+             "Got a badly formatted connected cell. Closing.");
+      connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
+      connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TORPROTOCOL);
+    }
+    if (tor_addr_family(&addr) != AF_UNSPEC) {
+      const sa_family_t family = tor_addr_family(&addr);
+      if (tor_addr_is_null(&addr) ||
+          (get_options()->ClientDNSRejectInternalAddresses &&
+           tor_addr_is_internal(&addr, 0))) {
         log_info(LD_APP, "...but it claims the IP address was %s. Closing.",
-                 fmt_addr32(addr));
+                 fmt_addr(&addr));
         connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
         connection_mark_unattached_ap(entry_conn,
                                       END_STREAM_REASON_TORPROTOCOL);
         return 0;
       }
-      if (rh->length >= 8)
-        ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+4));
-      else
-        ttl = -1;
-      client_dns_set_addressmap(entry_conn->socks_request->address, addr,
+
+      if ((family == AF_INET && ! entry_conn->ipv4_traffic_ok) ||
+          (family == AF_INET6 && ! entry_conn->ipv6_traffic_ok)) {
+        log_fn(LOG_PROTOCOL_WARN, LD_APP,
+               "Got a connected cell to %s with unsupported address family."
+               " Closing.", fmt_addr(&addr));
+        connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
+        connection_mark_unattached_ap(entry_conn,
+                                      END_STREAM_REASON_TORPROTOCOL);
+        return 0;
+      }
+
+      client_dns_set_addressmap(TO_ORIGIN_CIRCUIT(circ),
+                                entry_conn->socks_request->address, &addr,
                                 entry_conn->chosen_exit_name, ttl);
 
-      remap_event_helper(entry_conn, addr);
+      remap_event_helper(entry_conn, &addr);
     }
     circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
     /* don't send a socks reply to transparent conns */
@@ -990,8 +1066,15 @@ connection_edge_process_relay_cell_not_open(
                    ttl,
                    -1);
     if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) {
-      uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
-      remap_event_helper(entry_conn, addr);
+      tor_addr_t addr;
+      tor_addr_from_ipv4n(&addr,
+                          get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
+      remap_event_helper(entry_conn, &addr);
+    } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) {
+      tor_addr_t addr;
+      tor_addr_from_ipv6_bytes(&addr,
+                               (char*)(cell->payload+RELAY_HEADER_SIZE+2));
+      remap_event_helper(entry_conn, &addr);
     }
     connection_mark_unattached_ap(entry_conn,
                               END_STREAM_REASON_DONE |

+ 2 - 0
src/or/relay.h

@@ -68,6 +68,8 @@ void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan);
 #ifdef RELAY_PRIVATE
 int relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction,
                 crypt_path_t **layer_hint, char *recognized);
+int connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
+                         tor_addr_t *addr_out, int *ttl_out);
 #endif
 
 #endif

+ 47 - 12
src/or/router.c

@@ -1370,22 +1370,34 @@ router_upload_dir_desc_to_dirservers(int force)
  * conn.  Return 0 if we accept; non-0 if we reject.
  */
 int
-router_compare_to_my_exit_policy(edge_connection_t *conn)
+router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
 {
   if (!router_get_my_routerinfo()) /* make sure desc_routerinfo exists */
     return -1;
 
   /* make sure it's resolved to something. this way we can't get a
      'maybe' below. */
-  if (tor_addr_is_null(&conn->base_.addr))
+  if (tor_addr_is_null(addr))
     return -1;
 
-  /* XXXX IPv6 */
-  if (tor_addr_family(&conn->base_.addr) != AF_INET)
+  /* look at desc_routerinfo->exit_policy for both the v4 and the v6
+   * policies.  The exit_policy field in desc_routerinfo is a bit unusual,
+   * in that it contains IPv6 and IPv6 entries.  We don't want to look
+   * at desc_routerinfio->ipv6_exit_policy, since that's a port summary. */
+  if ((tor_addr_family(addr) == AF_INET ||
+       tor_addr_family(addr) == AF_INET6)) {
+    return compare_tor_addr_to_addr_policy(addr, port,
+                    desc_routerinfo->exit_policy) != ADDR_POLICY_ACCEPTED;
+#if 0
+  } else if (tor_addr_family(addr) == AF_INET6) {
+    return get_options()->IPv6Exit &&
+      desc_routerinfo->ipv6_exit_policy &&
+      compare_tor_addr_to_short_policy(addr, port,
+                  desc_routerinfo->ipv6_exit_policy) != ADDR_POLICY_ACCEPTED;
+#endif
+  } else {
     return -1;
-
-  return compare_tor_addr_to_addr_policy(&conn->base_.addr, conn->base_.port,
-                   desc_routerinfo->exit_policy) != ADDR_POLICY_ACCEPTED;
+  }
 }
 
 /** Return true iff my exit policy is reject *:*.  Return -1 if we don't
@@ -1561,7 +1573,7 @@ router_rebuild_descriptor(int force)
     SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) {
       if (p->type == CONN_TYPE_OR_LISTENER &&
           ! p->no_advertise &&
-          ! p->ipv4_only &&
+          ! p->bind_ipv4_only &&
           tor_addr_family(&p->addr) == AF_INET6) {
         if (! tor_addr_is_internal(&p->addr, 0)) {
           ipv6_orport = p;
@@ -1604,11 +1616,20 @@ router_rebuild_descriptor(int force)
     policies_exit_policy_append_reject_star(&ri->exit_policy);
   } else {
     policies_parse_exit_policy(options->ExitPolicy, &ri->exit_policy,
+                               options->IPv6Exit,
                                options->ExitPolicyRejectPrivate,
                                ri->address, !options->BridgeRelay);
   }
   ri->policy_is_reject_star =
-    policy_is_reject_star(ri->exit_policy);
+    policy_is_reject_star(ri->exit_policy, AF_INET) &&
+    policy_is_reject_star(ri->exit_policy, AF_INET6);
+
+  if (options->IPv6Exit) {
+    char *p_tmp = policy_summarize(ri->exit_policy, AF_INET6);
+    if (p_tmp)
+      ri->ipv6_exit_policy = parse_short_policy(p_tmp);
+    tor_free(p_tmp);
+  }
 
 #if 0
   /* XXXX NM NM I belive this is safe to remove */
@@ -2001,7 +2022,6 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
   size_t onion_pkeylen, identity_pkeylen;
   size_t written;
   int result=0;
-  addr_policy_t *tmpe;
   char *family_line;
   char *extra_or_address = NULL;
   const or_options_t *options = get_options();
@@ -2130,11 +2150,12 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
   if (!router->exit_policy || !smartlist_len(router->exit_policy)) {
     strlcat(s+written, "reject *:*\n", maxlen-written);
     written += strlen("reject *:*\n");
-    tmpe = NULL;
   } else if (router->exit_policy) {
     int i;
     for (i = 0; i < smartlist_len(router->exit_policy); ++i) {
-      tmpe = smartlist_get(router->exit_policy, i);
+      addr_policy_t *tmpe = smartlist_get(router->exit_policy, i);
+      if (tor_addr_family(&tmpe->addr) == AF_INET6)
+        continue; /* Don't include IPv6 parts of address policy */
       result = policy_write_item(s+written, maxlen-written, tmpe, 1);
       if (result < 0) {
         log_warn(LD_BUG,"descriptor policy_write_item ran out of room!");
@@ -2150,6 +2171,20 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
     }
   }
 
+  if (router->ipv6_exit_policy) {
+    char *p6 = write_short_policy(router->ipv6_exit_policy);
+    if (p6 && strcmp(p6, "reject 1-65535")) {
+      result = tor_snprintf(s+written, maxlen-written,
+                            "ipv6-policy %s\n", p6);
+      if (result<0) {
+        log_warn(LD_BUG,"Descriptor printf of policy ran out of room");
+        return -1;
+      }
+      written += result;
+    }
+    tor_free(p6);
+  }
+
   if (written + DIROBJ_MAX_SIG_LEN > maxlen) {
     /* Not enough room for signature. */
     log_warn(LD_BUG,"not enough room left in descriptor for signature!");

+ 1 - 1
src/or/router.h

@@ -72,7 +72,7 @@ void check_descriptor_bandwidth_changed(time_t now);
 void check_descriptor_ipaddress_changed(time_t now);
 void router_new_address_suggestion(const char *suggestion,
                                    const dir_connection_t *d_conn);
-int router_compare_to_my_exit_policy(edge_connection_t *conn);
+int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port);
 int router_my_exit_policy_is_reject_star(void);
 const routerinfo_t *router_get_my_routerinfo(void);
 extrainfo_t *router_get_my_extrainfo(void);

+ 1 - 0
src/or/routerlist.c

@@ -2402,6 +2402,7 @@ routerinfo_free(routerinfo_t *router)
     smartlist_free(router->declared_family);
   }
   addr_policy_list_free(router->exit_policy);
+  short_policy_free(router->ipv6_exit_policy);
 
   memset(router, 77, sizeof(routerinfo_t));
 

+ 31 - 7
src/or/routerparse.c

@@ -66,6 +66,7 @@ typedef enum {
   K_SERVER_VERSIONS,
   K_OR_ADDRESS,
   K_P,
+  K_P6,
   K_R,
   K_A,
   K_S,
@@ -77,6 +78,7 @@ typedef enum {
   K_CACHES_EXTRA_INFO,
   K_HIDDEN_SERVICE_DIR,
   K_ALLOW_SINGLE_HOP_EXITS,
+  K_IPV6_POLICY,
 
   K_DIRREQ_END,
   K_DIRREQ_V2_IPS,
@@ -271,6 +273,7 @@ static token_rule_t routerdesc_token_table[] = {
   T0N("reject6",             K_REJECT6,             ARGS,    NO_OBJ ),
   T0N("accept6",             K_ACCEPT6,             ARGS,    NO_OBJ ),
   T1_START( "router",        K_ROUTER,              GE(5),   NO_OBJ ),
+  T01("ipv6-policy",         K_IPV6_POLICY,         CONCAT_ARGS, NO_OBJ),
   T1( "signing-key",         K_SIGNING_KEY,         NO_ARGS, NEED_KEY_1024 ),
   T1( "onion-key",           K_ONION_KEY,           NO_ARGS, NEED_KEY_1024 ),
   T1_END( "router-signature",    K_ROUTER_SIGNATURE,    NO_ARGS, NEED_OBJ ),
@@ -527,6 +530,7 @@ static token_rule_t microdesc_token_table[] = {
   T0N("a",                     K_A,                GE(1),       NO_OBJ ),
   T01("family",                K_FAMILY,           ARGS,        NO_OBJ ),
   T01("p",                     K_P,                CONCAT_ARGS, NO_OBJ ),
+  T01("p6",                    K_P6,               CONCAT_ARGS, NO_OBJ ),
   A01("@last-listed",          A_LAST_LISTED,      CONCAT_ARGS, NO_OBJ ),
   END_OF_TABLE
 };
@@ -535,7 +539,8 @@ static token_rule_t microdesc_token_table[] = {
 
 /* static function prototypes */
 static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok);
-static addr_policy_t *router_parse_addr_policy(directory_token_t *tok);
+static addr_policy_t *router_parse_addr_policy(directory_token_t *tok,
+                                               unsigned fmt_flags);
 static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok);
 
 static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
@@ -1280,7 +1285,8 @@ find_single_ipv6_orport(const smartlist_t *list,
     uint16_t port_min, port_max;
     tor_assert(t->n_args >= 1);
     /* XXXX Prop186 the full spec allows much more than this. */
-    if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min,
+    if (tor_addr_parse_mask_ports(t->args[0], 0,
+                                  &a, &bits, &port_min,
                                   &port_max) == AF_INET6 &&
         bits == 128 &&
         port_min == port_max) {
@@ -1568,7 +1574,18 @@ router_parse_entry_from_string(const char *s, const char *end,
                       goto err;
                     });
   policy_expand_private(&router->exit_policy);
-  if (policy_is_reject_star(router->exit_policy))
+
+  if ((tok = find_opt_by_keyword(tokens, K_IPV6_POLICY)) && tok->n_args) {
+    router->ipv6_exit_policy = parse_short_policy(tok->args[0]);
+    if (! router->ipv6_exit_policy) {
+      log_warn(LD_DIR , "Error in ipv6-policy %s", escaped(tok->args[0]));
+      goto err;
+    }
+  }
+
+  if (policy_is_reject_star(router->exit_policy, AF_INET) &&
+      (!router->ipv6_exit_policy ||
+       short_policy_is_reject_star(router->ipv6_exit_policy)))
     router->policy_is_reject_star = 1;
 
   if ((tok = find_opt_by_keyword(tokens, K_FAMILY)) && tok->n_args) {
@@ -3632,6 +3649,10 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
 /** Parse the addr policy in the string <b>s</b> and return it.  If
  * assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
  * ADDR_POLICY_REJECT) for items that specify no action.
+ *
+ * The addr_policy_t returned by this function can have its address set to
+ * AF_UNSPEC for '*'.  Use policy_expand_unspec() to turn this into a pair
+ * of AF_INET and AF_INET6 items.
  */
 addr_policy_t *
 router_parse_addr_policy_item_from_string(const char *s, int assume_action)
@@ -3671,7 +3692,7 @@ router_parse_addr_policy_item_from_string(const char *s, int assume_action)
     goto err;
   }
 
-  r = router_parse_addr_policy(tok);
+  r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR);
   goto done;
  err:
   r = NULL;
@@ -3690,7 +3711,7 @@ static int
 router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
 {
   addr_policy_t *newe;
-  newe = router_parse_addr_policy(tok);
+  newe = router_parse_addr_policy(tok, 0);
   if (!newe)
     return -1;
   if (! router->exit_policy)
@@ -3715,7 +3736,7 @@ router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
 /** Given a K_ACCEPT or K_REJECT token and a router, create and return
  * a new exit_policy_t corresponding to the token. */
 static addr_policy_t *
-router_parse_addr_policy(directory_token_t *tok)
+router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
 {
   addr_policy_t newe;
   char *arg;
@@ -3737,7 +3758,7 @@ router_parse_addr_policy(directory_token_t *tok)
   else
     newe.policy_type = ADDR_POLICY_ACCEPT;
 
-  if (tor_addr_parse_mask_ports(arg, &newe.addr, &newe.maskbits,
+  if (tor_addr_parse_mask_ports(arg, fmt_flags, &newe.addr, &newe.maskbits,
                                 &newe.prt_min, &newe.prt_max) < 0) {
     log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg));
     return NULL;
@@ -4478,6 +4499,9 @@ microdescs_parse_from_string(const char *s, const char *eos,
     if ((tok = find_opt_by_keyword(tokens, K_P))) {
       md->exit_policy = parse_short_policy(tok->args[0]);
     }
+    if ((tok = find_opt_by_keyword(tokens, K_P6))) {
+      md->ipv6_exit_policy = parse_short_policy(tok->args[0]);
+    }
 
     crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);
 

+ 1 - 0
src/or/routerset.c

@@ -148,6 +148,7 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
         SMARTLIST_DEL_CURRENT(list, nick);
       }
   } SMARTLIST_FOREACH_END(nick);
+  policy_expand_unspec(&target->policies);
   smartlist_add_all(target->list, list);
   smartlist_free(list);
   if (added_countries)

+ 1 - 0
src/test/include.am

@@ -14,6 +14,7 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
 src_test_test_SOURCES = \
 	src/test/test.c \
 	src/test/test_addr.c \
+	src/test/test_cell_formats.c \
 	src/test/test_containers.c \
 	src/test/test_crypto.c \
 	src/test/test_data.c \

+ 10 - 8
src/test/test.c

@@ -1044,9 +1044,9 @@ test_policy_summary_helper(const char *policy_str,
   line.value = (char *)policy_str;
   line.next = NULL;
 
-  r = policies_parse_exit_policy(&line, &policy, 0, NULL, 1);
+  r = policies_parse_exit_policy(&line, &policy, 1, 0, NULL, 1);
   test_eq(r, 0);
-  summary = policy_summarize(policy);
+  summary = policy_summarize(policy, AF_INET);
 
   test_assert(summary != NULL);
   test_streq(summary, expected_summary);
@@ -1101,7 +1101,7 @@ test_policies(void)
   test_assert(ADDR_POLICY_REJECTED ==
           compare_tor_addr_to_addr_policy(&tar, 2, policy));
 
-  test_assert(0 == policies_parse_exit_policy(NULL, &policy2, 1, NULL, 1));
+  test_assert(0 == policies_parse_exit_policy(NULL, &policy2, 1, 1, NULL, 1));
   test_assert(policy2);
 
   policy3 = smartlist_new();
@@ -1176,9 +1176,9 @@ test_policies(void)
   test_assert(!cmp_addr_policies(policy2, policy2));
   test_assert(!cmp_addr_policies(NULL, NULL));
 
-  test_assert(!policy_is_reject_star(policy2));
-  test_assert(policy_is_reject_star(policy));
-  test_assert(policy_is_reject_star(NULL));
+  test_assert(!policy_is_reject_star(policy2, AF_INET));
+  test_assert(policy_is_reject_star(policy, AF_INET));
+  test_assert(policy_is_reject_star(NULL, AF_INET));
 
   addr_policy_list_free(policy);
   policy = NULL;
@@ -1188,11 +1188,11 @@ test_policies(void)
   line.key = (char*)"foo";
   line.value = (char*)"accept *:80,reject private:*,reject *:*";
   line.next = NULL;
-  test_assert(0 == policies_parse_exit_policy(&line, &policy, 0, NULL, 1));
+  test_assert(0 == policies_parse_exit_policy(&line, &policy, 1, 0, NULL, 1));
   test_assert(policy);
   //test_streq(policy->string, "accept *:80");
   //test_streq(policy->next->string, "reject *:*");
-  test_eq(smartlist_len(policy), 2);
+  test_eq(smartlist_len(policy), 4);
 
   /* test policy summaries */
   /* check if we properly ignore private IP addresses */
@@ -1983,6 +1983,7 @@ extern struct testcase_t pt_tests[];
 extern struct testcase_t config_tests[];
 extern struct testcase_t introduce_tests[];
 extern struct testcase_t replaycache_tests[];
+extern struct testcase_t cell_format_tests[];
 
 static struct testgroup_t testgroups[] = {
   { "", test_array },
@@ -1991,6 +1992,7 @@ static struct testgroup_t testgroups[] = {
   { "crypto/", crypto_tests },
   { "container/", container_tests },
   { "util/", util_tests },
+  { "cellfmt/", cell_format_tests },
   { "dir/", dir_tests },
   { "dir/md/", microdesc_tests },
   { "pt/", pt_tests },

+ 112 - 14
src/test/test_addr.c

@@ -159,7 +159,8 @@ test_addr_basic(void)
  * as <b>pt1..pt2</b>. */
 #define test_addr_mask_ports_parse(xx, f, ip1, ip2, ip3, ip4, mm, pt1, pt2) \
   STMT_BEGIN                                                                \
-    test_eq(tor_addr_parse_mask_ports(xx, &t1, &mask, &port1, &port2), f);  \
+    test_eq(tor_addr_parse_mask_ports(xx, 0, &t1, &mask, &port1, &port2),   \
+            f);                                                             \
     p1=tor_inet_ntop(AF_INET6, &t1.addr.in6_addr, bug, sizeof(bug));        \
     test_eq(htonl(ip1), tor_addr_to_in6_addr32(&t1)[0]);            \
     test_eq(htonl(ip2), tor_addr_to_in6_addr32(&t1)[1]);            \
@@ -401,11 +402,11 @@ test_addr_ip6_helpers(void)
   test_addr_compare("0::2:2:1", <, "0::ffff:0.3.2.1");
   test_addr_compare("0::ffff:0.3.2.1", >, "0::0:0:0");
   test_addr_compare("0::ffff:5.2.2.1", <, "::ffff:6.0.0.0"); /* XXXX wrong. */
-  tor_addr_parse_mask_ports("[::ffff:2.3.4.5]", &t1, NULL, NULL, NULL);
-  tor_addr_parse_mask_ports("2.3.4.5", &t2, NULL, NULL, NULL);
+  tor_addr_parse_mask_ports("[::ffff:2.3.4.5]", 0, &t1, NULL, NULL, NULL);
+  tor_addr_parse_mask_ports("2.3.4.5", 0, &t2, NULL, NULL, NULL);
   test_assert(tor_addr_compare(&t1, &t2, CMP_SEMANTIC) == 0);
-  tor_addr_parse_mask_ports("[::ffff:2.3.4.4]", &t1, NULL, NULL, NULL);
-  tor_addr_parse_mask_ports("2.3.4.5", &t2, NULL, NULL, NULL);
+  tor_addr_parse_mask_ports("[::ffff:2.3.4.4]", 0, &t1, NULL, NULL, NULL);
+  tor_addr_parse_mask_ports("2.3.4.5", 0, &t2, NULL, NULL, NULL);
   test_assert(tor_addr_compare(&t1, &t2, CMP_SEMANTIC) < 0);
 
   /* test compare_masked */
@@ -568,6 +569,7 @@ test_addr_ip6_helpers(void)
     test_streq(rbuf, addr_PTR);
   }
 
+  /* XXXX turn this into a separate function; it's not all IPv6. */
   /* test tor_addr_parse_mask_ports */
   test_addr_mask_ports_parse("[::f]/17:47-95", AF_INET6,
                              0, 0, 0, 0x0000000f, 17, 47, 95);
@@ -581,27 +583,123 @@ test_addr_ip6_helpers(void)
                              0xabcd0002, 0, 0, 0x044a0000, 128, 2, 65000);
 
   test_streq(p1, "abcd:2::44a:0");
-  r=tor_addr_parse_mask_ports("[fefef::]/112", &t1, NULL, NULL, NULL);
+  /* Try some long addresses. */
+  r=tor_addr_parse_mask_ports("[ffff:1111:1111:1111:1111:1111:1111:1111]",
+                              0, &t1, NULL, NULL, NULL);
+  test_assert(r == AF_INET6);
+  r=tor_addr_parse_mask_ports("[ffff:1111:1111:1111:1111:1111:1111:11111]",
+                              0, &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[ffff:1111:1111:1111:1111:1111:1111:1111:1]",
+                              0, &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports(
+         "[ffff:1111:1111:1111:1111:1111:1111:ffff:"
+         "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:"
+         "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:"
+         "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]",
+         0, &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  /* Try some failing cases. */
+  r=tor_addr_parse_mask_ports("[fefef::]/112", 0, &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[fefe::/112", 0, &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[fefe::", 0, &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[fefe::X]", 0, &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("efef::/112", 0, &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[f:f:f:f:f:f:f:f::]",0,&t1, NULL, NULL, NULL);
   test_assert(r == -1);
-  r=tor_addr_parse_mask_ports("efef::/112", &t1, NULL, NULL, NULL);
+  r=tor_addr_parse_mask_ports("[::f:f:f:f:f:f:f:f]",0,&t1, NULL, NULL, NULL);
   test_assert(r == -1);
-  r=tor_addr_parse_mask_ports("[f:f:f:f:f:f:f:f::]", &t1, NULL, NULL, NULL);
+  r=tor_addr_parse_mask_ports("[f:f:f:f:f:f:f:f:f]",0,&t1, NULL, NULL, NULL);
   test_assert(r == -1);
-  r=tor_addr_parse_mask_ports("[::f:f:f:f:f:f:f:f]", &t1, NULL, NULL, NULL);
+  r=tor_addr_parse_mask_ports("[f:f:f:f:f::]/fred",0,&t1,&mask, NULL, NULL);
   test_assert(r == -1);
-  r=tor_addr_parse_mask_ports("[f:f:f:f:f:f:f:f:f]", &t1, NULL, NULL, NULL);
+  r=tor_addr_parse_mask_ports("[f:f:f:f:f::]/255.255.0.0",
+                              0,&t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  /* This one will get rejected because it isn't a pure prefix. */
+  r=tor_addr_parse_mask_ports("1.1.2.3/255.255.64.0",0,&t1, &mask,NULL,NULL);
   test_assert(r == -1);
   /* Test for V4-mapped address with mask < 96.  (arguably not valid) */
-  r=tor_addr_parse_mask_ports("[::ffff:1.1.2.2/33]", &t1, &mask, NULL, NULL);
+  r=tor_addr_parse_mask_ports("[::ffff:1.1.2.2/33]",0,&t1, &mask, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("1.1.2.2/33",0,&t1, &mask, NULL, NULL);
+  test_assert(r == -1);
+  /* Try extended wildcard addresses with out TAPMP_EXTENDED_STAR*/
+  r=tor_addr_parse_mask_ports("*4",0,&t1, &mask, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("*6",0,&t1, &mask, NULL, NULL);
+  test_assert(r == -1);
+#if 0
+  /* Try a mask with a wildcard. */
+  r=tor_addr_parse_mask_ports("*/16",0,&t1, &mask, NULL, NULL);
   test_assert(r == -1);
-  r=tor_addr_parse_mask_ports("1.1.2.2/33", &t1, &mask, NULL, NULL);
+  r=tor_addr_parse_mask_ports("*4/16",TAPMP_EXTENDED_STAR,
+                              &t1, &mask, NULL, NULL);
   test_assert(r == -1);
-  r=tor_addr_parse_mask_ports("1.1.2.2/31", &t1, &mask, NULL, NULL);
+  r=tor_addr_parse_mask_ports("*6/30",TAPMP_EXTENDED_STAR,
+                              &t1, &mask, NULL, NULL);
+  test_assert(r == -1);
+#endif
+  /* Basic mask tests*/
+  r=tor_addr_parse_mask_ports("1.1.2.2/31",0,&t1, &mask, NULL, NULL);
+  test_assert(r == AF_INET);
+  tt_int_op(mask,==,31);
+  tt_int_op(tor_addr_family(&t1),==,AF_INET);
+  tt_int_op(tor_addr_to_ipv4h(&t1),==,0x01010202);
+  r=tor_addr_parse_mask_ports("3.4.16.032:1-2",0,&t1, &mask, &port1, &port2);
+  test_assert(r == AF_INET);
+  tt_int_op(mask,==,32);
+  tt_int_op(tor_addr_family(&t1),==,AF_INET);
+  tt_int_op(tor_addr_to_ipv4h(&t1),==,0x03041020);
+  test_assert(port1 == 1);
+  test_assert(port2 == 2);
+  r=tor_addr_parse_mask_ports("1.1.2.3/255.255.128.0",0,&t1, &mask,NULL,NULL);
   test_assert(r == AF_INET);
-  r=tor_addr_parse_mask_ports("[efef::]/112", &t1, &mask, &port1, &port2);
+  tt_int_op(mask,==,17);
+  tt_int_op(tor_addr_family(&t1),==,AF_INET);
+  tt_int_op(tor_addr_to_ipv4h(&t1),==,0x01010203);
+  r=tor_addr_parse_mask_ports("[efef::]/112",0,&t1, &mask, &port1, &port2);
   test_assert(r == AF_INET6);
   test_assert(port1 == 1);
   test_assert(port2 == 65535);
+  /* Try regular wildcard behavior without TAPMP_EXTENDED_STAR */
+  r=tor_addr_parse_mask_ports("*:80-443",0,&t1,&mask,&port1,&port2);
+  tt_int_op(r,==,AF_INET); /* Old users of this always get inet */
+  tt_int_op(tor_addr_family(&t1),==,AF_INET);
+  tt_int_op(tor_addr_to_ipv4h(&t1),==,0);
+  tt_int_op(mask,==,0);
+  tt_int_op(port1,==,80);
+  tt_int_op(port2,==,443);
+  /* Now try wildcards *with* TAPMP_EXTENDED_STAR */
+  r=tor_addr_parse_mask_ports("*:8000-9000",TAPMP_EXTENDED_STAR,
+                              &t1,&mask,&port1,&port2);
+  tt_int_op(r,==,AF_UNSPEC);
+  tt_int_op(tor_addr_family(&t1),==,AF_UNSPEC);
+  tt_int_op(mask,==,0);
+  tt_int_op(port1,==,8000);
+  tt_int_op(port2,==,9000);
+  r=tor_addr_parse_mask_ports("*4:6667",TAPMP_EXTENDED_STAR,
+                              &t1,&mask,&port1,&port2);
+  tt_int_op(r,==,AF_INET);
+  tt_int_op(tor_addr_family(&t1),==,AF_INET);
+  tt_int_op(tor_addr_to_ipv4h(&t1),==,0);
+  tt_int_op(mask,==,0);
+  tt_int_op(port1,==,6667);
+  tt_int_op(port2,==,6667);
+  r=tor_addr_parse_mask_ports("*6",TAPMP_EXTENDED_STAR,
+                              &t1,&mask,&port1,&port2);
+  tt_int_op(r,==,AF_INET6);
+  tt_int_op(tor_addr_family(&t1),==,AF_INET6);
+  tt_assert(tor_mem_is_zero((const char*)tor_addr_to_in6_addr32(&t1), 16));
+  tt_int_op(mask,==,0);
+  tt_int_op(port1,==,1);
+  tt_int_op(port2,==,65535);
 
   /* make sure inet address lengths >= max */
   test_assert(INET_NTOA_BUF_LEN >= sizeof("255.255.255.255"));

+ 386 - 0
src/test/test_cell_formats.c

@@ -0,0 +1,386 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#define CONNECTION_EDGE_PRIVATE
+#define RELAY_PRIVATE
+#include "or.h"
+#include "connection_edge.h"
+#include "relay.h"
+#include "test.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+static void
+test_cfmt_relay_header(void *arg)
+{
+  relay_header_t rh;
+  const uint8_t hdr_1[RELAY_HEADER_SIZE] =
+    "\x03" "\x00\x00" "\x21\x22" "ABCD" "\x01\x03";
+  uint8_t hdr_out[RELAY_HEADER_SIZE];
+  (void)arg;
+
+  tt_int_op(sizeof(hdr_1), ==, RELAY_HEADER_SIZE);
+  relay_header_unpack(&rh, hdr_1);
+  tt_int_op(rh.command, ==, 3);
+  tt_int_op(rh.recognized, ==, 0);
+  tt_int_op(rh.stream_id, ==, 0x2122);
+  test_mem_op(rh.integrity, ==, "ABCD", 4);
+  tt_int_op(rh.length, ==, 0x103);
+
+  relay_header_pack(hdr_out, &rh);
+  test_mem_op(hdr_out, ==, hdr_1, RELAY_HEADER_SIZE);
+
+ done:
+  ;
+}
+
+static void
+make_relay_cell(cell_t *out, uint8_t command,
+                const void *body, size_t bodylen)
+{
+  relay_header_t rh;
+
+  memset(&rh, 0, sizeof(rh));
+  rh.stream_id = 5;
+  rh.command = command;
+  rh.length = bodylen;
+
+  out->command = CELL_RELAY;
+  out->circ_id = 10;
+  relay_header_pack(out->payload, &rh);
+
+  memcpy(out->payload + RELAY_HEADER_SIZE, body, bodylen);
+}
+
+static void
+test_cfmt_begin_cells(void *arg)
+{
+  cell_t cell;
+  begin_cell_t bcell;
+  uint8_t end_reason;
+  (void)arg;
+
+  /* Try begindir. */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN_DIR, "", 0);
+  tt_int_op(0, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+  tt_ptr_op(NULL, ==, bcell.address);
+  tt_int_op(0, ==, bcell.flags);
+  tt_int_op(0, ==, bcell.port);
+  tt_int_op(5, ==, bcell.stream_id);
+  tt_int_op(1, ==, bcell.is_begindir);
+
+  /* A Begindir with extra stuff. */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN_DIR, "12345", 5);
+  tt_int_op(0, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+  tt_ptr_op(NULL, ==, bcell.address);
+  tt_int_op(0, ==, bcell.flags);
+  tt_int_op(0, ==, bcell.port);
+  tt_int_op(5, ==, bcell.stream_id);
+  tt_int_op(1, ==, bcell.is_begindir);
+
+  /* A short but valid begin cell */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN, "a.b:9", 6);
+  tt_int_op(0, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+  tt_str_op("a.b", ==, bcell.address);
+  tt_int_op(0, ==, bcell.flags);
+  tt_int_op(9, ==, bcell.port);
+  tt_int_op(5, ==, bcell.stream_id);
+  tt_int_op(0, ==, bcell.is_begindir);
+  tor_free(bcell.address);
+
+  /* A significantly loner begin cell */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  {
+    const char c[] = "here-is-a-nice-long.hostname.com:65535";
+    make_relay_cell(&cell, RELAY_COMMAND_BEGIN, c, strlen(c)+1);
+  }
+  tt_int_op(0, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+  tt_str_op("here-is-a-nice-long.hostname.com", ==, bcell.address);
+  tt_int_op(0, ==, bcell.flags);
+  tt_int_op(65535, ==, bcell.port);
+  tt_int_op(5, ==, bcell.stream_id);
+  tt_int_op(0, ==, bcell.is_begindir);
+  tor_free(bcell.address);
+
+  /* An IPv4 begin cell. */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN, "18.9.22.169:80", 15);
+  tt_int_op(0, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+  tt_str_op("18.9.22.169", ==, bcell.address);
+  tt_int_op(0, ==, bcell.flags);
+  tt_int_op(80, ==, bcell.port);
+  tt_int_op(5, ==, bcell.stream_id);
+  tt_int_op(0, ==, bcell.is_begindir);
+  tor_free(bcell.address);
+
+  /* An IPv6 begin cell. Let's make sure we handle colons*/
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN,
+                  "[2620::6b0:b:1a1a:0:26e5:480e]:80", 34);
+  tt_int_op(0, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+  tt_str_op("[2620::6b0:b:1a1a:0:26e5:480e]", ==, bcell.address);
+  tt_int_op(0, ==, bcell.flags);
+  tt_int_op(80, ==, bcell.port);
+  tt_int_op(5, ==, bcell.stream_id);
+  tt_int_op(0, ==, bcell.is_begindir);
+  tor_free(bcell.address);
+
+  /* a begin cell with extra junk but not enough for flags. */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  {
+    const char c[] = "another.example.com:80\x00\x01\x02";
+    make_relay_cell(&cell, RELAY_COMMAND_BEGIN, c, sizeof(c)-1);
+  }
+  tt_int_op(0, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+  tt_str_op("another.example.com", ==, bcell.address);
+  tt_int_op(0, ==, bcell.flags);
+  tt_int_op(80, ==, bcell.port);
+  tt_int_op(5, ==, bcell.stream_id);
+  tt_int_op(0, ==, bcell.is_begindir);
+  tor_free(bcell.address);
+
+  /* a begin cell with flags. */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  {
+    const char c[] = "another.example.com:443\x00\x01\x02\x03\x04";
+    make_relay_cell(&cell, RELAY_COMMAND_BEGIN, c, sizeof(c)-1);
+  }
+  tt_int_op(0, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+  tt_str_op("another.example.com", ==, bcell.address);
+  tt_int_op(0x1020304, ==, bcell.flags);
+  tt_int_op(443, ==, bcell.port);
+  tt_int_op(5, ==, bcell.stream_id);
+  tt_int_op(0, ==, bcell.is_begindir);
+  tor_free(bcell.address);
+
+  /* a begin cell with flags and even more cruft after that. */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  {
+    const char c[] = "a-further.example.com:22\x00\xee\xaa\x00\xffHi mom";
+    make_relay_cell(&cell, RELAY_COMMAND_BEGIN, c, sizeof(c)-1);
+  }
+  tt_int_op(0, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+  tt_str_op("a-further.example.com", ==, bcell.address);
+  tt_int_op(0xeeaa00ff, ==, bcell.flags);
+  tt_int_op(22, ==, bcell.port);
+  tt_int_op(5, ==, bcell.stream_id);
+  tt_int_op(0, ==, bcell.is_begindir);
+  tor_free(bcell.address);
+
+  /* bad begin cell: impossible length. */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN, "a.b:80", 7);
+  cell.payload[9] = 0x01; /* Set length to 510 */
+  cell.payload[10] = 0xfe;
+  {
+    relay_header_t rh;
+    relay_header_unpack(&rh, cell.payload);
+    tt_int_op(rh.length, ==, 510);
+  }
+  tt_int_op(-2, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+
+  /* Bad begin cell: no body. */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN, "", 0);
+  tt_int_op(-1, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+
+  /* bad begin cell: no body. */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN, "", 0);
+  tt_int_op(-1, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+
+  /* bad begin cell: no colon */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN, "a.b", 4);
+  tt_int_op(-1, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+
+  /* bad begin cell: no ports */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN, "a.b:", 5);
+  tt_int_op(-1, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+
+  /* bad begin cell: bad port */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN, "a.b:xyz", 8);
+  tt_int_op(-1, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN, "a.b:100000", 11);
+  tt_int_op(-1, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+
+  /* bad begin cell: no nul */
+  memset(&bcell, 0x7f, sizeof(bcell));
+  make_relay_cell(&cell, RELAY_COMMAND_BEGIN, "a.b:80", 6);
+  tt_int_op(-1, ==, begin_cell_parse(&cell, &bcell, &end_reason));
+
+ done:
+  tor_free(bcell.address);
+}
+
+static void
+test_cfmt_connected_cells(void *arg)
+{
+  relay_header_t rh;
+  cell_t cell;
+  tor_addr_t addr;
+  int ttl, r;
+  char *mem_op_hex_tmp = NULL;
+  (void)arg;
+
+  /* Let's try an oldschool one with nothing in it. */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, "", 0);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_UNSPEC);
+  tt_int_op(ttl, ==, -1);
+
+  /* A slightly less oldschool one: only an IPv4 address */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, "\x20\x30\x40\x50", 4);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET);
+  tt_str_op(fmt_addr(&addr), ==, "32.48.64.80");
+  tt_int_op(ttl, ==, -1);
+
+  /* Bogus but understandable: truncated TTL */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, "\x11\x12\x13\x14\x15", 5);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET);
+  tt_str_op(fmt_addr(&addr), ==, "17.18.19.20");
+  tt_int_op(ttl, ==, -1);
+
+  /* Regular IPv4 one: address and TTL */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x02\x03\x04\x05\x00\x00\x0e\x10", 8);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET);
+  tt_str_op(fmt_addr(&addr), ==, "2.3.4.5");
+  tt_int_op(ttl, ==, 3600);
+
+  /* IPv4 with too-big TTL */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x02\x03\x04\x05\xf0\x00\x00\x00", 8);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET);
+  tt_str_op(fmt_addr(&addr), ==, "2.3.4.5");
+  tt_int_op(ttl, ==, -1);
+
+  /* IPv6 (ttl is mandatory) */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x00\x00\x00\x00\x06"
+                  "\x26\x07\xf8\xb0\x40\x0c\x0c\x02"
+                  "\x00\x00\x00\x00\x00\x00\x00\x68"
+                  "\x00\x00\x02\x58", 25);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET6);
+  tt_str_op(fmt_addr(&addr), ==, "2607:f8b0:400c:c02::68");
+  tt_int_op(ttl, ==, 600);
+
+  /* IPv6 (ttl too big) */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x00\x00\x00\x00\x06"
+                  "\x26\x07\xf8\xb0\x40\x0c\x0c\x02"
+                  "\x00\x00\x00\x00\x00\x00\x00\x68"
+                  "\x90\x00\x02\x58", 25);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET6);
+  tt_str_op(fmt_addr(&addr), ==, "2607:f8b0:400c:c02::68");
+  tt_int_op(ttl, ==, -1);
+
+  /* Bogus size: 3. */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x00\x01\x02", 3);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, -1);
+
+  /* Bogus family: 7. */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x00\x00\x00\x00\x07"
+                  "\x26\x07\xf8\xb0\x40\x0c\x0c\x02"
+                  "\x00\x00\x00\x00\x00\x00\x00\x68"
+                  "\x90\x00\x02\x58", 25);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, -1);
+
+  /* Truncated IPv6. */
+  make_relay_cell(&cell, RELAY_COMMAND_CONNECTED,
+                  "\x00\x00\x00\x00\x06"
+                  "\x26\x07\xf8\xb0\x40\x0c\x0c\x02"
+                  "\x00\x00\x00\x00\x00\x00\x00\x68"
+                  "\x00\x00\x02", 24);
+  relay_header_unpack(&rh, cell.payload);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, -1);
+
+  /* Now make sure we can generate connected cells correctly. */
+  /* Try an IPv4 address */
+  memset(&rh, 0, sizeof(rh));
+  memset(&cell, 0, sizeof(cell));
+  tor_addr_parse(&addr, "30.40.50.60");
+  rh.length = connected_cell_format_payload(cell.payload+RELAY_HEADER_SIZE,
+                                            &addr, 128);
+  tt_int_op(rh.length, ==, 8);
+  test_memeq_hex(cell.payload+RELAY_HEADER_SIZE, "1e28323c" "00000080");
+
+  /* Try parsing it. */
+  tor_addr_make_unspec(&addr);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET);
+  tt_str_op(fmt_addr(&addr), ==, "30.40.50.60");
+  tt_int_op(ttl, ==, 128);
+
+  /* Try an IPv6 address */
+  memset(&rh, 0, sizeof(rh));
+  memset(&cell, 0, sizeof(cell));
+  tor_addr_parse(&addr, "2620::6b0:b:1a1a:0:26e5:480e");
+  rh.length = connected_cell_format_payload(cell.payload+RELAY_HEADER_SIZE,
+                                            &addr, 3600);
+  tt_int_op(rh.length, ==, 25);
+  test_memeq_hex(cell.payload + RELAY_HEADER_SIZE,
+                 "00000000" "06"
+                 "2620000006b0000b1a1a000026e5480e" "00000e10");
+
+  /* Try parsing it. */
+  tor_addr_make_unspec(&addr);
+  r = connected_cell_parse(&rh, &cell, &addr, &ttl);
+  tt_int_op(r, ==, 0);
+  tt_int_op(tor_addr_family(&addr), ==, AF_INET6);
+  tt_str_op(fmt_addr(&addr), ==, "2620:0:6b0:b:1a1a:0:26e5:480e");
+  tt_int_op(ttl, ==, 3600);
+
+ done:
+  tor_free(mem_op_hex_tmp);
+}
+
+#define TEST(name, flags)                                               \
+  { #name, test_cfmt_ ## name, flags, 0, NULL }
+
+struct testcase_t cell_format_tests[] = {
+  TEST(relay_header, 0),
+  TEST(begin_cells, 0),
+  TEST(connected_cells, 0),
+  END_OF_TESTCASES
+};
+

+ 1 - 0
src/test/test_config.c

@@ -5,6 +5,7 @@
 
 #include "orconfig.h"
 #include "or.h"
+#include "addressmap.h"
 #include "config.h"
 #include "confparse.h"
 #include "connection_edge.h"

+ 27 - 17
src/tools/tor-resolve.c

@@ -74,23 +74,29 @@ build_socks_resolve_request(char **out,
     memcpy((*out)+8+strlen(username)+1, hostname, strlen(hostname)+1);
   } else if (version == 5) {
     int is_ip_address;
-    struct in_addr in;
+    tor_addr_t addr;
     size_t addrlen;
-    is_ip_address = tor_inet_aton(hostname, &in);
+    int ipv6;
+    is_ip_address = tor_addr_parse(&addr, hostname) != -1;
     if (!is_ip_address && reverse) {
       log_err(LD_GENERAL, "Tried to do a reverse lookup on a non-IP!");
       return -1;
     }
-    addrlen = reverse ? 4 : 1 + strlen(hostname);
+    ipv6 = reverse && tor_addr_family(&addr) == AF_INET6;
+    addrlen = reverse ? (ipv6 ? 16 : 4) : 1 + strlen(hostname);
     len = 6 + addrlen;
     *out = tor_malloc(len);
     (*out)[0] = 5; /* SOCKS version 5 */
     (*out)[1] = reverse ? '\xF1' : '\xF0'; /* RESOLVE_PTR or RESOLVE */
     (*out)[2] = 0; /* reserved. */
-    (*out)[3] = reverse ? 1 : 3;
     if (reverse) {
-      set_uint32((*out)+4, in.s_addr);
+      (*out)[3] = ipv6 ? 4 : 1;
+      if (ipv6)
+        memcpy((*out)+4, tor_addr_to_in6_addr8(&addr), 16);
+      else
+        set_uint32((*out)+4, tor_addr_to_ipv4n(&addr));
     } else {
+      (*out)[3] = 3;
       (*out)[4] = (char)(uint8_t)(addrlen - 1);
       memcpy((*out)+5, hostname, addrlen - 1);
     }
@@ -109,7 +115,7 @@ build_socks_resolve_request(char **out,
 static int
 parse_socks4a_resolve_response(const char *hostname,
                                const char *response, size_t len,
-                               uint32_t *addr_out)
+                               tor_addr_t *addr_out)
 {
   uint8_t status;
   tor_assert(response);
@@ -140,7 +146,7 @@ parse_socks4a_resolve_response(const char *hostname,
     return -1;
   }
 
-  *addr_out = ntohl(get_uint32(response+4));
+  tor_addr_from_ipv4n(addr_out, get_uint32(response+4));
   return 0;
 }
 
@@ -179,7 +185,7 @@ socks5_reason_to_string(char reason)
 static int
 do_resolve(const char *hostname, uint32_t sockshost, uint16_t socksport,
            int reverse, int version,
-           uint32_t *result_addr, char **result_hostname)
+           tor_addr_t *result_addr, char **result_hostname)
 {
   int s;
   struct sockaddr_in socksaddr;
@@ -190,7 +196,7 @@ do_resolve(const char *hostname, uint32_t sockshost, uint16_t socksport,
   tor_assert(result_addr);
   tor_assert(version == 4 || version == 5);
 
-  *result_addr = 0;
+  tor_addr_make_unspec(result_addr);
   *result_hostname = NULL;
 
   s = tor_open_socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
@@ -255,7 +261,7 @@ do_resolve(const char *hostname, uint32_t sockshost, uint16_t socksport,
       return -1;
     }
   } else {
-    char reply_buf[4];
+    char reply_buf[16];
     if (read_all(s, reply_buf, 4, 1) != 4) {
       log_err(LD_NET, "Error reading SOCKS5 response.");
       return -1;
@@ -284,8 +290,16 @@ do_resolve(const char *hostname, uint32_t sockshost, uint16_t socksport,
         log_err(LD_NET, "Error reading address in socks5 response.");
         return -1;
       }
-      *result_addr = ntohl(get_uint32(reply_buf));
+      tor_addr_from_ipv4n(result_addr, get_uint32(reply_buf));
+    } else if (reply_buf[3] == 4) {
+      /* IPv6 address */
+      if (read_all(s, reply_buf, 16, 1) != 16) {
+        log_err(LD_NET, "Error reading address in socks5 response.");
+        return -1;
+      }
+      tor_addr_from_ipv6_bytes(result_addr, reply_buf);
     } else if (reply_buf[3] == 3) {
+      /* Domain name */
       size_t result_len;
       if (read_all(s, reply_buf, 1, 1) != 1) {
         log_err(LD_NET, "Error reading address_length in socks5 response.");
@@ -322,10 +336,8 @@ main(int argc, char **argv)
   int isSocks4 = 0, isVerbose = 0, isReverse = 0;
   char **arg;
   int n_args;
-  struct in_addr a;
-  uint32_t result = 0;
+  tor_addr_t result;
   char *result_hostname = NULL;
-  char buf[INET_NTOA_BUF_LEN];
   log_severity_list_t *s = tor_malloc_zero(sizeof(log_severity_list_t));
 
   init_logging();
@@ -423,9 +435,7 @@ main(int argc, char **argv)
   if (result_hostname) {
     printf("%s\n", result_hostname);
   } else {
-    a.s_addr = htonl(result);
-    tor_inet_ntoa(&a, buf, sizeof(buf));
-    printf("%s\n", buf);
+    printf("%s\n", fmt_addr(&result));
   }
   return 0;
 }

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.