浏览代码

Merge remote-tracking branch 'teor/bug16069-bug17027'

Nick Mathewson 8 年之前
父节点
当前提交
b257e34583

+ 20 - 0
changes/bug16069-exit-policy-rule6

@@ -0,0 +1,20 @@
+  o Minor bug fixes (torrc exit policies):
+    - accept6/reject6 * lines only produce IPv6 wildcard addresses,
+      previously they would produce both IPv4 and IPv6 wildcard addresses.
+    - When parsing torrc ExitPolicies, we now warn if:
+      * an IPv4 address is used on an accept6 or reject6 line. The line is
+        ignored, but the rest of the policy items in the list are used.
+        (accept/reject continue to allow both IPv4 and IPv6 addresses in
+        torrcs.)
+      * a "private" address alias is used on an accept6 or reject6 line.
+        The line filters both IPv4 and IPv6 private addresses, disregarding
+        the 6 in accept6/reject6.
+      * any ExitPolicy lines occur after accept/reject *:* or variants.
+        These are redundant, and were always ignored.
+    - When parsing torrc ExitPolicies, we now issue an info-level message:
+      * when expanding an accept/reject * line to include both IPv4 and IPv6
+        wildcard addresses.
+    - In each instance, usage advice is provided to avoid the message.
+      Resolves ticket 16069. Patch by "teor".
+      Patch on 2eb7eafc9d78 and a96c0affcb4c (25 Oct 2012),
+      released in 0.2.4.7-alpha.

+ 6 - 0
changes/bug17027-reject-private-all-interfaces

@@ -0,0 +1,6 @@
+  o Minor bug fixes (security, exit policies):
+    - ExitPolicyRejectPrivate rejects more private addresses by default:
+      * the relay's published IPv6 address (if any), and
+      * any publicly routable IPv4 or IPv6 addresses on any local interfaces.
+      Resolves ticket 17027. Patch by "teor".
+      Patch on 42b8fb5a1523 (11 Nov 2007), released in 0.2.0.11-alpha.

+ 27 - 12
doc/tor.1.txt

@@ -1537,12 +1537,26 @@ is non-zero):
     "\*". +
  +
     For example, "accept 18.7.22.69:\*,reject 18.0.0.0/8:\*,accept \*:\*" would
-    reject any traffic destined for MIT except for web.mit.edu, and accept
-    anything else. +
+    reject any IPv4 traffic destined for MIT except for web.mit.edu, and accept
+    any other IPv4 or IPv6 traffic. +
  +
-    To specify all internal and link-local networks (including 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, and
-    172.16.0.0/12), you can use the "private" alias instead of an address.
+    Tor also allows IPv6 exit policy entries. For instance, "reject6 [FC00::]/7:*"
+    rejects all destinations that share 7 most significant bit prefix with
+    address FC00::. Respectively, "accept6 [C000::]/3:*" accepts all destinations
+    that share 3 most significant bit prefix with address C000::. +
+ +
+    accept6 and reject6 only produce IPv6 exit policy entries. Using an IPv4
+    address with accept6 or reject6 is ignored and generates a warning.
+    accept/reject allows either IPv4 or IPv6 addresses. Use *4 as an IPv4
+    wildcard address, and *6 as an IPv6 wildcard address. accept/reject *
+    expands to matching IPv4 and IPv6 wildcard address rules. +
+ +
+    To specify all IPv4 and IPv6 internal and link-local networks (including
+    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, [::]/8, [FC00::]/7, [FE80::]/10, [FEC0::]/10, [FF00::]/8,
+    and [::]/127), you can use the "private" alias instead of an address.
+    ("private" always produces rules for IPv4 and IPv6 addresses, even when
+    used with accept6/reject6.)
     These addresses are rejected by default (at the beginning of your exit
     policy), along with your public IP address, unless you set the
     ExitPolicyRejectPrivate config option to 0. For example, once you've done
@@ -1551,11 +1565,6 @@ is non-zero):
     may also allow connections to your own computer that are addressed to its
     public (external) IP address. See RFC 1918 and RFC 3330 for more details
     about internal and reserved IP address space. +
- +
-    Tor also allow IPv6 exit policy entries. For instance, "reject6 [FC00::]/7:*"
-    rejects all destinations that share 7 most significant bit prefix with 
-    address FC00::. Respectively, "accept6 [C000::]/3:*" accepts all destinations
-    that share 3 most significant bit prefix with address C000::. +
  +
     This directive can be specified multiple times so you don't have to put it
     all on one line. +
@@ -1577,9 +1586,15 @@ is non-zero):
        reject *:6881-6999
        accept *:*
 
+    Since the default exit policy uses accept/reject *, it applies to both
+    IPv4 and IPv6 addresses.
+
 [[ExitPolicyRejectPrivate]] **ExitPolicyRejectPrivate** **0**|**1**::
-    Reject all private (local) networks, along with your own public IP address,
-    at the beginning of your exit policy. See above entry on ExitPolicy.
+    Reject all private (local) networks, along with your own configured public
+    IPv4 and IPv6 addresses, at the beginning of your exit policy. Also reject
+    any public IPv4 and IPv6 addresses on any interface on the relay. (If
+    IPv6Exit is not set, all IPv6 addresses will be rejected anyway.)
+    See above entry on ExitPolicy.
     (Default: 1)
 
 [[IPv6Exit]] **IPv6Exit** **0**|**1**::

+ 119 - 27
src/common/address.c

