[or-cvs] Improved exit policy syntax; basic client-side DNS caching.

Nick Mathewson nickm at seul.org
Fri Nov 14 20:45:49 UTC 2003


Update of /home/or/cvsroot/src/or
In directory moria.mit.edu:/tmp/cvs-serv21013/or

Modified Files:
	circuit.c connection_edge.c onion.c or.h routers.c test.c 
Log Message:
Improved exit policy syntax; basic client-side DNS caching.

- Exit policies now support bitmasks (18.0.0.0/255.0.0.0) and bitcounts
  18.0.0.0/8.  Policies are parsed on startup, not when comparing to them.

- desired_path_len is now part of an opaque cpath_build_state_t structure.

- END_REASON_EXITPOLICY cells no longer include a port.

- RELAY_COMMAND_CONNECTED cells now include the IP address we've connected
  to.

- connection_edge now has a client_dns cache to remember resolved addresses.
  It gets populated by RELAY_COMMAND_CONNECTED cells and END_REASON_EXITPOLICY
  cells.  It gets used by connection_ap_handshake_send_begin.  We don't
  compare it to exit policies yet.



Index: circuit.c
===================================================================
RCS file: /home/or/cvsroot/src/or/circuit.c,v
retrieving revision 1.91
retrieving revision 1.92
diff -u -d -r1.91 -r1.92
--- circuit.c	14 Nov 2003 07:15:52 -0000	1.91
+++ circuit.c	14 Nov 2003 20:45:47 -0000	1.92
@@ -81,10 +81,12 @@
 }
 
 void circuit_free(circuit_t *circ) {
+  assert(circ);
   if (circ->n_crypto)
     crypto_free_cipher_env(circ->n_crypto);
   if (circ->p_crypto)
     crypto_free_cipher_env(circ->p_crypto);
+  tor_free(circ->build_state);
   circuit_free_cpath(circ->cpath);
   free(circ);
 }
@@ -656,16 +658,15 @@
 
   circ = circuit_new(0, NULL); /* sets circ->p_circ_id and circ->p_conn */
   circ->state = CIRCUIT_STATE_OR_WAIT;
-  circ->desired_cpath_len = onion_new_route_len();
+  circ->build_state = onion_new_cpath_build_state();
   
-  if (circ->desired_cpath_len < 0) {
+  if (! circ->build_state) {
     log_fn(LOG_INFO,"Generating cpath length failed.");
     circuit_close(circ);
     return -1;
   }
 
-  onion_extend_cpath(&circ->cpath, circ->desired_cpath_len,
-                     &firsthop);
+  onion_extend_cpath(&circ->cpath, circ->build_state, &firsthop);
   if(!circ->cpath) {
     log_fn(LOG_INFO,"Generating first cpath hop failed.");
     circuit_close(circ);
@@ -766,11 +767,11 @@
     assert(circ->cpath->state == CPATH_STATE_OPEN);
     assert(circ->state == CIRCUIT_STATE_BUILDING);
     log_fn(LOG_DEBUG,"starting to send subsequent skin.");
-    r = onion_extend_cpath(&circ->cpath, circ->desired_cpath_len, &router);
+    r = onion_extend_cpath(&circ->cpath, circ->build_state, &router);
     if (r==1) {
       /* done building the circuit. whew. */
       circ->state = CIRCUIT_STATE_OPEN;
-      log_fn(LOG_INFO,"circuit built! (%d hops long)",circ->desired_cpath_len);
+      log_fn(LOG_INFO,"circuit built!");
       /* Tell any AP connections that have been waiting for a new
        * circuit that one is ready. */
       connection_ap_attach_pending();

Index: connection_edge.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection_edge.c,v
retrieving revision 1.53
retrieving revision 1.54
diff -u -d -r1.53 -r1.54
--- connection_edge.c	12 Nov 2003 04:24:04 -0000	1.53
+++ connection_edge.c	14 Nov 2003 20:45:47 -0000	1.54
@@ -3,6 +3,7 @@
 /* $Id$ */
 
 #include "or.h"
+#include "tree.h"
 
 extern or_options_t options; /* command-line and config-file options */
 
@@ -15,6 +16,10 @@
 static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
 static void connection_edge_consider_sending_sendme(connection_t *conn);
 
+static uint32_t client_dns_lookup_entry(const char *address);
+static void client_dns_set_entry(const char *address, uint32_t val);
+static void client_dns_clean(void);
+
 int connection_edge_process_inbuf(connection_t *conn) {
 
   assert(conn);
@@ -96,7 +101,6 @@
   payload[0] = reason;
   if(reason == END_STREAM_REASON_EXITPOLICY) {
     *(uint32_t *)(payload+1) = htonl(conn->addr);
-    *(uint16_t *)(payload+5) = htons(conn->port);
     payload_len += 6;
   }
 
@@ -163,6 +167,7 @@
                                        int edge_type, crypt_path_t *layer_hint) {
   int relay_command;
   static int num_seen=0;
+  uint32_t addr;
 
   assert(cell && circ);
 
@@ -241,19 +246,21 @@
           *(int*)conn->stream_id);
         return 0;
       }
-      log_fn(LOG_INFO,"end cell (%s) for stream %d. Removing stream.",
-        connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, cell->length),
-        *(int*)conn->stream_id);
-      if(cell->length && *(cell->payload+RELAY_HEADER_SIZE) ==
-                           END_STREAM_REASON_EXITPOLICY) {
+      if(cell->length-RELAY_HEADER_SIZE >= 5 && 
+         *(cell->payload+RELAY_HEADER_SIZE) == END_STREAM_REASON_EXITPOLICY) {
         /* No need to close the connection. We'll hold it open while
          * we try a new exit node.
          * cell->payload+RELAY_HEADER_SIZE+1 holds the addr and then
          * port of the destination. Which is good, because we've
          * forgotten it.
          */
-        /* XXX */
+        addr = ntohl(*cell->payload+RELAY_HEADER_SIZE+1);
+        client_dns_set_entry(conn->address, addr);
+        /* XXX Move state back into CIRCUIT_WAIT. */
       }
+      log_fn(LOG_INFO,"end cell (%s) for stream %d. Removing stream.",
+        connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, cell->length),
+        *(int*)conn->stream_id);
 
 #ifdef HALF_OPEN
       conn->done_sending = 1;
@@ -313,6 +320,10 @@
         break;
       }
       log_fn(LOG_INFO,"Connected! Notifying application.");
