Browse Source

Replace (Fascist)Firewall* with a new ReachableAddresses option that understands address policies.

svn:r4751
Nick Mathewson 19 years ago
parent
commit
249b72f53e
12 changed files with 154 additions and 95 deletions
  1. 2 0
      doc/TODO
  2. 16 10
      doc/tor.1.in
  3. 3 2
      src/or/circuitbuild.c
  4. 96 62
      src/or/config.c
  5. 1 1
      src/or/connection_edge.c
  6. 3 3
      src/or/directory.c
  7. 11 5
      src/or/or.h
  8. 2 2
      src/or/relay.c
  9. 1 1
      src/or/router.c
  10. 3 4
      src/or/routerlist.c
  11. 15 4
      src/or/routerparse.c
  12. 1 1
      src/or/test.c

+ 2 - 0
doc/TODO

@@ -108,6 +108,8 @@ N . helper nodes (Choose N nodes randomly; if a node dies (goes down for a
     - On sighup, if usehelpernodes changed to 1, use new circs.
   o Make a FirewallIPs to correspond to firewallPorts so I can use Tor at
     MIT when my directory is out of date.
+    o Document, rename, deprecate fascistfirewall, and make it use
+      addr_policy_t logic.
   - switch accountingmax to count total in+out, not either in or
     out. it's easy to move in this direction (not risky), but hard to
     back, out if we decide we prefer it the way it already is. hm.

+ 16 - 10
doc/tor.1.in

@@ -233,19 +233,25 @@ If 1, Tor will only create outgoing connections to ORs running on ports that
 your firewall allows (defaults to 80 and 443; see \fBFirewallPorts\fR).  This will
 allow you to run Tor as a client behind a firewall with restrictive policies,
 but will not allow you to run as a server behind such a firewall.
+This option is deprecated; use
+ReachableAddresses instead.
 .LP
 .TP
 \fBFirewallPorts \fR\fIPORTS\fP
-A list of ports that your firewall allows you to connect to.  Only used when
-\fBFascistFirewall\fR is set. (Default: 80, 443)
-.LP
-.TP
-\fBFirewallIPs \fR\fIADDR\fP[\fB/\fP\fIMASK\fP\fB][:\fP\fIPORT\fP]...\fP
-A comma-separated list of IPs that your firewall allows you to connect to.
-Only used when \fBFascistFirewall\fR is set.  The format is as for the
-addresses in ExitPolicy.  For example, 'FirewallIPs 99.0.0.0/8, *:80' means
-that your firewall allows connections to everything inside net 99, and to
-port 80 outside.
+A list of ports that your firewall allows you to connect to.  Only
+used when \fBFascistFirewall\fR is set. This option is deprecated; use
+ReachableAddresses instead. (Default: 80, 443)
+.LP
+.TP
+\fBReachableAddresses \fR\fIADDR\fP[\fB/\fP\fIMASK\fP\fB][:\fP\fIPORT\fP]...\fP
+A comma-separated list of IPs that your firewall allows you to connect
+to.  Only used when \fBFascistFirewall\fR is set.  The format is as
+for the addresses in ExitPolicy, except that "accept" is understood
+unless "reject" is explicitly provided.  For example, 'FirewallIPs
+99.0.0.0/8, reject 18.0.0.0/8:80, accept *:80' means that your
+firewall allows connections to everything inside net 99, rejects port
+80 connections to net 18, and accepts connections to port 80 otherwise.
+(Default: 'accept *:*'.)
 .LP
 .TP
 \fBLongLivedPorts \fR\fIPORTS\fP

+ 3 - 2
src/or/circuitbuild.c

@@ -1416,7 +1416,7 @@ choose_good_entry_server(cpath_build_state_t *state)
     smartlist_add(excluded, r);
     routerlist_add_family(excluded, r);
   }
-  if (options->FascistFirewall) {
+  if (firewall_is_fascist()) {
     /* exclude all ORs that listen on the wrong port */
     routerlist_t *rl;
     int i;
@@ -1427,7 +1427,7 @@ choose_good_entry_server(cpath_build_state_t *state)
 
     for (i=0; i < smartlist_len(rl->routers); i++) {
       r = smartlist_get(rl->routers, i);
-      if (!fascist_firewall_allows_address(options,r->addr,r->or_port))
+      if (!fascist_firewall_allows_address(r->addr,r->or_port))
         smartlist_add(excluded, r);
     }
   }
@@ -1986,3 +1986,4 @@ helper_nodes_getinfo_helper(const char *question, char **answer)
   }
   return 0;
 }