@@ -620,13 +620,20 @@ tor_addr_to_PTR_name(char *out, size_t outlen,
  *  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
+ *  yields an AF_UNSPEC wildcard address, which expands to corresponding
+ *  wildcard IPv4 and IPv6 rules, 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.
  *
- */
+ *  If 'flags & TAPMP_EXTENDED_STAR' and 'flags & TAPMP_STAR_IPV4_ONLY' are
+ *  both true, then the wildcard address '*' yields an IPv4 wildcard.
+ *
+ *  If 'flags & TAPMP_EXTENDED_STAR' and 'flags & TAPMP_STAR_IPV6_ONLY' are
+ *  both true, then the wildcard address '*' yields an IPv6 wildcard.
+ *
+ * TAPMP_STAR_IPV4_ONLY and TAPMP_STAR_IPV6_ONLY are mutually exclusive. */
 int
 tor_addr_parse_mask_ports(const char *s,
                           unsigned flags,
@@ -643,6 +650,10 @@ tor_addr_parse_mask_ports(const char *s,
 
   tor_assert(s);
   tor_assert(addr_out);
+  /* We can either only want an IPv4 address or only want an IPv6 address,
+   * but we can't only want IPv4 & IPv6 at the same time. */
+  tor_assert(!((flags & TAPMP_STAR_IPV4_ONLY)
+               && (flags & TAPMP_STAR_IPV6_ONLY)));
 
   /** Longest possible length for an address, mask, and port-range combination.
    * Includes IP, [], /mask, :, ports */
@@ -688,8 +699,21 @@ tor_addr_parse_mask_ports(const char *s,
 
   if (!strcmp(address, "*")) {
     if (flags & TAPMP_EXTENDED_STAR) {
-      family = AF_UNSPEC;
-      tor_addr_make_unspec(addr_out);
+      if (flags & TAPMP_STAR_IPV4_ONLY) {
+        family = AF_INET;
+        tor_addr_from_ipv4h(addr_out, 0);
+      } else if (flags & TAPMP_STAR_IPV6_ONLY) {
+        static char nil_bytes[16] = { [0]=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);
+      } else {
+        family = AF_UNSPEC;
+        tor_addr_make_unspec(addr_out);
+        log_info(LD_GENERAL,
+                 "'%s' expands into rules which apply to all IPv4 and IPv6 "
+                 "addresses. (Use accept/reject *4:* for IPv4 or "
+                 "accept[6]/reject[6] *6:* for IPv6.)", s);
+      }
     } else {
       family = AF_INET;
       tor_addr_from_ipv4h(addr_out, 0);
@@ -1503,7 +1527,7 @@ get_interface_addresses_raw(int severity)
 }
 
 /** Return true iff <b>a</b> is a multicast address.  */
-static int
+STATIC int
 tor_addr_is_multicast(const tor_addr_t *a)
 {
   sa_family_t family = tor_addr_family(a);
@@ -1593,43 +1617,108 @@ get_interface_address6_via_udp_socket_hack(int severity,
   return r;
 }
 
-/** Set *<b>addr</b> to the IP address (if any) of whatever interface
- * connects to the Internet.  This address should only be used in checking
- * whether our address has changed.  Return 0 on success, -1 on failure.
+/** Set *<b>addr</b> to an arbitrary IP address (if any) of an interface that
+ * connects to the Internet.  Prefer public IP addresses to internal IP
+ * addresses.  This address should only be used in checking whether our
+ * address has changed, as it may be an internal IP address.  Return 0 on
+ * success, -1 on failure.
+ * Prefer get_interface_address6_list for a list of all addresses on all
+ * interfaces which connect to the Internet.
  */
 MOCK_IMPL(int,
 get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
 {
-  /* XXX really, this function should yield a smartlist of addresses. */
   smartlist_t *addrs;
+  int rv = -1;
   tor_assert(addr);
 
+  /* Get a list of public or internal IPs in arbitrary order */
+  addrs = get_interface_address6_list(severity, family, 1);
+
+  /* Find the first non-internal address, or the last internal address
+   * Ideally, we want the default route, see #12377 for details */
+  SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
+    tor_addr_copy(addr, a);
+    rv = 0;
+
+    /* If we found a non-internal address, declare success.  Otherwise,
+     * keep looking. */
+    if (!tor_addr_is_internal(a, 0))
+      break;
+  } SMARTLIST_FOREACH_END(a);
+
+  free_interface_address6_list(addrs);
+  return rv;
+}
+
+/** Free a smartlist of IP addresses returned by get_interface_address6_list.
+ */
+void
+free_interface_address6_list(smartlist_t *addrs)
+{
+  SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
+  smartlist_free(addrs);
+}
+
+/** Return a smartlist of the IP addresses of type family from all interfaces
+ * on the server. Excludes loopback and multicast addresses. Only includes
+ * internal addresses if include_internal is true. (Note that a relay behind
+ * NAT may use an internal address to connect to the Internet.)
+ * An empty smartlist means that there are no addresses of the selected type
+ * matching these criteria.
+ * Returns NULL on failure.
+ * Use free_interface_address6_list to free the returned list.
+ */
+MOCK_IMPL(smartlist_t *,get_interface_address6_list,(int severity,
+                                                     sa_family_t family,
+                                                     int include_internal))
+{
+  smartlist_t *addrs;
+  tor_addr_t addr;
+
   /* Try to do this the smart way if possible. */
   if ((addrs = get_interface_addresses_raw(severity))) {
-    int rv = -1;
-    SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
-      if (family != AF_UNSPEC && family != tor_addr_family(a))
+    SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a)
+    {
+      if (family != AF_UNSPEC && family != tor_addr_family(a)) {
+        SMARTLIST_DEL_CURRENT(addrs, a);
+        tor_free(a);
         continue;
+      }
+
       if (tor_addr_is_loopback(a) ||
-          tor_addr_is_multicast(a))
+          tor_addr_is_multicast(a)) {
+        SMARTLIST_DEL_CURRENT(addrs, a);
+        tor_free(a);
         continue;
+      }
 
-      tor_addr_copy(addr, a);
-      rv = 0;
-
-      /* If we found a non-internal address, declare success.  Otherwise,
-       * keep looking. */
-      if (!tor_addr_is_internal(a, 0))
-        break;
+      if (!include_internal && tor_addr_is_internal(a, 0)) {
+        SMARTLIST_DEL_CURRENT(addrs, a);
+        tor_free(a);
+        continue;
+      }
     } SMARTLIST_FOREACH_END(a);
+  }
+
+  if (addrs && smartlist_len(addrs) > 0) {
+    return addrs;
+  }
 
-    SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
+  /* if we removed all entries as unsuitable */
+  if (addrs) {
     smartlist_free(addrs);
-    return rv;
   }
 
   /* Okay, the smart way is out. */
-  return get_interface_address6_via_udp_socket_hack(severity,family,addr);
+  get_interface_address6_via_udp_socket_hack(severity,family,&addr);
+  if (!include_internal && tor_addr_is_internal(&addr, 0)) {
+    return smartlist_new();
+  } else {
+    addrs = smartlist_new();
+    smartlist_add(addrs, tor_dup_addr(&addr));
+    return addrs;
+  }
 }
 
 /* ======
@@ -1871,10 +1960,13 @@ tor_dup_ip(uint32_t addr)
 }
 
 /**
- * Set *<b>addr</b> to the host-order IPv4 address (if any) of whatever
- * interface connects to the Internet.  This address should only be used in
- * checking whether our address has changed.  Return 0 on success, -1 on
- * failure.
+ * Set *<b>addr</b> to a host-order IPv4 address (if any) of an
+ * interface that connects to the Internet.  Prefer public IP addresses to
+ * internal IP addresses.  This address should only be used in checking
+ * whether our address has changed, as it may be an internal IPv4 address.
+ * Return 0 on success, -1 on failure.
+ * Prefer get_interface_address_list6 for a list of all IPv4 and IPv6
+ * addresses on all interfaces which connect to the Internet.
  */
 MOCK_IMPL(int,
 get_interface_address,(int severity, uint32_t *addr))

+ 39 - 1
src/common/address.h

@@ -15,6 +15,7 @@
 #include "orconfig.h"
 #include "torint.h"
 #include "compat.h"
+#include "container.h"
 
 #ifdef ADDRESS_PRIVATE
 
@@ -43,7 +44,6 @@
 #endif
 
 // TODO win32 specific includes
-#include "container.h"
 #endif // ADDRESS_PRIVATE
 
 /** The number of bits from an address to consider while doing a masked
@@ -190,8 +190,13 @@ char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC;
 const char *fmt_addr_impl(const tor_addr_t *addr, int decorate);
 const char *fmt_addrport(const tor_addr_t *addr, uint16_t port);
 const char * fmt_addr32(uint32_t addr);
+
 MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family,
 tor_addr_t *addr));
+void free_interface_address6_list(smartlist_t * addrs);
+MOCK_DECL(smartlist_t *,get_interface_address6_list,(int severity,
+                                                     sa_family_t family,
+                                                     int include_internal));
 
 /** Flag to specify how to do a comparison between addresses.  In an "exact"
  * comparison, addresses are equivalent only if they are in the same family
@@ -227,7 +232,19 @@ 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);
+
+/* Does the address * yield an AF_UNSPEC wildcard address (1),
+ * which expands to corresponding wildcard IPv4 and IPv6 rules, and do we
+ * allow *4 and *6 for IPv4 and IPv6 wildcards, respectively;
+ * or does the address * yield IPv4 wildcard address (0).  */
 #define TAPMP_EXTENDED_STAR 1
+/* Does the address * yield an IPv4 wildcard address rule (1);
+ * or does it yield wildcard IPv4 and IPv6 rules (0) */
+#define TAPMP_STAR_IPV4_ONLY     (1 << 1)
+/* Does the address * yield an IPv6 wildcard address rule (1);
+ * or does it yield wildcard IPv4 and IPv6 rules (0) */
+#define TAPMP_STAR_IPV6_ONLY     (1 << 2)
+/* TAPMP_STAR_IPV4_ONLY and TAPMP_STAR_IPV6_ONLY are mutually exclusive. */
 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);
@@ -269,11 +286,32 @@ int addr_mask_get_bits(uint32_t mask);
 int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len);
 char *tor_dup_ip(uint32_t addr) ATTR_MALLOC;
 MOCK_DECL(int,get_interface_address,(int severity, uint32_t *addr));
+/** Free a smartlist of IP addresses returned by get_interface_address_list.
+ */
+static INLINE void
+free_interface_address_list(smartlist_t *addrs)
+{
+  free_interface_address6_list(addrs);
+}
+/** Return a smartlist of the IPv4 addresses of all interfaces on the server.
+ * Excludes loopback and multicast addresses. Only includes internal addresses
+ * if include_internal is true. (Note that a relay behind NAT may use an
+ * internal address to connect to the Internet.)
+ * An empty smartlist means that there are no IPv4 addresses.
+ * Returns NULL on failure.
+ * Use free_interface_address_list to free the returned list.
+ */
+static INLINE smartlist_t *
+get_interface_address_list(int severity, int include_internal)
+{
+  return get_interface_address6_list(severity, AF_INET, include_internal);
+}
 
 tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port);
 
 #ifdef ADDRESS_PRIVATE
 STATIC smartlist_t *get_interface_addresses_raw(int severity);
+STATIC int tor_addr_is_multicast(const tor_addr_t *a);
 STATIC int get_interface_address6_via_udp_socket_hack(int severity,
                                                       sa_family_t family,
                                                       tor_addr_t *addr);

+ 10 - 5
src/config/torrc.minimal.in-staging

@@ -1,5 +1,5 @@
 ## Configuration file for a typical Tor user
-## Last updated 2 September 2014 for Tor 0.2.6.1-alpha.
+## Last updated 15 September 2015 for Tor 0.2.7.3-alpha.
 ## (may or may not work for much older or much newer versions of Tor.)
 ##
 ## Lines that begin with "## " try to explain what's going on. Lines
@@ -24,6 +24,7 @@
 ## can access your SocksPort may be able to learn about the connections
 ## you make.
 #SocksPolicy accept 192.168.0.0/16
+#SocksPolicy accept6 FC00::/7
 #SocksPolicy reject *
 
 ## Logs go to stdout at level "notice" unless redirected by something
@@ -171,11 +172,15 @@
 ## users will be told that those destinations are down.
 ##
 ## For security, by default Tor rejects connections to private (local)
-## networks, including to your public IP address. See the man page entry
-## for ExitPolicyRejectPrivate if you want to allow "exit enclaving".
+## networks, including to the configured public IPv4 and IPv6 addresses,
+## and any public IPv4 and IPv6 addresses on any interface on the relay.
+## See the man page entry for ExitPolicyRejectPrivate if you want to allow
+## "exit enclaving".
 ##
-#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports but no more
-#ExitPolicy accept *:119 # accept nntp as well as default exit policy
+#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports on IPv4 and IPv6 but no more
+#ExitPolicy accept *:119 # accept nntp ports on IPv4 and IPv6 as well as default exit policy
+#ExitPolicy accept *4:119 # accept nntp ports on IPv4 only as well as default exit policy
+#ExitPolicy accept6 *6:119 # accept nntp ports on IPv6 only as well as default exit policy
 #ExitPolicy reject *:* # no exits allowed
 
 ## Bridge relays (or "bridges") are Tor relays that aren't listed in the

+ 10 - 5
src/config/torrc.sample.in

@@ -1,5 +1,5 @@
 ## Configuration file for a typical Tor user
-## Last updated 2 September 2014 for Tor 0.2.6.1-alpha.
+## Last updated 15 September 2015 for Tor 0.2.7.3-alpha.
 ## (may or may not work for much older or much newer versions of Tor.)
 ##
 ## Lines that begin with "## " try to explain what's going on. Lines
@@ -24,6 +24,7 @@
 ## can access your SocksPort may be able to learn about the connections
 ## you make.
 #SocksPolicy accept 192.168.0.0/16
+#SocksPolicy accept6 FC00::/7
 #SocksPolicy reject *
 
 ## Logs go to stdout at level "notice" unless redirected by something
@@ -171,11 +172,15 @@
 ## users will be told that those destinations are down.
 ##
 ## For security, by default Tor rejects connections to private (local)
-## networks, including to your public IP address. See the man page entry
-## for ExitPolicyRejectPrivate if you want to allow "exit enclaving".
+## networks, including to the configured public IPv4 and IPv6 addresses,
+## and any public IPv4 and IPv6 addresses on any interface on the relay.
+## See the man page entry for ExitPolicyRejectPrivate if you want to allow
+## "exit enclaving".
 ##
-#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports but no more
-#ExitPolicy accept *:119 # accept nntp as well as default exit policy
+#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports on IPv4 and IPv6 but no more
+#ExitPolicy accept *:119 # accept nntp ports on IPv4 and IPv6 as well as default exit policy
+#ExitPolicy accept *4:119 # accept nntp ports on IPv4 only as well as default exit policy
+#ExitPolicy accept6 *6:119 # accept nntp ports on IPv6 only as well as default exit policy
 #ExitPolicy reject *:* # no exits allowed
 
 ## Bridge relays (or "bridges") are Tor relays that aren't listed in the

+ 190 - 22
src/or/policies.c

@@ -67,6 +67,8 @@ static int policies_parse_exit_policy_internal(config_line_t *cfg,
                                                int ipv6_exit,
                                                int rejectprivate,
                                                uint32_t local_address,
+                                               tor_addr_t *ipv6_local_address,
+                                               int reject_interface_addresses,
                                                int add_default_policy);
 
 /** Replace all "private" entries in *<b>policy</b> with their expanded
@@ -152,7 +154,7 @@ policy_expand_unspec(smartlist_t **policy)
 }
 
 /**
- * Given a linked list of config lines containing "allow" and "deny"
+ * Given a linked list of config lines containing "accept[6]" and "reject[6]"
  * tokens, parse them and append the result to <b>dest</b>. Return -1
  * if any tokens are malformed (and don't append any), else return 0.
  *
@@ -167,6 +169,7 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
   smartlist_t *result;
   smartlist_t *entries;
   addr_policy_t *item;
+  int malformed_list;
   int r = 0;
 
   if (!cfg)
@@ -179,12 +182,22 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
                            SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
     SMARTLIST_FOREACH_BEGIN(entries, const char *, ent) {
       log_debug(LD_CONFIG,"Adding new entry '%s'",ent);
-      item = router_parse_addr_policy_item_from_string(ent, assume_action);
+      malformed_list = 0;
+      item = router_parse_addr_policy_item_from_string(ent, assume_action,
+                                                       &malformed_list);
       if (item) {
         smartlist_add(result, item);
-      } else {
-        log_warn(LD_CONFIG,"Malformed policy '%s'.", ent);
+      } else if (malformed_list) {
+        /* the error is so severe the entire list should be discarded */
+        log_warn(LD_CONFIG, "Malformed policy '%s'. Discarding entire policy "
+                 "list.", ent);
         r = -1;
+      } else {
+        /* the error is minor: don't add the item, but keep processing the
+         * rest of the policies in the list */
+        log_debug(LD_CONFIG, "Ignored policy '%s' due to non-fatal error. "
+                  "The remainder of the policy list will be used.",
+                  ent);
       }
     } SMARTLIST_FOREACH_END(ent);
     SMARTLIST_FOREACH(entries, char *, ent, tor_free(ent));