+      if (cell->length-RELAY_HEADER_SIZE == 4) {
+        addr = htonl(*(uint32_t*)(cell->payload + RELAY_HEADER_SIZE));
+        client_dns_set_entry(conn->socks_request->addr, addr);
+      }
       if(connection_ap_handshake_socks_reply(conn, NULL, 0, 1) < 0) {
         log_fn(LOG_INFO,"Writing to socks-speaking application failed. Closing.");
         connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
@@ -386,7 +397,7 @@
       connection_stop_writing(conn);
       return 0;
     default:
-      log_fn(LOG_WARN,"BUG: called in unexpected state.");
+      log_fn(LOG_WARN,"BUG: called in unexpected state: %d", conn->state);
       return -1;
   }
   return 0;
@@ -583,6 +594,8 @@
 {
   char payload[CELL_PAYLOAD_SIZE];
   int payload_len;
+  struct in_addr in;
+  const char *string_addr;
 
   assert(ap_conn->type == CONN_TYPE_AP);
   assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
@@ -592,10 +605,15 @@
   crypto_pseudo_rand(STREAM_ID_SIZE, ap_conn->stream_id);
   /* FIXME check for collisions */
 
+  in.s_addr = client_dns_lookup_entry(ap_conn->socks_request->addr);
+  string_addr = in.s_addr ? inet_ntoa(in) : NULL;
+
   memcpy(payload, ap_conn->stream_id, STREAM_ID_SIZE);
   payload_len = STREAM_ID_SIZE + 1 +
     snprintf(payload+STREAM_ID_SIZE,CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE-STREAM_ID_SIZE,
-             "%s:%d", ap_conn->socks_request->addr, ap_conn->socks_request->port);
+             "%s:%d", 
+             string_addr ? string_addr : ap_conn->socks_request->addr, 
+             ap_conn->socks_request->port);
 
   log_fn(LOG_DEBUG,"Sending relay cell to begin stream %d.",*(int *)ap_conn->stream_id);
 
@@ -606,6 +624,12 @@
   ap_conn->package_window = STREAMWINDOW_START;
   ap_conn->deliver_window = STREAMWINDOW_START;
   ap_conn->state = AP_CONN_STATE_OPEN;