+

+ 96 - 62
src/or/config.c

@@ -123,7 +123,6 @@ static config_var_t _option_vars[] = {
   VAR("ExitPolicy",          LINELIST, ExitPolicy,           NULL),
   VAR("FascistFirewall",     BOOL,     FascistFirewall,      "0"),
   VAR("FirewallPorts",       CSV,      FirewallPorts,        ""),
-  VAR("FirewallIPs",         CSV,      FirewallIPs,          NULL),
   VAR("Group",               STRING,   Group,                NULL),
   VAR("HardwareAccel",       BOOL,     HardwareAccel,        "1"),
   VAR("HashedControlPassword",STRING,  HashedControlPassword, NULL),
@@ -160,6 +159,7 @@ static config_var_t _option_vars[] = {
   VAR("OutboundBindAddress", STRING,   OutboundBindAddress,  NULL),
   VAR("PathlenCoinWeight",   DOUBLE,   PathlenCoinWeight,    "0.3"),
   VAR("PidFile",             STRING,   PidFile,              NULL),
+  VAR("ReachableAddresses",  LINELIST, ReachableAddresses,   NULL),
   VAR("RecommendedVersions", LINELIST, RecommendedVersions,  NULL),
   VAR("RedirectExit",        LINELIST, RedirectExit,         NULL),
   VAR("RendExcludeNodes",    STRING,   RendExcludeNodes,     NULL),
@@ -285,6 +285,7 @@ static int or_state_validate(or_state_t *options);
 static uint64_t config_parse_memunit(const char *s, int *ok);
 static int config_parse_interval(const char *s, int *ok);
 static void print_cvs_version(void);
+static void parse_reachable_addresses(void);
 static int init_libevent(void);
 #if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
 static void check_libevent_version(const char *m, const char *v, int server);
@@ -324,6 +325,8 @@ static or_options_t *global_options = NULL;
 static char *config_fname = NULL;
 /** Persistant serialized state. */
 static or_state_t *global_state = NULL;
+/** DOCDOC */
+static addr_policy_t *reachable_addr_policy = NULL;
 
 static void *
 config_alloc(config_format_t *fmt)
@@ -358,6 +361,8 @@ config_free_all(void)
 {
   config_free(&options_format, global_options);
   tor_free(config_fname);
+  addr_policy_free(reachable_addr_policy);
+  reachable_addr_policy = NULL;
 }
 
 /** If options->SafeLogging is on, return a not very useful string,
@@ -483,6 +488,7 @@ options_act(void)
   /* Update address policies. */
   parse_socks_policy();
   parse_dir_policy();
+  parse_reachable_addresses();
 
   init_cookie_authentication(options->CookieAuthentication);
 
@@ -1376,7 +1382,6 @@ config_dump(config_format_t *fmt, void *options, int minimal)
     if (minimal && option_is_same(fmt, options, defaults, fmt->vars[i].name))
       continue;
 
-
     desc = config_find_description(fmt, fmt->vars[i].name);
     if (desc) {
       size_t len = strlen(desc)+8;
@@ -1439,56 +1444,51 @@ validate_ports_csv(smartlist_t *sl, const char *name)
   return result;
 }
 
-/* Return 0 if every element of sl is a string holding an IP with
- * optional mask and port, or if sl is NULL. Otherwise return -1. */
-static int
-validate_addr_port_ranges_csv(smartlist_t *sl, const char *name)
+/** DOCDOC */
+static void
+parse_reachable_addresses(void)
 {
-  uint32_t addr, mask;
-  uint16_t port_min, port_max;
-  int result = 0;
-  tor_assert(name);
+  or_options_t *options = get_options();
 
-  if (!sl)
-    return 0;
+  addr_policy_free(reachable_addr_policy);
+  reachable_addr_policy = NULL;
 
-  SMARTLIST_FOREACH(sl, const char *, cp,
-  {
-    if (parse_addr_and_port_range(cp, &addr, &mask, &port_min, &port_max)<0) {
-      log(LOG_WARN, "IP/port range '%s' invalid in %s", cp, name);
-      result=-1;
-    }
-  });
-  return result;
+  if (config_parse_addr_policy(options->ReachableAddresses,
+                               &reachable_addr_policy,
+                               ADDR_POLICY_ACCEPT)) {
+    log_fn(LOG_WARN, "Error in ReachableAddresses entry; ignoring.");
+    return;
+  }
 }
 
-/** Return true iff we are configured to think that the local fascist
- * firewall (if any) will allow a connection to <b>addr</b>:<b>port</b> */
+/** Return true iff the firewall options might block any address:port
+ * combination
+ */
 int
-fascist_firewall_allows_address(or_options_t *options, uint32_t addr,
-                                uint16_t port)
+firewall_is_fascist(void)
 {
-  uint32_t ipaddr, ipmask;
-  uint16_t portmin, portmax;
-  if (!options->FascistFirewall)
-    return 1;
-
-  if (smartlist_string_num_isin(options->FirewallPorts, port))
-    return 1;
-
-  if (!options->FirewallIPs)
-    return 0;
-
-  SMARTLIST_FOREACH(options->FirewallIPs, const char *, cp,
-    {
-      if (parse_addr_and_port_range(cp, &ipaddr, &ipmask, &portmin, &portmax)<0)
-        continue;
-      if ((addr&ipmask) == (ipaddr&ipmask) &&
-          (portmin <= port) && (port <= portmax))
-        return 1;
-    });
+  return reachable_addr_policy ? 1 : 0;
+}
 
-  return 0;
+/** Return true iff we are configured to think that the local fascist
+ * firewall (if any) will allow a connection to <b>addr</b>:<b>port</b> */
+int
+fascist_firewall_allows_address(uint32_t addr, uint16_t port)
+{
+  addr_policy_result_t p = router_compare_addr_to_addr_policy(
+               addr, port, reachable_addr_policy);
+
+  switch (p) {
+    case ADDR_POLICY_PROBABLY_ACCEPTED:
+    case ADDR_POLICY_ACCEPTED:
+      return 1;
+    case ADDR_POLICY_PROBABLY_REJECTED:
+    case ADDR_POLICY_REJECTED:
+      return 0;
+    default:
+      log_fn(LOG_WARN, "Unexpected result: %d", (int)p);
+      return 0;
+  }
 }
 
 /** Return 0 if every setting in <b>options</b> is reasonable.  Else
@@ -1632,20 +1632,48 @@ options_validate(or_options_t *options)
                          "FirewallPorts") < 0)
     result = -1;
 
-  if (validate_addr_port_ranges_csv(options->FirewallIPs,
-                                    "FirewallIPs") < 0)
+  if (validate_ports_csv(options->LongLivedPorts,
+                         "LongLivedPorts") < 0)
     result = -1;
 
-  if (options->FascistFirewall &&
-      !smartlist_len(options->FirewallIPs) &&
-      !smartlist_len(options->FirewallPorts)) {
-    smartlist_add(options->FirewallPorts, tor_strdup("80"));
-    smartlist_add(options->FirewallPorts, tor_strdup("443"));
+  if (options->FascistFirewall) {
+    smartlist_t *instead = smartlist_create();
+    config_line_t *new_line = tor_malloc_zero(sizeof(config_line_t));
+    new_line->key = tor_strdup("ReachableAddresses");
+    /* If we're configured with the old format, we need to prepend some
+     * open ports. */
+    if (!smartlist_len(options->FirewallPorts)) {
+      smartlist_add(options->FirewallPorts, tor_strdup("80"));
+      smartlist_add(options->FirewallPorts, tor_strdup("443"));
+    }
+    SMARTLIST_FOREACH(options->FirewallPorts, const char *, portno,
+      {
+        int p = atoi(portno);
+        char *s;
+        if (p<0) continue;
+        s = tor_malloc(16);
+        tor_snprintf(s, 16, "*:%d", p);
+        smartlist_add(instead, s);
+      });
+    new_line->value = smartlist_join_strings(instead,",",0,NULL);
+    /* These have been deprecated since 0.1.1.5-alpha-cvs */
+    log_fn(LOG_WARN, "FascistFirewall and FirewallPorts are deprecated.  Instead, use \"ReachableAddresses %s\"", new_line->value);
+    new_line->next = options->ReachableAddresses;
+    options->ReachableAddresses = new_line;
+    SMARTLIST_FOREACH(instead, char *, cp, tor_free(cp));
+    smartlist_free(instead);
   }
 
-  if (validate_ports_csv(options->LongLivedPorts,
-                         "LongLivedPorts") < 0)
-    result = -1;
+  if (options->FascistFirewall || options->ReachableAddresses) {
+    /* We need to end with a reject *:*, not an implicit accept *:* */
+    config_line_t **linep = &options->ReachableAddresses;
+    while (*linep) {
+      linep = &((*linep)->next);
+    }
+    *linep = tor_malloc_zero(sizeof(config_line_t));
+    (*linep)->key = tor_strdup("ReachableAddresses");
+    (*linep)->value = tor_strdup("reject *:*");
+  }
 
   options->_AllowUnverified = 0;
   if (options->AllowUnverifiedNodes) {
@@ -1844,7 +1872,7 @@ options_validate(or_options_t *options)
       result = -1;
   }
 
-  if (config_parse_addr_policy(options->ExitPolicy, &addr_policy)) {
+  if (config_parse_addr_policy(options->ExitPolicy, &addr_policy, -1)) {
     log_fn(LOG_WARN, "Error in Exit Policy entry.");
     result = -1;
   }
@@ -1854,14 +1882,19 @@ options_validate(or_options_t *options)
   }
   /* The rest of these calls *append* to addr_policy. So don't actually
    * use the results for anything other than checking if they parse! */
-  if (config_parse_addr_policy(options->DirPolicy, &addr_policy)) {
+  if (config_parse_addr_policy(options->DirPolicy, &addr_policy, -1)) {
     log_fn(LOG_WARN, "Error in DirPolicy entry.");
     result = -1;
   }
-  if (config_parse_addr_policy(options->SocksPolicy, &addr_policy)) {
+  if (config_parse_addr_policy(options->SocksPolicy, &addr_policy, -1)) {
     log_fn(LOG_WARN, "Error in SocksPolicy entry.");
     result = -1;
   }
+  if (config_parse_addr_policy(options->ReachableAddresses, &addr_policy,
+                               ADDR_POLICY_ACCEPT)) {
+    log_fn(LOG_WARN, "Error in ReachableAddresses entry.");
+    result = -1;
+  }
   addr_policy_free(addr_policy);
 
   for (cl = options->RedirectExit; cl; cl = cl->next) {
@@ -2461,7 +2494,7 @@ options_append_default_exit_policy(addr_policy_t **policy)
   tmp.key = NULL;
   tmp.value = (char*)DEFAULT_EXIT_POLICY;
   tmp.next = NULL;
-  config_parse_addr_policy(&tmp, policy);
+  config_parse_addr_policy(&tmp, policy, -1);
 
   /* Remove redundant parts, if any. */
   for (ap=*policy; ap; ap=ap->next) {
@@ -2482,7 +2515,8 @@ options_append_default_exit_policy(addr_policy_t **policy)
  */
 int
 config_parse_addr_policy(config_line_t *cfg,
-                         addr_policy_t **dest)
+                         addr_policy_t **dest,
+                         int assume_action)
 {
   addr_policy_t **nextp;
   smartlist_t *entries;
@@ -2502,7 +2536,7 @@ config_parse_addr_policy(config_line_t *cfg,
     SMARTLIST_FOREACH(entries, const char *, ent,
     {
       log_fn(LOG_DEBUG,"Adding new entry '%s'",ent);
-      *nextp = router_parse_addr_policy_from_string(ent);
+      *nextp = router_parse_addr_policy_from_string(ent, assume_action);
       if (*nextp) {
         nextp = &((*nextp)->next);
       } else {
@@ -3140,9 +3174,9 @@ config_getinfo_helper(const char *question, char **answer)
       size_t len;
       desc = config_find_description(&options_format, var->name);
       switch (var->type) {
-        case CONFIG_TYPE_STRING: type = "String"; break; 
+        case CONFIG_TYPE_STRING: type = "String"; break;
         case CONFIG_TYPE_UINT: type = "Integer"; break;
-        case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break; 
+        case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break;
         case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break;
         case CONFIG_TYPE_DOUBLE: type = "Float"; break;
         case CONFIG_TYPE_BOOL: type = "Boolean"; break;

+ 1 - 1
src/or/connection_edge.c

@@ -1696,7 +1696,7 @@ parse_socks_policy(void)
     addr_policy_free(socks_policy);
     socks_policy = NULL;
   }
-  config_parse_addr_policy(get_options()->SocksPolicy, &socks_policy);
+  config_parse_addr_policy(get_options()->SocksPolicy, &socks_policy, -1);
   /* ports aren't used. */
   for (n=socks_policy; n; n = n->next) {
     n->prt_min = 1;

+ 3 - 3
src/or/directory.c

@@ -69,7 +69,7 @@ parse_dir_policy(void)
     addr_policy_free(dir_policy);
     dir_policy = NULL;
   }
-  config_parse_addr_policy(get_options()->DirPolicy, &dir_policy);
+  config_parse_addr_policy(get_options()->DirPolicy, &dir_policy, -1);
   /* ports aren't used. */
   for (n=dir_policy; n; n = n->next) {
     n->prt_min = 1;
@@ -138,7 +138,7 @@ directory_post_to_dirservers(uint8_t purpose, const char *payload,
        * router descriptor, but not when uploading a service
        * descriptor -- those use Tor. */
       if (purpose == DIR_PURPOSE_UPLOAD_DIR && !get_options()->HttpProxy) {
-        if (!fascist_firewall_allows_address(get_options(),ds->addr,ds->dir_port))
+        if (!fascist_firewall_allows_address(ds->addr,ds->dir_port))
           continue;
       }
       directory_initiate_command_trusted_dir(ds, purpose, purpose_is_private(purpose),
@@ -159,7 +159,7 @@ directory_get_from_dirserver(uint8_t purpose, const char *resource,
 {
   routerinfo_t *r = NULL;
   trusted_dir_server_t *ds = NULL;
-  int fascistfirewall = get_options()->FascistFirewall;
+  int fascistfirewall = firewall_is_fascist();
   int directconn = purpose == DIR_PURPOSE_FETCH_DIR ||
                    purpose == DIR_PURPOSE_FETCH_RUNNING_LIST;
   int fetch_fresh_first = advertised_server_mode();

+ 11 - 5
src/or/or.h

@@ -1094,7 +1094,9 @@ typedef struct {
   int RunAsDaemon; /**< If true, run in the background. (Unix only) */
   int FascistFirewall; /**< Whether to prefer ORs reachable on open ports. */
   smartlist_t *FirewallPorts; /**< Which ports our firewall allows (strings). */
-  smartlist_t *FirewallIPs; /**< Which IPs our firewall allows (strings). */
+  config_line_t *ReachableAddresses; /**< Which IP:ports our firewall allows
+                                      * (exit policy.) */
+
   /** Application ports that require all nodes in circ to have sufficient uptime. */
   smartlist_t *LongLivedPorts;
   /** Should we try to reuse the same exit node for a given host */
@@ -1175,6 +1177,8 @@ typedef struct {
                        * of fixed nodes? */
   int NumHelperNodes; /**< How many helper nodes do we try to establish? */
   int RephistTrackTime; /**< How many seconds do we keep rephist info? */
+
+  addr_policy_t *reachable_addr_policy; /**< Parsed from ReachableAddresses */
 } or_options_t;
 
 /** Persistent state for an onion router, as saved to disk. */
@@ -1360,7 +1364,8 @@ void options_init(or_options_t *options);
 int options_init_from_torrc(int argc, char **argv);
 int options_init_logs(or_options_t *options, int validate_only);
 int config_parse_addr_policy(config_line_t *cfg,
-                             addr_policy_t **dest);
+                             addr_policy_t **dest,
+                             int assume_action);
 void options_append_default_exit_policy(addr_policy_t **policy);
 void addr_policy_free(addr_policy_t *p);
 int option_is_recognized(const char *key);
@@ -1376,8 +1381,8 @@ int or_state_save(void);
 
 int config_getinfo_helper(const char *question, char **answer);
 
-int fascist_firewall_allows_address(or_options_t *options, uint32_t addr,
-                                    uint16_t port);
+int firewall_is_fascist(void);
+int fascist_firewall_allows_address(uint32_t addr, uint16_t port);
 
 /********************************* connection.c ***************************/
 
@@ -2022,7 +2027,8 @@ running_routers_t *router_parse_runningrouters(const char *str,
                                                int write_to_cache);
 routerinfo_t *router_parse_entry_from_string(const char *s, const char *end);
 int router_add_exit_policy_from_string(routerinfo_t *router, const char *s);
-addr_policy_t *router_parse_addr_policy_from_string(const char *s);
+addr_policy_t *router_parse_addr_policy_from_string(const char *s,
+                                                    int assume_action);
 int check_software_version_against_directory(const char *directory);
 int tor_version_parse(const char *s, tor_version_t *out);
 int tor_version_as_new_as(const char *platform, const char *cutoff);

+ 2 - 2
src/or/relay.c

@@ -656,7 +656,7 @@ connection_edge_process_end_not_open(
           log_fn(LOG_NOTICE,"Exitrouter '%s' seems to be more restrictive than its exit policy. Not using this router as exit for now.", exitrouter->nickname);
           addr_policy_free(exitrouter->exit_policy);
           exitrouter->exit_policy =
-            router_parse_addr_policy_from_string("reject *:*");
+            router_parse_addr_policy_from_string("reject *:*", -1);
         }
         if (connection_ap_detach_retriable(conn, circ) >= 0)
           return 0;
@@ -683,7 +683,7 @@ connection_edge_process_end_not_open(
         if (exitrouter) {
           addr_policy_free(exitrouter->exit_policy);
           exitrouter->exit_policy =
-            router_parse_addr_policy_from_string("reject *:*");
+            router_parse_addr_policy_from_string("reject *:*", -1);
         }
         if (connection_ap_detach_retriable(conn, circ) >= 0)
           return 0;

+ 1 - 1
src/or/router.c

@@ -749,7 +749,7 @@ router_rebuild_descriptor(int force)
   if (options->BandwidthRate > options->MaxAdvertisedBandwidth)
     ri->bandwidthrate = (int)options->MaxAdvertisedBandwidth;
 
-  config_parse_addr_policy(get_options()->ExitPolicy, &ri->exit_policy);
+  config_parse_addr_policy(get_options()->ExitPolicy, &ri->exit_policy, -1);
   options_append_default_exit_policy(&ri->exit_policy);
 
   if (desc_routerinfo) /* inherit values */

+ 3 - 4
src/or/routerlist.c

@@ -123,7 +123,7 @@ router_pick_directory_server(int requireother,
     return choice;
 
   log_fn(LOG_INFO,"Still no %s router entries. Reloading and trying again.",
-         get_options()->FascistFirewall ? "reachable" : "known");
+         firewall_is_fascist() ? "reachable" : "known");
   has_fetched_directory=0; /* reset it */
   if (router_reload_router_list()) {
     return NULL;
@@ -187,8 +187,7 @@ router_pick_directory_server_impl(int requireother, int fascistfirewall,
     if (requireother && router_is_me(router))
       continue;
     if (fascistfirewall) {
-      if (!fascist_firewall_allows_address(get_options(),router->addr,
-                                           router->dir_port))
+      if (!fascist_firewall_allows_address(router->addr, router->dir_port))
         continue;
     }
     /* before 0.0.9rc5-cvs, only trusted dirservers served status info. */
@@ -231,7 +230,7 @@ router_pick_trusteddirserver_impl(int requireother, int fascistfirewall)
           !memcmp(me->identity_digest, d->digest, DIGEST_LEN))
         continue;
       if (fascistfirewall) {
-        if (!fascist_firewall_allows_address(get_options(),d->addr,d->dir_port))
+        if (!fascist_firewall_allows_address(d->addr, d->dir_port))
           continue;
       }
       smartlist_add(sl, d);

+ 15 - 4
src/or/routerparse.c

@@ -1028,10 +1028,12 @@ router_parse_entry_from_string(const char *s, const char *end)
   return router;
 }
 
-/** Parse the exit policy in the string <b>s</b> and return it.
+/** Parse the exit 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.
  */
 addr_policy_t *
-router_parse_addr_policy_from_string(const char *s)
+router_parse_addr_policy_from_string(const char *s, int assume_action)
 {
   directory_token_t *tok = NULL;
   const char *cp;
@@ -1047,6 +1049,15 @@ router_parse_addr_policy_from_string(const char *s)
   }
   tmp[len]='\n';
   tmp[len+1]='\0';
+  while (TOR_ISSPACE(*cp))
+    ++cp;
+  if ((*cp == '*' || TOR_ISDIGIT(*cp)) && assume_action >= 0) {
+    char *new_str = tor_malloc(len+10);
+    tor_snprintf(new_str, len+10, "%s %s\n",
+                 assume_action == ADDR_POLICY_ACCEPT?"accept":"reject", cp);
+    tor_free(tmp);
+    cp = tmp = new_str;
+  }
   tok = get_next_token(&cp, RTR_ONLY);
   if (tok->tp == _ERR) {
     log_fn(LOG_WARN, "Error reading exit policy: %s", tok->error);
@@ -1073,7 +1084,7 @@ int
 router_add_exit_policy_from_string(routerinfo_t *router, const char *s)
 {
   addr_policy_t *newe, *tmpe;
-  newe = router_parse_addr_policy_from_string(s);
+  newe = router_parse_addr_policy_from_string(s, -1);
   if (!newe)
     return -1;
   for (tmpe = router->exit_policy; tmpe; tmpe=tmpe->next)
@@ -1156,7 +1167,7 @@ assert_addr_policy_ok(addr_policy_t *t)
     tor_assert(t->policy_type == ADDR_POLICY_REJECT ||
                t->policy_type == ADDR_POLICY_ACCEPT);
     tor_assert(t->prt_min <= t->prt_max);
-    t2 = router_parse_addr_policy_from_string(t->string);
+    t2 = router_parse_addr_policy_from_string(t->string, -1);
     tor_assert(t2);
     tor_assert(t2->policy_type == t->policy_type);
     tor_assert(t2->addr == t->addr);

+ 1 - 1
src/or/test.c

@@ -1385,7 +1385,7 @@ test_exit_policies(void)
 {
   addr_policy_t *policy;
 
-  policy = router_parse_addr_policy_from_string("reject 192.168.0.0/16:*");
+  policy = router_parse_addr_policy_from_string("reject 192.168.0.0/16:*",-1);
   test_eq(NULL, policy->next);
   test_eq(ADDR_POLICY_REJECT, policy->policy_type);
   test_eq(0xc0a80000u, policy->addr);