@@ -430,7 +443,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
   smartlist_t *addr_policy=NULL;
   *msg = NULL;
 
-  if (policies_parse_exit_policy_from_options(options,0,&addr_policy)) {
+  if (policies_parse_exit_policy_from_options(options,0,NULL,0,&addr_policy)) {
     REJECT("Error in ExitPolicy entry.");
   }
 
@@ -568,6 +581,8 @@ cmp_single_addr_policy(addr_policy_t *a, addr_policy_t *b)
     return r;
   if ((r=((int)a->is_private - (int)b->is_private)))
     return r;
+  /* refcnt and is_canonical are irrelevant to equality,
+   * they are hash table implementation details */
   if ((r=tor_addr_compare(&a->addr, &b->addr, CMP_EXACT)))
     return r;
   if ((r=((int)a->maskbits - (int)b->maskbits)))
@@ -969,12 +984,24 @@ exit_policy_remove_redundancies(smartlist_t *dest)
   "reject *:563,reject *:1214,reject *:4661-4666,"                  \
   "reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*"
 
-/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>. If
- * cfg doesn't end in an absolute accept or reject and if
+/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>.
+ *
+ * If <b>ipv6_exit</b> is true, prepend "reject *6:*" to the policy.
+ *
+ * If <b>rejectprivate</b> is true:
+ *   - prepend "reject private:*" to the policy.
+ *   - if local_address is non-zero, treat it as a host-order IPv4 address,
+ *     and prepend an entry that rejects it as a destination.
+ *   - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
+ *     a destination.
+ *   - if reject_interface_addresses is true, prepend entries that reject each
+ *     public IPv4 and IPv6 address of each interface on this machine.
+ *
+ * If cfg doesn't end in an absolute accept or reject and if
  * <b>add_default_policy</b> is true, add the default exit
- * 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.
+ * policy afterwards.
+ *
+ * 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,
@@ -985,21 +1012,142 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
                                     int ipv6_exit,
                                     int rejectprivate,
                                     uint32_t local_address,
+                                    tor_addr_t *ipv6_local_address,
+                                    int reject_interface_addresses,
                                     int add_default_policy)
 {
   if (!ipv6_exit) {
     append_exit_policy_string(dest, "reject *6:*");
   }
   if (rejectprivate) {
+    /* Reject IPv4 and IPv6 reserved private netblocks */
     append_exit_policy_string(dest, "reject private:*");
+    /* Reject our local IPv4 address */
     if (local_address) {
       char buf[POLICY_BUF_LEN];
       tor_snprintf(buf, sizeof(buf), "reject %s:*", fmt_addr32(local_address));
       append_exit_policy_string(dest, buf);
+      log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our published "
+               "IPv4 address", buf);
+    }
+    /* Reject our local IPv6 address */
+    if (ipv6_exit && ipv6_local_address != NULL) {
+      if (tor_addr_is_v4(ipv6_local_address)) {
+        log_warn(LD_CONFIG, "IPv4 address '%s' provided as our IPv6 local "
+                 "address", fmt_addr(ipv6_local_address));
+      } else {
+        char buf6[POLICY_BUF_LEN];
+        tor_snprintf(buf6, sizeof(buf6), "reject %s:*",
+                     fmt_addr(ipv6_local_address));
+        append_exit_policy_string(dest, buf6);
+        log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our "
+                 "published IPv6 address", buf6);
+      }
+    }
+    /* Reject local addresses from public netblocks on any interface,
+     * but don't reject our published addresses twice */
+    if (reject_interface_addresses) {
+      smartlist_t *public_addresses = NULL;
+      char bufif[POLICY_BUF_LEN];
+
+      /* Reject public IPv4 addresses on any interface,
+       * but don't reject our published IPv4 address twice */
+      public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0);
+      SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
+        if (!tor_addr_eq_ipv4h(a, local_address)) {
+          tor_snprintf(bufif, sizeof(bufif), "reject %s:*",
+                       fmt_addr(a));
+          append_exit_policy_string(dest, bufif);
+          log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
+                   "interface's public IPv4 address", bufif);
+        }
+      } SMARTLIST_FOREACH_END(a);
+      free_interface_address6_list(public_addresses);
+
+      if (ipv6_exit) {
+        /* Reject public IPv6 addresses on any interface,
+         * but don't reject our published IPv6 address (if any) twice */
+        public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0);
+        SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
+          /* if we don't have an IPv6 local address, we won't have rejected
+           * it above. This could happen if a future release does IPv6
+           * autodiscovery, and we are waiting to discover our external IPv6
+           * address */
+          if (ipv6_local_address == NULL
+              || !tor_addr_eq(ipv6_local_address, a)) {
+            tor_snprintf(bufif, sizeof(bufif), "reject6 %s:*",
+                         fmt_addr(a));
+            append_exit_policy_string(dest, bufif);
+            log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
+                     "interface's public IPv6 address", bufif);
+          }
+        } SMARTLIST_FOREACH_END(a);
+        free_interface_address6_list(public_addresses);
+      }
     }
   }
   if (parse_addr_policy(cfg, dest, -1))
     return -1;