+  /* XXX Right now, we rely on the socks client not to send us any data
+   * XXX until we've sent back a socks reply.  (If it does, we could wind
+   * XXX up packaging that data and sending it to the exit, then later having
+   * XXX the exit refuse us.)  
+   * XXX Perhaps we should grow an AP_CONN_STATE_CONNECTING state.
+   */
   log_fn(LOG_INFO,"Address/port sent, ap socket %d, n_circ_id %d",ap_conn->s,circ->n_circ_id);
   return;
 }
@@ -701,6 +725,7 @@
 }
 
 void connection_exit_connect(connection_t *conn) {
+  unsigned char connected_payload[4];
 
   if(router_compare_to_exit_policy(conn) < 0) {
     log_fn(LOG_INFO,"%s:%d failed exit policy. Closing.", conn->address, conn->port);
@@ -733,8 +758,137 @@
   connection_watch_events(conn, POLLIN);
 
   /* also, deliver a 'connected' cell back through the circuit. */
+  *((uint32_t*) connected_payload) = htonl(conn->addr);
   connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_CONNECTED,
-                               NULL, 0, conn->cpath_layer);
+                               connected_payload, 4, conn->cpath_layer);
+}
+
+/* ***** Client DNS code ***** */
+#define MAX_DNS_ENTRY_AGE 30*60
+
+/* XXX Perhaps this should get merged with the dns.c code somehow. */
+struct client_dns_entry {
+  SPLAY_ENTRY(client_dns_entry) node;
+  char *address;
+  uint32_t addr;
+  time_t expires;
+};      
+static int client_dns_size = 0;
+static SPLAY_HEAD(client_dns_tree, client_dns_entry) client_dns_root;
+
+static int compare_client_dns_entries(struct client_dns_entry *a,
+                                      struct client_dns_entry *b) 
+{
+  return strcasecmp(a->address, b->address);
+}
+
+static void client_dns_entry_free(struct client_dns_entry *ent)
+{
+  tor_free(ent->address);
+  tor_free(ent);
+}
+
+SPLAY_PROTOTYPE(client_dns_tree, client_dns_entry, node, compare_client_dns_entries);
+SPLAY_GENERATE(client_dns_tree, client_dns_entry, node, compare_client_dns_entries);
+
+void client_dns_init(void) {
+  SPLAY_INIT(&client_dns_root);
+  client_dns_size = 0;
+}
+
+static uint32_t client_dns_lookup_entry(const char *address)
+{
+  struct client_dns_entry *ent;
+  struct client_dns_entry search;
+  struct in_addr in;
+  time_t now;
+
+  assert(address);
+
+  if (inet_aton(address, &in)) {
+    log_fn(LOG_DEBUG, "Using static address %s", address);
+    return in.s_addr;
+  }
+  search.address = (char*)address;
+  ent = SPLAY_FIND(client_dns_tree, &client_dns_root, &search);
+  if (!ent) {
+    log_fn(LOG_DEBUG, "No entry found for address %s", address);
+    return 0;
+  } else {
+    now = time(NULL);
+    if (ent->expires < now) {
+      log_fn(LOG_DEBUG, "Expired entry found for address %s", address);
+      SPLAY_REMOVE(client_dns_tree, &client_dns_root, ent);
+      client_dns_entry_free(ent);
+      --client_dns_size;
+      return 0;
+    }
+    in.s_addr = ent->addr;
+    log_fn(LOG_DEBUG, "Found cached entry for address %s: %s", address,
+           inet_ntoa(in));
+    return ent->addr;
+  }
+}
+static void client_dns_set_entry(const char *address, uint32_t val)
+{
+  struct client_dns_entry *ent;
+  struct client_dns_entry search;
+  struct in_addr in;
+  time_t now;
+
+  assert(address);
+  assert(val);
+
+  if (inet_aton(address, &in)) {
+    if (in.s_addr == val)
+      return;
+    in.s_addr = val;
+    log_fn(LOG_WARN, 
+        "Trying to store incompatible cached value %s for static address %s",
+        inet_ntoa(in), address);
+    return;
+  }
+  search.address = (char*) address;
+  now = time(NULL);
+  ent = SPLAY_FIND(client_dns_tree, &client_dns_root, &search);
+  if (ent) {
+    log_fn(LOG_DEBUG, "Updating entry for address %s", address);
+    ent->addr = val;
+    ent->expires = now+MAX_DNS_ENTRY_AGE;
+  } else {
+    in.s_addr = val;
+    log_fn(LOG_DEBUG, "Caching result for address %s: %s", address,
+           inet_ntoa(in));
+    ent = tor_malloc(sizeof(struct client_dns_entry));
+    ent->address = tor_strdup(address);
+    ent->addr = val;
+    ent->expires = now+MAX_DNS_ENTRY_AGE;
+    SPLAY_INSERT(client_dns_tree, &client_dns_root, ent);
+    ++client_dns_size;
+  }
+}
+static void client_dns_clean()
+{
+  struct client_dns_entry **expired_entries;
+  int n_expired_entries = 0;
+  struct client_dns_entry *ent;
+  time_t now;
+  int i;
+
+  expired_entries = tor_malloc(client_dns_size * 
+                               sizeof(struct client_dns_entry *));   
+  
+  now = time(NULL);
+  SPLAY_FOREACH(ent, client_dns_tree, &client_dns_root) {
+    if (ent->expires < now) {
+      expired_entries[n_expired_entries++] = ent;
+    }
+  }
+  for (i = 0; i < n_expired_entries; ++i) {
+    SPLAY_REMOVE(client_dns_tree, &client_dns_root, expired_entries[i]);
+    client_dns_entry_free(expired_entries[i]);
+  }
+  tor_free(expired_entries);
 }
 
 /*
@@ -744,4 +898,3 @@
   c-basic-offset:2
   End:
 */