+
+  /* Before we add the default policy and final rejects, check to see if
+   * there are any lines after accept *:* or reject *:*. These lines have no
+   * effect, and are most likely an error. */
+  int found_final_effective_entry = 0;
+  int first_redundant_entry = 0;
+  for (int i = 0; i < smartlist_len(*dest); ++i) {
+    sa_family_t family;
+    addr_policy_t *p;
+    int found_ipv4_wildcard = 0, found_ipv6_wildcard = 0;
+
+    p = smartlist_get(*dest, i);
+
+    /* Look for accept/reject *[4|6|]:* entires */
+    if (p->prt_min <= 1 && p->prt_max == 65535 && p->maskbits == 0) {
+      family = tor_addr_family(&p->addr);
+      /* accept/reject *:* may have already been expanded into
+       * accept/reject *4:*,accept/reject *6:*
+       * But handle both forms.
+       */
+      if (family == AF_INET || family == AF_UNSPEC) {
+        found_ipv4_wildcard = 1;
+      }
+      if (family == AF_INET6 || family == AF_UNSPEC) {
+        found_ipv6_wildcard = 1;
+      }
+    }
+
+    /* We also find accept *4:*,reject *6:* ; and
+     * accept *4:*,<other policies>,accept *6:* ; and similar.
+     * That's ok, because they make any subsequent entries redundant. */
+    if (found_ipv4_wildcard && found_ipv6_wildcard) {
+      found_final_effective_entry = 1;
+      /* if we're not on the final entry in the list */
+      if (i < smartlist_len(*dest) - 1) {
+        first_redundant_entry = i + 1;
+      }
+      break;
+    }
+  }
+  /* Work out if there are redundant trailing entries in the policy list */
+  if (found_final_effective_entry && first_redundant_entry > 0) {
+    addr_policy_t *p;
+    /* Longest possible policy is
+     * "accept6 ffff:ffff:..255/128:10000-65535",
+     * which contains a max-length IPv6 address, plus 24 characters. */
+    char line[TOR_ADDR_BUF_LEN + 32];
+
+    tor_assert(first_redundant_entry < smartlist_len(*dest));
+    p = smartlist_get(*dest, first_redundant_entry);
+    /* since we've already parsed the policy into an addr_policy_t struct,
+     * we might not log exactly what the user typed in */
+    policy_write_item(line, TOR_ADDR_BUF_LEN + 32, p, 0);
+    log_warn(LD_DIR, "Exit policy '%s' and all following policies are "
+             "redundant, as it follows accept/reject *:* rules for both "
+             "IPv4 and IPv6. They will be removed from the exit policy. (Use "
+             "accept/reject *:* as the last entry in any exit policy.)",
+             line);
+  }
+
   if (add_default_policy) {
     append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
   } else {
@@ -1013,20 +1161,28 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
 
 /** Parse exit policy in <b>cfg</b> into <b>dest</b> smartlist.
  *
- * Add entry that rejects all IPv6 destinations unless
+ * Prepend an entry that rejects all IPv6 destinations unless
  * <b>EXIT_POLICY_IPV6_ENABLED</b> bit is set in <b>options</b> bitmask.
  *
- * If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>,
- * do add entry that rejects all destinations in private subnetwork
- * Tor is running in.
+ * If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>:
+ *   - prepend an entry that rejects all destinations in all netblocks
+ *     reserved for private use.
+ *   - if local_address is non-zero, treat it as a host-order IPv4 address,
+ *     and prepend an entry that rejects it as a destination.
+ *   - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
+ *     a destination.
+ *   - if reject_interface_addresses is true, prepend entries that reject each
+ *     public IPv4 and IPv6 address of each interface on this machine.
  *
- * Respectively, if <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set, add
+ * If <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set in <b>options</b>, append
  * default exit policy entries to <b>result</b> smartlist.
  */
 int
 policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
                            exit_policy_parser_cfg_t options,
-                           uint32_t local_address)
+                           uint32_t local_address,
+                           tor_addr_t *ipv6_local_address,
+                           int reject_interface_addresses)
 {
   int ipv6_enabled = (options & EXIT_POLICY_IPV6_ENABLED) ? 1 : 0;
   int reject_private = (options & EXIT_POLICY_REJECT_PRIVATE) ? 1 : 0;
@@ -1035,19 +1191,27 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
   return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
                                              reject_private,
                                              local_address,
+                                             ipv6_local_address,
+                                             reject_interface_addresses,
                                              add_default);
 }
 
 /** Parse <b>ExitPolicy</b> member of <b>or_options</b> into <b>result</b>
  * smartlist.
- * If <b>or_options->IPv6Exit</b> is false, add an entry that
+ * If <b>or_options->IPv6Exit</b> is false, prepend an entry that
  * rejects all IPv6 destinations.
  *
- * If <b>or_options->ExitPolicyRejectPrivate</b> is true, add entry that
- * rejects all destinations in the private subnetwork of machine Tor
- * instance is running in.
+ * If <b>or_options->ExitPolicyRejectPrivate</b> is true:
+ *  - prepend an entry that rejects all destinations in all netblocks reserved
+ *    for private use.
+ *  - if local_address is non-zero, treat it as a host-order IPv4 address, and
+ *    prepend an entry that rejects it as a destination.
+ *  - if ipv6_local_address is non-NULL, prepend an entry that rejects it as a
+ *    destination.
+ *  - if reject_interface_addresses is true, prepend entries that reject each
+ *    public IPv4 and IPv6 address of each interface on this machine.
  *
- * If <b>or_options->BridgeRelay</b> is false, add entries of default
+ * If <b>or_options->BridgeRelay</b> is false, append entries of default
  * Tor exit policy into <b>result</b> smartlist.
  *
  * If or_options->ExitRelay is false, then make our exit policy into
@@ -1056,6 +1220,8 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
 int
 policies_parse_exit_policy_from_options(const or_options_t *or_options,
                                         uint32_t local_address,
+                                        tor_addr_t *ipv6_local_address,
+                                        int reject_interface_addresses,
                                         smartlist_t **result)
 {
   exit_policy_parser_cfg_t parser_cfg = 0;
@@ -1079,7 +1245,9 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
   }
 
   return policies_parse_exit_policy(or_options->ExitPolicy,result,
-                                    parser_cfg,local_address);
+                                    parser_cfg,local_address,
+                                    ipv6_local_address,
+                                    reject_interface_addresses);
 }
 
 /** Add "reject *:*" to the end of the policy in *<b>dest</b>, allocating

+ 5 - 7
src/or/policies.h

@@ -48,18 +48,16 @@ MOCK_DECL(addr_policy_result_t, compare_tor_addr_to_addr_policy,
 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, uint32_t local_address,
-                               int add_default_policy);
-*/
 int policies_parse_exit_policy_from_options(const or_options_t *or_options,
                                             uint32_t local_address,
+                                            tor_addr_t *ipv6_local_address,
+                                            int reject_interface_addresses,
                                             smartlist_t **result);
 int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
                                exit_policy_parser_cfg_t options,
-                               uint32_t local_address);
+                               uint32_t local_address,
+                               tor_addr_t *ipv6_local_address,
+                               int reject_interface_addresses);
 void policies_exit_policy_append_reject_star(smartlist_t **dest);
 void addr_policy_append_reject_addr(smartlist_t **dest,
                                     const tor_addr_t *addr);

+ 1 - 1
src/or/router.c

@@ -1922,7 +1922,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
     /* DNS is screwed up; don't claim to be an exit. */
     policies_exit_policy_append_reject_star(&ri->exit_policy);
   } else {
-    policies_parse_exit_policy_from_options(options,ri->addr,
+    policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,1,
                                             &ri->exit_policy);
   }
   ri->policy_is_reject_star =

+ 73 - 8
src/or/routerparse.c

@@ -3666,24 +3666,38 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
  * assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
  * ADDR_POLICY_REJECT) for items that specify no action.
  *
+ * Returns NULL on policy errors.
+ *
+ * If there is a policy error, malformed_list is set to true if the entire
+ * policy list should be discarded. Otherwise, it is set to false, and only
+ * this item should be ignored - the rest of the policy list can continue to
+ * be processed and used.
+ *
  * 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.
  */
 MOCK_IMPL(addr_policy_t *,
-router_parse_addr_policy_item_from_string,(const char *s, int assume_action))
+router_parse_addr_policy_item_from_string,(const char *s, int assume_action,
+                                           int *malformed_list))
 {
   directory_token_t *tok = NULL;
   const char *cp, *eos;
   /* Longest possible policy is
-   * "accept6 ffff:ffff:..255/ffff:...255:10000-65535",
-   * which contains 2 max-length IPv6 addresses, plus 21 characters.
+   * "accept6 ffff:ffff:..255/128:10000-65535",
+   * which contains a max-length IPv6 address, plus 24 characters.
    * But note that there can be an arbitrary amount of space between the
-   * accept and the address:mask/port element. */
+   * accept and the address:mask/port element.
+   * We don't need to multiply TOR_ADDR_BUF_LEN by 2, as there is only one
+   * IPv6 address. But making the buffer shorter might cause valid long lines,
+   * which parsed in previous versions, to fail to parse in new versions.
+   * (These lines would have to have excessive amounts of whitespace.) */
   char line[TOR_ADDR_BUF_LEN*2 + 32];
   addr_policy_t *r;
   memarea_t *area = NULL;
 
+  tor_assert(malformed_list);
+
   s = eat_whitespace(s);
   if ((*s == '*' || TOR_ISDIGIT(*s)) && assume_action >= 0) {
     if (tor_snprintf(line, sizeof(line), "%s %s",
@@ -3710,9 +3724,32 @@ router_parse_addr_policy_item_from_string,(const char *s, int assume_action))
     goto err;
   }
 
+  /* Use the extended interpretation of accept/reject *,
+   * expanding it into an IPv4 wildcard and an IPv6 wildcard.
+   * Also permit *4 and *6 for IPv4 and IPv6 only wildcards. */
   r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR);
+  if (!r) {
+    goto err;
+  }
+
+  /* Ensure that accept6/reject6 fields are followed by IPv6 addresses.
+   * AF_UNSPEC addresses are only permitted on the accept/reject field type.
+   * Unlike descriptors, torrcs exit policy accept/reject can be followed by
+   * either an IPv4 or IPv6 address. */
+  if ((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) &&
+       tor_addr_family(&r->addr) != AF_INET6) {
+    /* This is a non-fatal error, just ignore this one entry. */
+    *malformed_list = 0;
+    log_warn(LD_DIR, "IPv4 address '%s' with accept6/reject6 field type in "
+             "exit policy. Ignoring, but continuing to parse rules. (Use "
+             "accept/reject with IPv4 addresses.)",
+             tok->n_args == 1 ? tok->args[0] : "");
+    return NULL;
+  }
+
   goto done;
  err:
+  *malformed_list = 1;
   r = NULL;
  done:
   token_clear(tok);
@@ -3729,19 +3766,27 @@ static int
 router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
 {
   addr_policy_t *newe;
+  /* Use the standard interpretation of accept/reject *, an IPv4 wildcard. */
   newe = router_parse_addr_policy(tok, 0);
   if (!newe)
     return -1;
   if (! router->exit_policy)
     router->exit_policy = smartlist_new();
 
+  /* Ensure that in descriptors, accept/reject fields are followed by
+   * IPv4 addresses, and accept6/reject6 fields are followed by
+   * IPv6 addresses. Unlike torrcs, descriptor exit policies do not permit
+   * accept/reject followed by IPv6. */
   if (((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) &&
        tor_addr_family(&newe->addr) == AF_INET)
       ||
       ((tok->tp == K_ACCEPT || tok->tp == K_REJECT) &&
        tor_addr_family(&newe->addr) == AF_INET6)) {
+    /* There's nothing the user can do about other relays' descriptors,
+     * so we don't provide usage advice here. */
     log_warn(LD_DIR, "Mismatch between field type and address type in exit "
-             "policy");
+             "policy '%s'. Discarding entire router descriptor.",
+             tok->n_args == 1 ? tok->args[0] : "");
     addr_policy_free(newe);
     return -1;
   }
@@ -3751,8 +3796,11 @@ router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
   return 0;
 }
 
-/** Given a K_ACCEPT or K_REJECT token and a router, create and return
- * a new exit_policy_t corresponding to the token. */
+/** Given a K_ACCEPT[6] or K_REJECT[6] token and a router, create and return
+ * a new exit_policy_t corresponding to the token. If TAPMP_EXTENDED_STAR
+ * is set in fmt_flags, K_ACCEPT6 and K_REJECT6 tokens followed by *
+ * expand to IPv6-only policies, otherwise they expand to IPv4 and IPv6
+ * policies */
 static addr_policy_t *
 router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
 {
@@ -3776,6 +3824,13 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
   else
     newe.policy_type = ADDR_POLICY_ACCEPT;
 
+  /* accept6/reject6 * produces an IPv6 wildcard address only.
+   * (accept/reject * produces rules for IPv4 and IPv6 wildcard addresses.) */
+  if ((fmt_flags & TAPMP_EXTENDED_STAR)
+      && (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6)) {
+    fmt_flags |= TAPMP_STAR_IPV6_ONLY;
+  }
+
   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));
@@ -3785,9 +3840,12 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
   return addr_policy_get_canonical_entry(&newe);
 }
 
-/** Parse an exit policy line of the format "accept/reject private:...".
+/** Parse an exit policy line of the format "accept[6]/reject[6] private:...".
  * This didn't exist until Tor 0.1.1.15, so nobody should generate it in
  * router descriptors until earlier versions are obsolete.
+ *
+ * accept/reject and accept6/reject6 private all produce rules for both
+ * IPv4 and IPv6 addresses.
  */
 static addr_policy_t *
 router_parse_addr_policy_private(directory_token_t *tok)
@@ -3817,6 +3875,13 @@ router_parse_addr_policy_private(directory_token_t *tok)
   result.prt_min = port_min;
   result.prt_max = port_max;
 
+  if (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) {
+    log_warn(LD_GENERAL,
+             "'%s' expands into rules which apply to all private IPv4 and "
+             "IPv6 addresses. (Use accept/reject private:* for IPv4 and "
+             "IPv6.)", tok->n_args == 1 ? tok->args[0] : "");
+  }
+
   return addr_policy_get_canonical_entry(&result);
 }
 