-

Index: onion.c
===================================================================
RCS file: /home/or/cvsroot/src/or/onion.c,v
retrieving revision 1.82
retrieving revision 1.83
diff -u -d -r1.82 -r1.83
--- onion.c	13 Nov 2003 23:01:55 -0000	1.82
+++ onion.c	14 Nov 2003 20:45:47 -0000	1.83
@@ -6,6 +6,10 @@
 
 extern or_options_t options; /* command-line and config-file options */
 
+struct cpath_build_state_t {
+  int desired_path_len;
+};
+
 static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len);
 
 int decide_circ_id_type(char *local_nick, char *remote_nick) {
@@ -219,11 +223,18 @@
   return routelen;
 }
 
-int onion_new_route_len(void) {
+cpath_build_state_t *onion_new_cpath_build_state(void) {
   directory_t *dir;
-
+  int r;
+  cpath_build_state_t *info;
+  
   router_get_directory(&dir);
-  return new_route_len(options.PathlenCoinWeight, dir->routers, dir->n_routers);
+  r = new_route_len(options.PathlenCoinWeight, dir->routers, dir->n_routers);
+  if (r < 0) 
+    return NULL;
+  info = tor_malloc(sizeof(cpath_build_state_t));
+  info->desired_path_len = r;
+  return info;
 }
 
 static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len) {
@@ -256,7 +267,7 @@
   return num;
 }
 
-int onion_extend_cpath(crypt_path_t **head_ptr, int path_len, routerinfo_t **router_out)
+int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, routerinfo_t **router_out)
 {
   int cur_len;
   crypt_path_t *cpath, *hop;
@@ -283,8 +294,13 @@
       ++cur_len;
     }
   }