+ 1 - 1
src/or/routerparse.h

@@ -41,7 +41,7 @@ extrainfo_t *extrainfo_parse_entry_from_string(const char *s, const char *end,
                              int cache_copy, struct digest_ri_map_t *routermap,
                              int *can_dl_again_out);
 MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
-    (const char *s, int assume_action));
+         (const char *s, int assume_action, int *malformed_list));
 version_status_t tor_version_is_obsolete(const char *myversion,
                                          const char *versionlist);
 int tor_version_as_new_as(const char *platform, const char *cutoff);

+ 13 - 4
src/or/routerset.c

@@ -85,10 +85,13 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
   int added_countries = 0;
   char *countryname;
   smartlist_t *list = smartlist_new();
+  int malformed_list;
   smartlist_split_string(list, s, ",",
                          SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK, 0);
   SMARTLIST_FOREACH_BEGIN(list, char *, nick) {
       addr_policy_t *p;
+      /* if it doesn't pass our validation, assume it's malformed */
+      malformed_list = 1;
       if (is_legal_hexdigest(nick)) {
         char d[DIGEST_LEN];
         if (*nick == '$')
@@ -106,15 +109,21 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
         added_countries = 1;
       } else if ((strchr(nick,'.') || strchr(nick, '*')) &&
                  (p = router_parse_addr_policy_item_from_string(
-                                     nick, ADDR_POLICY_REJECT))) {
+                                     nick, ADDR_POLICY_REJECT,
+                                     &malformed_list))) {
         log_debug(LD_CONFIG, "Adding address %s to %s", nick, description);
         smartlist_add(target->policies, p);
-      } else {
-        log_warn(LD_CONFIG, "Entry '%s' in %s is malformed.", nick,
-                 description);
+      } else if (malformed_list) {
+        log_warn(LD_CONFIG, "Entry '%s' in %s is malformed. Discarding entire"
+                 " list.", nick, description);
         r = -1;
         tor_free(nick);
         SMARTLIST_DEL_CURRENT(list, nick);
+      } else {
+        log_notice(LD_CONFIG, "Entry '%s' in %s is ignored. Using the"
+                   " remainder of the list.", nick, description);
+        tor_free(nick);
+        SMARTLIST_DEL_CURRENT(list, nick);
       }
   } SMARTLIST_FOREACH_END(nick);
   policy_expand_unspec(&target->policies);

+ 215 - 15
src/test/test_address.c

@@ -74,35 +74,79 @@ sockaddr_in_from_string(const char *ip_str, struct sockaddr_in *out)
 }
 
 /** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
- * that points to 127.0.0.1. Otherwise, return 0.
+ * that is an IPv4 or IPv6 localhost address. Otherwise, return 0.
  */
 static int
 smartlist_contains_localhost_tor_addr(smartlist_t *smartlist)
 {
-  int found_localhost = 0;
+  SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+    if (tor_addr_is_loopback(tor_addr)) {
+      return 1;
+    }
+  } SMARTLIST_FOREACH_END(tor_addr);
 
-  struct sockaddr_in *sockaddr_localhost;
-  struct sockaddr_storage *sockaddr_to_check;
+  return 0;
+}
 
-  sockaddr_localhost = sockaddr_in_from_string("127.0.0.1",NULL);
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is an IPv4 or IPv6 multicast address. Otherwise, return 0.
+ */
+static int
+smartlist_contains_multicast_tor_addr(smartlist_t *smartlist)
+{
+  SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+    if (tor_addr_is_multicast(tor_addr)) {
+      return 1;
+    }
+  } SMARTLIST_FOREACH_END(tor_addr);
 
-  sockaddr_to_check = tor_malloc(sizeof(struct sockaddr_in));
+  return 0;
+}
 
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is an IPv4 or IPv6 internal address. Otherwise, return 0.
+ */
+static int
+smartlist_contains_internal_tor_addr(smartlist_t *smartlist)
+{
   SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
-    tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
-                         sizeof(struct sockaddr_in));
+    if (tor_addr_is_internal(tor_addr, 0)) {
+      return 1;
+    }
+  } SMARTLIST_FOREACH_END(tor_addr);
 
-    if (sockaddr_in_are_equal((struct sockaddr_in *)sockaddr_to_check,
-                              sockaddr_localhost)) {
-      found_localhost = 1;
-      break;
+  return 0;
+}
+
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is an IPv4 address. Otherwise, return 0.
+ */
+static int
+smartlist_contains_ipv4_tor_addr(smartlist_t *smartlist)
+{
+  SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+    if (tor_addr_is_v4(tor_addr)) {
+      return 1;
     }
   } SMARTLIST_FOREACH_END(tor_addr);
 
-  tor_free(sockaddr_localhost);
-  tor_free(sockaddr_to_check);
+  return 0;
+}
 
-  return found_localhost;
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is an IPv6 address. Otherwise, return 0.
+ */
+static int
+smartlist_contains_ipv6_tor_addr(smartlist_t *smartlist)
+{
+  SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+    /* Since there's no tor_addr_is_v6, assume all non-v4s are v6 */
+    if (!tor_addr_is_v4(tor_addr)) {
+      return 1;
+    }
+  } SMARTLIST_FOREACH_END(tor_addr);
+
+  return 0;
 }
 
 #ifdef HAVE_IFADDRS_TO_SMARTLIST
@@ -634,12 +678,168 @@ test_address_udp_socket_trick_blackbox(void *arg)
   return;
 }
 
+static void
+test_address_get_if_addrs_list_internal(void *arg)
+{
+  smartlist_t *results = NULL;
+
+  (void)arg;
+
+  results = get_interface_address_list(LOG_ERR, 1);
+
+  tt_assert(results != NULL);
+  /* Assume every system has at least 1 non-local non-multicast IPv4
+   * interface, even if it is an internal one */
+  tt_int_op(smartlist_len(results),>=,1);
+
+  tt_assert(!smartlist_contains_localhost_tor_addr(results));
+  tt_assert(!smartlist_contains_multicast_tor_addr(results));
+  /* The list may or may not contain internal addresses */
+
+  tt_assert(smartlist_contains_ipv4_tor_addr(results));
+  tt_assert(!smartlist_contains_ipv6_tor_addr(results));
+
+ done:
+  free_interface_address_list(results);
+  return;
+}
+
+static void
+test_address_get_if_addrs_list_no_internal(void *arg)
+{
+  smartlist_t *results = NULL;
+
+  (void)arg;
+
+  results = get_interface_address_list(LOG_ERR, 0);
+
+  tt_assert(results != NULL);
+  /* Work even on systems with only internal IPv4 addresses */
+  tt_int_op(smartlist_len(results),>=,0);
+
+  tt_assert(!smartlist_contains_localhost_tor_addr(results));
+  tt_assert(!smartlist_contains_multicast_tor_addr(results));
+  tt_assert(!smartlist_contains_internal_tor_addr(results));
+
+    /* The list may or may not contain IPv4 addresses */
+  tt_assert(!smartlist_contains_ipv6_tor_addr(results));
+
+ done:
+  free_interface_address_list(results);
+  return;
+}
+
+static void
+test_address_get_if_addrs6_list_internal(void *arg)
+{
+  smartlist_t *results = NULL;
+
+  (void)arg;
+
+  results = get_interface_address6_list(LOG_ERR, AF_INET6, 1);
+
+  tt_assert(results != NULL);
+  /* Work even on systems without IPv6 interfaces */
+  tt_int_op(smartlist_len(results),>=,0);
+
+  tt_assert(!smartlist_contains_localhost_tor_addr(results));
+  tt_assert(!smartlist_contains_multicast_tor_addr(results));
+  /* The list may or may not contain internal addresses */
+
+  tt_assert(!smartlist_contains_ipv4_tor_addr(results));
+  /* The list may or may not contain IPv6 addresses */
+
+ done:
+  free_interface_address6_list(results);
+  return;
+}
+
+static void
+test_address_get_if_addrs6_list_no_internal(void *arg)
+{
+  smartlist_t *results = NULL;
+
+  (void)arg;
+
+  results = get_interface_address6_list(LOG_ERR, AF_INET6, 0);
+
+  tt_assert(results != NULL);
+  /* Work even on systems without IPv6 interfaces */
+  tt_int_op(smartlist_len(results),>=,0);
+
+  tt_assert(!smartlist_contains_localhost_tor_addr(results));
+  tt_assert(!smartlist_contains_multicast_tor_addr(results));
+  tt_assert(!smartlist_contains_internal_tor_addr(results));
+
+  tt_assert(!smartlist_contains_ipv4_tor_addr(results));
+  /* The list may or may not contain IPv6 addresses */
+
+ done:
+  free_interface_address6_list(results);
+  return;
+}
+
+static void
+test_address_get_if_addrs(void *arg)
+{
+  int rv;
+  uint32_t addr_h = 0;
+  tor_addr_t tor_addr;
+
+  (void)arg;
+
+  rv = get_interface_address(LOG_ERR, &addr_h);
+
+  /* Assume every system has at least 1 non-local non-multicast IPv4
+   * interface, even if it is an internal one */
+  tt_assert(rv == 0);
+  tor_addr_from_ipv4h(&tor_addr, addr_h);
+
+  tt_assert(!tor_addr_is_loopback(&tor_addr));
+  tt_assert(!tor_addr_is_multicast(&tor_addr));
+  /* The address may or may not be an internal address */
+
+  tt_assert(tor_addr_is_v4(&tor_addr));
+
+ done:
+  return;
+}
+
+static void
+test_address_get_if_addrs6(void *arg)
+{
+  int rv;
+  tor_addr_t tor_addr;
+
+  (void)arg;
+
+  rv = get_interface_address6(LOG_ERR, AF_INET6, &tor_addr);
+
+  /* Work even on systems without IPv6 interfaces */
+  if (rv == 0) {
+    tt_assert(!tor_addr_is_loopback(&tor_addr));
+    tt_assert(!tor_addr_is_multicast(&tor_addr));
+    /* The address may or may not be an internal address */
+
+    tt_assert(!tor_addr_is_v4(&tor_addr));
+  }
+
+ done:
+  return;
+}
+
 #define ADDRESS_TEST(name, flags) \
   { #name, test_address_ ## name, flags, NULL, NULL }
 
 struct testcase_t address_tests[] = {
   ADDRESS_TEST(udp_socket_trick_whitebox, TT_FORK),
   ADDRESS_TEST(udp_socket_trick_blackbox, TT_FORK),
+  ADDRESS_TEST(get_if_addrs_list_internal, 0),
+  ADDRESS_TEST(get_if_addrs_list_no_internal, 0),
+  ADDRESS_TEST(get_if_addrs6_list_internal, 0),
+  ADDRESS_TEST(get_if_addrs6_list_no_internal, 0),
+  ADDRESS_TEST(get_if_addrs, 0),
+  ADDRESS_TEST(get_if_addrs6, 0),
 #ifdef HAVE_IFADDRS_TO_SMARTLIST
   ADDRESS_TEST(get_if_addrs_ifaddrs, TT_FORK),
   ADDRESS_TEST(ifaddrs_to_smartlist, 0),

+ 166 - 26
src/test/test_policy.c

@@ -49,7 +49,7 @@ test_policy_summary_helper(const char *policy_str,
 
   r = policies_parse_exit_policy(&line, &policy,
                                  EXIT_POLICY_IPV6_ENABLED |
-                                 EXIT_POLICY_ADD_DEFAULT ,0);
+                                 EXIT_POLICY_ADD_DEFAULT, 0, NULL, 0);
   tt_int_op(r,OP_EQ, 0);
 
   summary = policy_summarize(policy, AF_INET);
@@ -77,18 +77,21 @@ test_policies_general(void *arg)
   int i;
   smartlist_t *policy = NULL, *policy2 = NULL, *policy3 = NULL,
               *policy4 = NULL, *policy5 = NULL, *policy6 = NULL,
-              *policy7 = NULL;
+              *policy7 = NULL, *policy8 = NULL, *policy9 = NULL,
+              *policy10 = NULL, *policy11 = NULL, *policy12 = NULL;
   addr_policy_t *p;
   tor_addr_t tar;
   config_line_t line;
   smartlist_t *sm = NULL;
   char *policy_str = NULL;
   short_policy_t *short_parsed = NULL;
+  int malformed_list = -1;
   (void)arg;
 
   policy = smartlist_new();
 
-  p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*",-1);
+  p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   tt_int_op(ADDR_POLICY_REJECT,OP_EQ, p->policy_type);
   tor_addr_from_ipv4h(&tar, 0xc0a80000u);
@@ -112,68 +115,122 @@ test_policies_general(void *arg)
   tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy2,
                                               EXIT_POLICY_IPV6_ENABLED |
                                               EXIT_POLICY_REJECT_PRIVATE |
-                                              EXIT_POLICY_ADD_DEFAULT, 0));
+                                              EXIT_POLICY_ADD_DEFAULT, 0,
+                                              NULL, 0));
 
   tt_assert(policy2);
 
+  tor_addr_parse(&tar, "[2000::1234]");
+  tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy12,
+                                                 EXIT_POLICY_IPV6_ENABLED |
+                                                 EXIT_POLICY_REJECT_PRIVATE |
+                                                 EXIT_POLICY_ADD_DEFAULT,
+                                                 0x0306090cu, &tar, 1));
+
+  tt_assert(policy12);
+
   policy3 = smartlist_new();