-  if (cur_len >= path_len) { return 1; }
-  log_fn(LOG_DEBUG, "Path is %d long; we want %d", cur_len, path_len);
+  if (cur_len >= state->desired_path_len) { 
+    log_fn(LOG_DEBUG, "Path is complete: %d steps long", 
+           state->desired_path_len);
+    return 1; 
+  }
+  log_fn(LOG_DEBUG, "Path is %d long; we want %d", cur_len, 
+         state->desired_path_len);
 
  again:
   if(cur_len == 0) { /* picking entry node */
@@ -554,5 +570,3 @@
   c-basic-offset:2
   End:
 */
-
-

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/src/or/or.h,v
retrieving revision 1.184
retrieving revision 1.185
diff -u -d -r1.184 -r1.185
--- or.h	13 Nov 2003 23:01:56 -0000	1.184
+++ or.h	14 Nov 2003 20:45:47 -0000	1.185
@@ -248,6 +248,7 @@
 #define SET_CELL_RELAY_COMMAND(c,cmd) (*(uint8_t*)((c).payload) = (cmd))
 #define STREAM_ID_SIZE 7
 #define SET_CELL_STREAM_ID(c,id)      memcpy((c).payload+1,(id),STREAM_ID_SIZE)
+#define CELL_RELAY_COMMAND_END_REASON(c) (*(uint8_t)((c).payload+1))
 
 #define ZERO_STREAM "\0\0\0\0\0\0\0\0"
 
@@ -326,8 +327,9 @@
 struct exit_policy_t {
   char policy_type;
   char *string;
-  char *address;
-  char *port;
+  uint32_t addr;
+  uint32_t msk;
+  uint16_t prt;
 
   struct exit_policy_t *next;
 };
@@ -390,6 +392,8 @@
 
 typedef struct crypt_path_t crypt_path_t;
 
+typedef struct cpath_build_state_t cpath_build_state_t;
+
 /* struct for a path (circuit) through the network */
 struct circuit_t {
   uint32_t n_addr;
@@ -407,7 +411,7 @@
   crypto_cipher_env_t *p_crypto; /* used only for intermediate hops */
   crypto_cipher_env_t *n_crypto;
 
-  int desired_cpath_len;
+  cpath_build_state_t *build_state;
   crypt_path_t *cpath;
 
   char onionskin[DH_ONIONSKIN_LEN]; /* for storage while onionskin pending */
@@ -695,7 +699,8 @@
 
 char **parse_nickname_list(char *start, int *num);
 
-int onion_extend_cpath(crypt_path_t **head_ptr, int path_len, routerinfo_t **router_out);
+int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, 
+                       routerinfo_t **router_out);
 
 int onion_skin_create(crypto_pk_env_t *router_key,
                       crypto_dh_env_t **handshake_state_out,
@@ -712,7 +717,7 @@
                              char *key_out,
                              int key_out_len);
 
-int onion_new_route_len(void);
+cpath_build_state_t *onion_new_cpath_build_state(void);
 
 /********************************* routers.c ***************************/
 
@@ -741,6 +746,8 @@
                                     crypto_pk_env_t *pkey);
 routerinfo_t *router_get_entry_from_string(char **s);
 int router_compare_to_exit_policy(connection_t *conn);
+int router_compare_addr_to_exit_policy(uint32_t addr, uint16_t port,
+                                       struct exit_policy_t *policy);
 void routerinfo_free(routerinfo_t *router);
 int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
                                  crypto_pk_env_t *ident_key);

Index: routers.c
===================================================================
RCS file: /home/or/cvsroot/src/or/routers.c,v
retrieving revision 1.88
retrieving revision 1.89
diff -u -d -r1.88 -r1.89
--- routers.c	12 Nov 2003 19:34:34 -0000	1.88
+++ routers.c	14 Nov 2003 20:45:47 -0000	1.89
@@ -164,8 +164,6 @@
     e = router->exit_policy;
     router->exit_policy = e->next;
     tor_free(e->string);
-    tor_free(e->address);
-    tor_free(e->port);
     free(e);
   }
   free(router);
@@ -915,11 +913,12 @@
     e = strchr(s,',');
     if(!e) {
       last = 1;
-      strcpy(line,s);
+      strncpy(line,s,1023); 
     } else {
-      memcpy(line,s,e-s);
+      memcpy(line,s, ((e-s)<1023)?(e-s):1023); 
       line[e-s] = 0;
     }
+    line[1023]=0;
     log_fn(LOG_DEBUG,"Adding new entry '%s'",line);
     if(router_add_exit_policy_from_string(router,line) < 0)
       log_fn(LOG_WARN,"Malformed exit policy %s; skipping.", line);
@@ -963,7 +962,9 @@
 static int router_add_exit_policy(routerinfo_t *router, 
                                   directory_token_t *tok) {
   struct exit_policy_t *tmpe, *newe;
-  char *arg, *colon;
+  struct in_addr in;
+  char *arg, *address, *mask, *port, *endptr;
+  int bits;
 
   if (tok->val.cmd.n_args != 1)
     return -1;
@@ -982,17 +983,63 @@
     newe->policy_type = EXIT_POLICY_ACCEPT;
   }
   strcat(newe->string, arg);
-  
-  colon = strchr(arg,':');
-  if(!colon)
+
+  address = arg;
+  mask = strchr(arg,'/');
+  port = strchr(mask?mask:arg,':');
+  if(!port)
     goto policy_read_failed;
-  *colon = 0;
-  newe->address = tor_strdup(arg);
-  newe->port = tor_strdup(colon+1);
+  if (mask)
+    *mask++ = 0;
+  *port++ = 0;
 
-  log_fn(LOG_DEBUG,"%s %s:%s",
-      newe->policy_type == EXIT_POLICY_REJECT ? "reject" : "accept",
-      newe->address, newe->port);
+  if (strcmp(address, "*") == 0) {
+    newe->addr = 0;
+  } else if (inet_aton(address, &in) != 0) {
+    newe->addr = in.s_addr;
+  } else {
+    log_fn(LOG_WARN, "Malformed IP %s in exit policy; rejecting.",
+           address);
+    goto policy_read_failed;
+  }
+  if (!mask) {
+    if (strcmp(address, "*") == 0)
+      newe->msk = 0;
+    else
+      newe->msk = 0xFFFFFFFFu;
+  } else {
+    endptr = NULL;
+    bits = (int) strtol(mask, &endptr, 10);
+    if (!*endptr) {
+      /* strtol handled the whole mask. */
+      newe->msk = ~((1<<(32-bits))-1);
+    } else if (inet_aton(mask, &in) != 0) {
+      newe->msk = in.s_addr;
+    } else {
+      log_fn(LOG_WARN, "Malformed mask %s on exit policy; rejecting.",
+             mask);
+      goto policy_read_failed;
+    }
+  }
+  if (strcmp(port, "*") == 0) {
+    newe->prt = 0;
+  } else { 
+    endptr = NULL;
+    newe->prt = strtol(port, &endptr, 10);
+    if (*endptr) {
+      log_fn(LOG_WARN, "Malformed port %s on exit policy; rejecting.",
+             port);
+      goto policy_read_failed;
+    }
+  }
+
+  in.s_addr = newe->addr;
+  address = tor_strdup(inet_ntoa(in));
+  in.s_addr = newe->msk;
+  log_fn(LOG_DEBUG,"%s %s/%s:%d",
+         newe->policy_type == EXIT_POLICY_REJECT ? "reject" : "accept",
+         address, inet_ntoa(in), newe->prt);
+  tor_free(address);
 
   /* now link newe onto the end of exit_policy */
 
@@ -1010,44 +1057,75 @@
   assert(newe->string);
   log_fn(LOG_WARN,"Couldn't parse line '%s'. Dropping", newe->string);
   tor_free(newe->string);
-  tor_free(newe->address);
-  tor_free(newe->port);
   free(newe);
   return -1;
 }
 
-/* Return 0 if my exit policy says to allow connection to conn.
- * Else return -1.
+
+/* Addr is 0 for "IP unknown".
+ *
+ * Returns -1 for 'rejected', 0 for accepted, 1 for 'maybe' (since IP is
+ * unknown.
  */