-  p = router_parse_addr_policy_item_from_string("reject *:*",-1);
+  p = router_parse_addr_policy_item_from_string("reject *:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy3, p);
-  p = router_parse_addr_policy_item_from_string("accept *:*",-1);
+  p = router_parse_addr_policy_item_from_string("accept *:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy3, p);
 
   policy4 = smartlist_new();
-  p = router_parse_addr_policy_item_from_string("accept *:443",-1);
+  p = router_parse_addr_policy_item_from_string("accept *:443", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy4, p);
-  p = router_parse_addr_policy_item_from_string("accept *:443",-1);
+  p = router_parse_addr_policy_item_from_string("accept *:443", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy4, p);
 
   policy5 = smartlist_new();
-  p = router_parse_addr_policy_item_from_string("reject 0.0.0.0/8:*",-1);
+  p = router_parse_addr_policy_item_from_string("reject 0.0.0.0/8:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy5, p);
-  p = router_parse_addr_policy_item_from_string("reject 169.254.0.0/16:*",-1);
+  p = router_parse_addr_policy_item_from_string("reject 169.254.0.0/16:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy5, p);
-  p = router_parse_addr_policy_item_from_string("reject 127.0.0.0/8:*",-1);
+  p = router_parse_addr_policy_item_from_string("reject 127.0.0.0/8:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy5, p);
-  p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*",-1);
+  p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*",
+                                                -1, &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy5, p);
-  p = router_parse_addr_policy_item_from_string("reject 10.0.0.0/8:*",-1);
+  p = router_parse_addr_policy_item_from_string("reject 10.0.0.0/8:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy5, p);
-  p = router_parse_addr_policy_item_from_string("reject 172.16.0.0/12:*",-1);
+  p = router_parse_addr_policy_item_from_string("reject 172.16.0.0/12:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy5, p);
-  p = router_parse_addr_policy_item_from_string("reject 80.190.250.90:*",-1);
+  p = router_parse_addr_policy_item_from_string("reject 80.190.250.90:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy5, p);
-  p = router_parse_addr_policy_item_from_string("reject *:1-65534",-1);
+  p = router_parse_addr_policy_item_from_string("reject *:1-65534", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy5, p);
-  p = router_parse_addr_policy_item_from_string("reject *:65535",-1);
+  p = router_parse_addr_policy_item_from_string("reject *:65535", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy5, p);
-  p = router_parse_addr_policy_item_from_string("accept *:1-65535",-1);
+  p = router_parse_addr_policy_item_from_string("accept *:1-65535", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy5, p);
 
   policy6 = smartlist_new();
-  p = router_parse_addr_policy_item_from_string("accept 43.3.0.0/9:*",-1);
+  p = router_parse_addr_policy_item_from_string("accept 43.3.0.0/9:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy6, p);
 
   policy7 = smartlist_new();
-  p = router_parse_addr_policy_item_from_string("accept 0.0.0.0/8:*",-1);
+  p = router_parse_addr_policy_item_from_string("accept 0.0.0.0/8:*", -1,
+                                                &malformed_list);
   tt_assert(p != NULL);
   smartlist_add(policy7, p);
 
+  tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy8,
+                                                 EXIT_POLICY_IPV6_ENABLED |
+                                                 EXIT_POLICY_REJECT_PRIVATE |
+                                                 EXIT_POLICY_ADD_DEFAULT, 0,
+                                                 NULL, 0));
+
+  tt_assert(policy8);
+
+  tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy9,
+                                                 EXIT_POLICY_REJECT_PRIVATE |
+                                                 EXIT_POLICY_ADD_DEFAULT, 0,
+                                                 NULL, 0));
+
+  tt_assert(policy9);
+
+  /* accept6 * and reject6 * produce IPv6 wildcards only */
+  policy10 = smartlist_new();
+  p = router_parse_addr_policy_item_from_string("accept6 *:*", -1,
+                                                &malformed_list);
+  tt_assert(p != NULL);
+  smartlist_add(policy10, p);
+
+  policy11 = smartlist_new();
+  p = router_parse_addr_policy_item_from_string("reject6 *:*", -1,
+                                                &malformed_list);
+  tt_assert(p != NULL);
+  smartlist_add(policy11, p);
+
   tt_assert(!exit_policy_is_general_exit(policy));
   tt_assert(exit_policy_is_general_exit(policy2));
   tt_assert(!exit_policy_is_general_exit(NULL));
@@ -182,6 +239,10 @@ test_policies_general(void *arg)
   tt_assert(!exit_policy_is_general_exit(policy5));
   tt_assert(!exit_policy_is_general_exit(policy6));
   tt_assert(!exit_policy_is_general_exit(policy7));
+  tt_assert(exit_policy_is_general_exit(policy8));
+  tt_assert(exit_policy_is_general_exit(policy9));
+  tt_assert(!exit_policy_is_general_exit(policy10));
+  tt_assert(!exit_policy_is_general_exit(policy11));
 
   tt_assert(cmp_addr_policies(policy, policy2));
   tt_assert(cmp_addr_policies(policy, NULL));
@@ -190,7 +251,12 @@ test_policies_general(void *arg)
 
   tt_assert(!policy_is_reject_star(policy2, AF_INET));
   tt_assert(policy_is_reject_star(policy, AF_INET));
+  tt_assert(policy_is_reject_star(policy10, AF_INET));
+  tt_assert(!policy_is_reject_star(policy10, AF_INET6));
+  tt_assert(policy_is_reject_star(policy11, AF_INET));
+  tt_assert(policy_is_reject_star(policy11, AF_INET6));
   tt_assert(policy_is_reject_star(NULL, AF_INET));
+  tt_assert(policy_is_reject_star(NULL, AF_INET6));
 
   addr_policy_list_free(policy);
   policy = NULL;
@@ -202,7 +268,8 @@ test_policies_general(void *arg)
   line.next = NULL;
   tt_int_op(0, OP_EQ, policies_parse_exit_policy(&line,&policy,
                                               EXIT_POLICY_IPV6_ENABLED |
-                                              EXIT_POLICY_ADD_DEFAULT,0));
+                                              EXIT_POLICY_ADD_DEFAULT, 0,
+                                              NULL, 0));
   tt_assert(policy);
 
   //test_streq(policy->string, "accept *:80");
@@ -297,6 +364,68 @@ test_policies_general(void *arg)
   TT_BAD_SHORT_POLICY("accept 1-,3");
   TT_BAD_SHORT_POLICY("accept 1-,3");
 
+  /* Make sure that IPv4 addresses are ignored in accept6/reject6 lines. */
+  p = router_parse_addr_policy_item_from_string("accept6 1.2.3.4:*", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(!malformed_list);
+
+  p = router_parse_addr_policy_item_from_string("reject6 2.4.6.0/24:*", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(!malformed_list);
+
+  p = router_parse_addr_policy_item_from_string("accept6 *4:*", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(!malformed_list);
+
+  /* Make sure malformed policies are detected as such. */
+  p = router_parse_addr_policy_item_from_string("bad_token *4:*", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(malformed_list);
+
+  p = router_parse_addr_policy_item_from_string("accept6 **:*", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(malformed_list);
+
+  p = router_parse_addr_policy_item_from_string("accept */15:*", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(malformed_list);
+
+  p = router_parse_addr_policy_item_from_string("reject6 */:*", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(malformed_list);
+
+  p = router_parse_addr_policy_item_from_string("accept 127.0.0.1/33:*", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(malformed_list);
+
+  p = router_parse_addr_policy_item_from_string("accept6 [::1]/129:*", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(malformed_list);
+
+  p = router_parse_addr_policy_item_from_string("reject 8.8.8.8/-1:*", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(malformed_list);
+
+  p = router_parse_addr_policy_item_from_string("reject 8.8.4.4:10-5", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(malformed_list);
+
+  p = router_parse_addr_policy_item_from_string("reject 1.2.3.4:-1", -1,
+                                                &malformed_list);
+  tt_assert(p == NULL);
+  tt_assert(malformed_list);
+
   /* Test a too-long policy. */
   {
     int i;
@@ -347,6 +476,11 @@ test_policies_general(void *arg)
   addr_policy_list_free(policy5);
   addr_policy_list_free(policy6);
   addr_policy_list_free(policy7);
+  addr_policy_list_free(policy8);
+  addr_policy_list_free(policy9);
+  addr_policy_list_free(policy10);
+  addr_policy_list_free(policy11);
+  addr_policy_list_free(policy12);
   tor_free(policy_str);
   if (sm) {
     SMARTLIST_FOREACH(sm, char *, s, tor_free(s));
@@ -360,6 +494,7 @@ test_dump_exit_policy_to_string(void *arg)
 {
  char *ep;
  addr_policy_t *policy_entry;
+ int malformed_list = -1;
 
  routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t));
 
@@ -376,7 +511,8 @@ test_dump_exit_policy_to_string(void *arg)
  ri->exit_policy = smartlist_new();
  ri->policy_is_reject_star = 0;
 
- policy_entry = router_parse_addr_policy_item_from_string("accept *:*",-1);
+ policy_entry = router_parse_addr_policy_item_from_string("accept *:*", -1,
+                                                          &malformed_list);
 
  smartlist_add(ri->exit_policy,policy_entry);
 
@@ -386,7 +522,8 @@ test_dump_exit_policy_to_string(void *arg)
 
  tor_free(ep);
 
- policy_entry = router_parse_addr_policy_item_from_string("reject *:25",-1);
+ policy_entry = router_parse_addr_policy_item_from_string("reject *:25", -1,
+                                                          &malformed_list);
 
  smartlist_add(ri->exit_policy,policy_entry);
 
@@ -397,7 +534,8 @@ test_dump_exit_policy_to_string(void *arg)
  tor_free(ep);
 
  policy_entry =
- router_parse_addr_policy_item_from_string("reject 8.8.8.8:*",-1);
+ router_parse_addr_policy_item_from_string("reject 8.8.8.8:*", -1,
+                                           &malformed_list);
 
  smartlist_add(ri->exit_policy,policy_entry);
 
@@ -407,7 +545,8 @@ test_dump_exit_policy_to_string(void *arg)
  tor_free(ep);
 
  policy_entry =
- router_parse_addr_policy_item_from_string("reject6 [FC00::]/7:*",-1);
+ router_parse_addr_policy_item_from_string("reject6 [FC00::]/7:*", -1,
+                                           &malformed_list);
 
  smartlist_add(ri->exit_policy,policy_entry);
 
@@ -418,7 +557,8 @@ test_dump_exit_policy_to_string(void *arg)
  tor_free(ep);
 
  policy_entry =
- router_parse_addr_policy_item_from_string("accept6 [c000::]/3:*",-1);
+ router_parse_addr_policy_item_from_string("accept6 [c000::]/3:*", -1,
+                                           &malformed_list);
 
  smartlist_add(ri->exit_policy,policy_entry);
 

+ 5 - 2
src/test/test_routerset.c

@@ -430,7 +430,7 @@ NS(test_main)(void *arg)
  */
 
 NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
-    (const char *s, int assume_action));
+    (const char *s, int assume_action, int *malformed_list));
 
 addr_policy_t *NS(mock_addr_policy);
 
@@ -457,10 +457,13 @@ NS(test_main)(void *arg)
 }
 
 addr_policy_t *
-NS(router_parse_addr_policy_item_from_string)(const char *s, int assume_action)
+NS(router_parse_addr_policy_item_from_string)(const char *s,
+                                              int assume_action,
+                                              int *malformed_list)
 {
   (void)s;
   (void)assume_action;
+  (void)malformed_list;
   CALLED(router_parse_addr_policy_item_from_string)++;
 
   return NS(mock_addr_policy);