-int router_compare_to_exit_policy(connection_t *conn) {
-  struct exit_policy_t *tmpe;
+int router_compare_addr_to_exit_policy(uint32_t addr, uint16_t port,
+                                       struct exit_policy_t *policy)
+{
+  int maybe_reject = 0;
+  int match = 0;
   struct in_addr in;
+  struct exit_policy_t *tmpe;
 
   assert(desc_routerinfo);
 
-  for(tmpe=desc_routerinfo->exit_policy; tmpe; tmpe=tmpe->next) {
-    assert(tmpe->address);
-    assert(tmpe->port);
-
-    log_fn(LOG_DEBUG,"Considering exit policy %s:%s",tmpe->address, tmpe->port);
-    if(strcmp(tmpe->address,"*") &&
-       inet_aton(tmpe->address,&in) == 0) { /* malformed IP. reject. */
-      log_fn(LOG_WARN,"Malformed IP %s in exit policy. Rejecting.",tmpe->address);
-      return -1;
+  for(tmpe=policy; tmpe; tmpe=tmpe->next) {
+    log_fn(LOG_DEBUG,"Considering exit policy %s", tmpe->string);
+    if (!addr) {
+      /* Address is unknown. */
+      if (tmpe->msk == 0 && port == tmpe->prt) {
+        /* The exit policy is accept/reject *:port */
+        match = 1;
+      } else if ((!tmpe->prt || port == tmpe->prt) && 
+                 tmpe->policy_type == EXIT_POLICY_REJECT) {
+        /* The exit policy is reject ???:port */
+        maybe_reject = 1;
+      }
+    } else {
+      /* Address is known */
+      if ( (addr & tmpe->msk) == (tmpe->addr & tmpe->msk) &&
+           (!tmpe->prt || port == tmpe->prt) ) {
+        /* Exact match for the policy */
+        match = 1;
+      }
     }
-    if((!strcmp(tmpe->address,"*") || conn->addr == ntohl(in.s_addr)) &&
-       (!strcmp(tmpe->port,"*") || atoi(tmpe->port) == conn->port)) {
-      log_fn(LOG_INFO,"Address '%s' matches '%s' and port '%s' matches '%d'. %s.",
-          tmpe->address, conn->address,
-          tmpe->port, conn->port,
-          tmpe->policy_type == EXIT_POLICY_ACCEPT ? "Accepting" : "Rejecting");
+    if (match) {
+      in.s_addr = addr;
+      log_fn(LOG_INFO,"Address %s:%d matches exit policy '%s'",
+             inet_ntoa(in), port, tmpe->string);
       if(tmpe->policy_type == EXIT_POLICY_ACCEPT)
         return 0;
       else
         return -1;
     }
   }
-  return 0; /* accept all by default. */
+  if (maybe_reject)
+    return 1;
+  else
+    return 0; /* accept all by default. */
+}
+
+/* Return 0 if my exit policy says to allow connection to conn.
+ * Else return -1.
+ */
+int router_compare_to_exit_policy(connection_t *conn) {
+  struct exit_policy_t *tmpe;
+
+  assert(desc_routerinfo);
+  
+  if (router_compare_addr_to_exit_policy(conn->addr, conn->port,
+                                         desc_routerinfo->exit_policy))
+    return -1;
+  else 
+    return 0;
 }
 
 const char *router_get_my_descriptor(void) {
@@ -1120,6 +1198,7 @@
   char *onion_pkey;
   char *link_pkey;
   char *identity_pkey;
+  struct in_addr in;
   char platform[256];
   char digest[20];
   char signature[128];
@@ -1187,14 +1266,32 @@
   written = result;
 
   for(tmpe=router->exit_policy; tmpe; tmpe=tmpe->next) {
-    result = snprintf(s+written, maxlen-written, "%s %s:%s\n", 
-      tmpe->policy_type == EXIT_POLICY_ACCEPT ? "accept" : "reject",
-      tmpe->address, tmpe->port);
+    in.s_addr = tmpe->addr;
+    result = snprintf(s+written, maxlen-written, "%s %s",
+        tmpe->policy_type == EXIT_POLICY_ACCEPT ? "accept" : "reject",
+        tmpe->msk == 0 ? "*" : inet_ntoa(in));
     if(result < 0 || result+written > maxlen) {
       /* apparently different glibcs do different things on snprintf error.. so check both */
       return -1;
     }
     written += result;
+    if (tmpe->msk != 0xFFFFFFFFu) {
+      in.s_addr = tmpe->msk;
+      result = snprintf(s+written, maxlen-written, "/%s", inet_ntoa(in));
+      if (result<0 || result+written > maxlen)
+        return -1;
+      written += result;
+    }
+    if (tmpe->prt) {
+      result = snprintf(s+written, maxlen-written, ":%d", tmpe->prt);
+      if (result<0 || result+written > maxlen)
+        return -1;
+      written += result;
+    } else {
+      if (written > maxlen-3)
+        return -1;
+      strcat(s+written, ":*");
+    }
   }
   if (written > maxlen-256) /* Not enough room for signature. */
     return -1;

Index: test.c
===================================================================
RCS file: /home/or/cvsroot/src/or/test.c,v
retrieving revision 1.49
retrieving revision 1.50
diff -u -d -r1.49 -r1.50
--- test.c	13 Nov 2003 06:49:25 -0000	1.49
+++ test.c	14 Nov 2003 20:45:47 -0000	1.50
@@ -558,12 +558,14 @@
 
   ex1.policy_type = EXIT_POLICY_ACCEPT;
   ex1.string = NULL;
-  ex1.address = "*";
-  ex1.port = "80";
+  ex1.addr = 0;
+  ex1.msk = 0;
+  ex1.prt = 80;
   ex1.next = &ex2;
   ex2.policy_type = EXIT_POLICY_REJECT;
-  ex2.address = "18.*";
-  ex2.port = "24";
+  ex2.addr = 18 << 24;
+  ex2.msk = 0xFF000000u;
+  ex2.prt = 24;
   ex2.next = NULL;
   r2.address = "tor.tor.tor";
   r2.addr = 0x0a030201u; /* 10.3.2.1 */



More information about the tor-commits mailing list