Explorar el Código

rudimentary dns caching (of both resolves and resolve failures)
serious performance increase over non-caching


svn:r158

Roger Dingledine hace 22 años
padre
commit
3cf02a88f4
Se han modificado 6 ficheros con 182 adiciones y 96 borrados
  1. 5 5
      TODO
  2. 1 1
      src/or/connection_exit.c
  3. 172 67
      src/or/dns.c
  4. 0 18
      src/or/main.c
  5. 3 2
      src/or/onion.c
  6. 1 3
      src/or/or.h

+ 5 - 5
TODO

@@ -32,11 +32,11 @@ SPEC    - Spec not finalized
                 o Distribute queries onto the farm, get answers
                 o Preemptively grow a new worker before he's needed
                 - Prune workers when too many are idle
-                - Keep track of which connections are in dns_wait
-                - Need to cache positives/negatives on the tor side
-                        - Keep track of which queries have been asked
-                - Better error handling when
-                        - An address doesn't resolve
+                o Keep track of which connections are in dns_wait
+                o Need to cache positives/negatives on the tor side
+                        o Keep track of which queries have been asked
+                . Better error handling when
+                        . An address doesn't resolve
                         - We have max workers running
         . Directory servers
                 - Automated reputation management

+ 1 - 1
src/or/connection_exit.c

@@ -150,7 +150,7 @@ int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) {
   circ->n_conn = n_conn;
 
   /* send it off to the gethostbyname farm */
-  if(dns_tor_to_master(n_conn) < 0) {
+  if(dns_resolve(n_conn) < 0) {
     log(LOG_DEBUG,"connection_exit_begin_conn(): Couldn't queue resolve request.");
     connection_remove(n_conn);
     connection_free(n_conn);

+ 172 - 67
src/or/dns.c

@@ -4,6 +4,8 @@
 
 #include "or.h"
 
+#define MAX_ADDRESSLEN 256
+
 #define MAX_DNSSLAVES 50
 #define MIN_DNSSLAVES 3 /* 1 for the tor process, 3 slaves */
 
@@ -11,9 +13,9 @@ struct slave_data_t {
   int fd; /* socket to talk on */
   int num_processed; /* number of times we've used this slave */
   char busy; /* whether this slave currently has a task */
-  char question[256]; /* the hostname that we're resolving */
+  char question[MAX_ADDRESSLEN]; /* the hostname that we're resolving */
   unsigned char question_len; /* how many bytes in question */
-  char answer[256]; /* the answer to the question */
+  char answer[MAX_ADDRESSLEN]; /* the answer to the question */
   unsigned char answer_len; /* how many bytes in answer */
 };
 
@@ -29,6 +31,8 @@ static int dns_find_idle_slave(int max);
 static int dns_assign_to_slave(int from, int to);
 static int dns_master_to_tor(int from, int to);
 static void dns_master_main(int fd);
+static int dns_tor_to_master(connection_t *exitconn);
+static int dns_found_answer(char *question, uint32_t answer, uint32_t valid);
 
 int connection_dns_finished_flushing(connection_t *conn) {
 
@@ -41,9 +45,9 @@ int connection_dns_finished_flushing(connection_t *conn) {
 
 int connection_dns_process_inbuf(connection_t *conn) {
   unsigned char length;
-  char buf[256];
+  char buf[MAX_ADDRESSLEN];
   char *question;
-  connection_t *exitconn;
+  uint32_t answer;
 
   assert(conn && conn->type == CONN_TYPE_DNSMASTER);
   assert(conn->state == DNSMASTER_STATE_OPEN);
@@ -75,25 +79,12 @@ int connection_dns_process_inbuf(connection_t *conn) {
   question = buf+1;
   log(LOG_DEBUG,"connection_dns_process_inbuf(): length %d, question '%s', strlen question %d", length, question, strlen(question));
   assert(length == 4 + strlen(question) + 1);
-  
-  /* find the conn that question refers to. */
-  exitconn = connection_get_pendingresolve_by_address(question);
-
-  if(!exitconn) {
-    log(LOG_DEBUG,"connection_dns_process_inbuf(): No conn -- question no longer relevant? Dropping.");
-    return connection_process_inbuf(conn); /* process the remainder of the buffer */
-  }
-  memcpy((char *)&exitconn->addr, buf+1+length-4,4);
-  exitconn->addr = ntohl(exitconn->addr); /* get it back to host order */
-
-  if(connection_exit_connect(exitconn) < 0) {
-    exitconn->marked_for_close = 1;
-  }
 
+  answer = *(uint32_t *)(buf+1+length-4);
+  dns_found_answer(question, answer, (answer != 0));
   return connection_process_inbuf(conn); /* process the remainder of the buffer */
 }
 
-
 /* return -1 if error, else the fd that can talk to the dns master */
 int dns_master_start(void) {
   connection_t *conn;
@@ -149,7 +140,7 @@ int dns_master_start(void) {
 }
 
 static void dns_slave_main(int fd) {
-  char question[256];
+  char question[MAX_ADDRESSLEN];
   unsigned char question_len;
   struct hostent *rent;
 
@@ -316,7 +307,7 @@ static int dns_assign_to_slave(int from, int to) {
 }
 
 static int dns_master_to_tor(int from, int to) {
-  char tmp[256];
+  char tmp[MAX_ADDRESSLEN*2];
   unsigned char len;
 
   len = slave_data[from].question_len+1+slave_data[from].answer_len;
@@ -339,52 +330,6 @@ static int dns_master_to_tor(int from, int to) {
   return 0;
 }
 
-int dns_tor_to_master(connection_t *exitconn) {
-  connection_t *conn;
-  unsigned char len;
-
-#ifdef DO_DNS_DIRECTLY
-  /* new version which does it all right here */
-  struct hostent *rent;
-  rent = gethostbyname(exitconn->address);
-  if (!rent) {
-    return -1;
-  }
-
-  memcpy((char *)&exitconn->addr, rent->h_addr, rent->h_length);
-  exitconn->addr = ntohl(exitconn->addr); /* get it back to host order */
-
-  if(connection_exit_connect(exitconn) < 0) {
-    exitconn->marked_for_close = 1;
-  }
-  return 0;
-#endif
-
-
-
-  /* old version which actually uses the dns farm */
-  conn = connection_get_by_type(CONN_TYPE_DNSMASTER);
-  if(!conn) {
-    log(LOG_ERR,"dns_tor_to_master(): dns master nowhere to be found!");
-    /* XXX should do gethostbyname right here */
-    return -1;
-  }
-
-  len = strlen(exitconn->address);
-  if(connection_write_to_buf(&len, 1, conn) < 0) {
-    log(LOG_DEBUG,"dns_tor_to_master(): Couldn't write length.");
-    return -1;
-  }
-
-  if(connection_write_to_buf(exitconn->address, len, conn) < 0) {
-    log(LOG_DEBUG,"dns_tor_to_master(): Couldn't write address.");
-    return -1;
-  }
-
-//  log(LOG_DEBUG,"dns_tor_to_master(): submitted '%s'", address);
-  return 0;
-}
-
 static void dns_master_main(int fd) {
   int nfds=1; /* the 0th index is the tor process, the rest are slaves */
   int num_slaves_busy=0;
@@ -455,3 +400,163 @@ static void dns_master_main(int fd) {
   assert(0); /* should never get here */
 }
 
+
+
+#include "tree.h"
+
+struct pending_connection_t {
+  struct connection_t *conn;
+  struct pending_connection_t *next;
+};
+
+struct cached_resolve {
+  SPLAY_ENTRY(cached_resolve) node;
+  char question[MAX_ADDRESSLEN]; /* the hostname to be resolved */
+  uint32_t answer; /* in host order. I know I'm horrible for assuming ipv4 */
+  char state; /* 0 is pending; 1 means answer is valid; 2 means resolve failed */
+#define CACHE_STATE_PENDING 0
+#define CACHE_STATE_VALID 1
+#define CACHE_STATE_FAILED 2
+  uint32_t expire; /* remove untouched items from cache after some time? */
+  struct pending_connection_t *pending_connections;
+  struct cached_resolve *next;
+};
+
+SPLAY_HEAD(cache_tree, cached_resolve) cache_root;
+
+static int compare_cached_resolves(struct cached_resolve *a, struct cached_resolve *b) {
+  /* make this smarter one day? */
+  return strncasecmp(a->question, b->question, MAX_ADDRESSLEN);
+}
+
+SPLAY_PROTOTYPE(cache_tree, cached_resolve, node, compare_cached_resolves);
+SPLAY_GENERATE(cache_tree, cached_resolve, node, compare_cached_resolves);
+
+void init_cache_tree(void) {
+  SPLAY_INIT(&cache_root);
+}
+
+
+/* see if the question 'exitconn->address' has been answered. if so,
+ * if resolve valid, put it into exitconn->addr and call
+ * connection_exit_connect directly. If resolve failed, return -1.
+ *
+ * Else, if seen before and pending, add conn to the pending list,
+ * and return 0.
+ *
+ * Else, if not seen before, add conn to pending list, hand to
+ * dns farm, and return 0.
+ */
+int dns_resolve(connection_t *exitconn) {
+  struct cached_resolve *new_resolve;
+  struct cached_resolve *resolve;
+  struct pending_connection_t *pending_connection;
+
+  new_resolve = malloc(sizeof(struct cached_resolve));
+  memset(new_resolve, 0, sizeof(struct cached_resolve));
+  strncpy(new_resolve->question, exitconn->address, MAX_ADDRESSLEN);
+
+  /* try adding it to the tree. if it's already there it will
+   * return it. */
+  resolve = SPLAY_INSERT(cache_tree, &cache_root, new_resolve);
+  if(resolve) { /* already there. free up new_resolve */
+    free(new_resolve);
+    switch(resolve->state) {
+      case CACHE_STATE_PENDING:
+        /* add us to the pending list */
+        pending_connection = malloc(sizeof(struct pending_connection_t));
+        pending_connection->conn = exitconn;
+        pending_connection->next = new_resolve->pending_connections;
+        new_resolve->pending_connections = pending_connection;
+
+        return dns_tor_to_master(exitconn);
+      case CACHE_STATE_VALID:
+        exitconn->addr = resolve->answer;
+        return connection_exit_connect(exitconn);
+      case CACHE_STATE_FAILED:
+        return -1;
+    }
+  } else { /* this was newly added to the tree. ask the dns farm. */
+    new_resolve->state = CACHE_STATE_PENDING;
+
+    /* add us to the pending list */
+    pending_connection = malloc(sizeof(struct pending_connection_t));
+    pending_connection->conn = exitconn;
+    pending_connection->next = new_resolve->pending_connections;
+    new_resolve->pending_connections = pending_connection;
+
+    return dns_tor_to_master(exitconn);
+  }
+
+  assert(0);
+  return 0; /* not reached; keep gcc happy */
+}
+
+static int dns_tor_to_master(connection_t *exitconn) {
+  connection_t *dnsconn;
+  unsigned char len;
+  int do_dns_directly=0;
+
+  dnsconn = connection_get_by_type(CONN_TYPE_DNSMASTER);
+
+  if(!dnsconn) {
+    log(LOG_ERR,"dns_tor_to_master(): dns master nowhere to be found!");
+  }
+
+  if(!dnsconn || do_dns_directly) {
+    /* new version which does it all right here */
+    struct hostent *rent;
+    rent = gethostbyname(exitconn->address);
+    if (!rent) {
+      return dns_found_answer(exitconn->address, 0, 0);
+    }
+    return dns_found_answer(exitconn->address, *(uint32_t *)rent->h_addr, 1);
+  }
+
+  len = strlen(exitconn->address);
+  if(connection_write_to_buf(&len, 1, dnsconn) < 0) {
+    log(LOG_DEBUG,"dns_tor_to_master(): Couldn't write length.");
+    return -1;
+  }
+
+  if(connection_write_to_buf(exitconn->address, len, dnsconn) < 0) {
+    log(LOG_DEBUG,"dns_tor_to_master(): Couldn't write address.");
+    return -1;
+  }
+
+//  log(LOG_DEBUG,"dns_tor_to_master(): submitted '%s'", address);
+  return 0;
+}
+
+static int dns_found_answer(char *question, uint32_t answer, uint32_t valid) {
+  struct pending_connection_t *pend;
+  struct cached_resolve search;
+  struct cached_resolve *resolve;
+
+  strncpy(search.question, question, MAX_ADDRESSLEN);
+
+  resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
+  if(!resolve) {
+    log(LOG_ERR,"dns_found_answer(): Answer to unasked question '%s'? Dropping.", question);
+    return 0;
+  }
+
+  assert(resolve->state == CACHE_STATE_PENDING);
+
+  if(valid)
+    resolve->state = CACHE_STATE_VALID;
+  else
+    resolve->state = CACHE_STATE_FAILED;
+
+  while(resolve->pending_connections) {
+    pend = resolve->pending_connections;
+    pend->conn->addr = ntohl(answer);
+    if(resolve->state == CACHE_STATE_FAILED || connection_exit_connect(pend->conn) < 0) {
+      pend->conn->marked_for_close = 1;
+    }
+    resolve->pending_connections = pend->next;
+    free(pend);
+  }
+  return 0;
+}
+

+ 0 - 18
src/or/main.c

@@ -159,24 +159,6 @@ connection_t *connection_get_by_type(int type) {
   return NULL;
 }
 
-connection_t *connection_get_pendingresolve_by_address(char *address) {
-  int i;
-  connection_t *conn;
-
-  for(i=0;i<nfds;i++) {
-    conn = connection_array[i];
-    if(conn->type == CONN_TYPE_EXIT &&
-       conn->state == EXIT_CONN_STATE_RESOLVING &&
-       !strcmp(conn->address, address)) {
-         return conn;
-    }
-  }
-  return NULL;
-}
-
-
-
-
 void connection_watch_events(connection_t *conn, short events) {
 
   assert(conn && conn->poll_index < nfds);

+ 3 - 2
src/or/onion.c

@@ -9,6 +9,7 @@ extern or_options_t options; /* command-line and config-file options */
 
 static int onion_process(circuit_t *circ);
 static int onion_deliver_to_conn(aci_t aci, unsigned char *onion, uint32_t onionlen, connection_t *conn);
+static int find_tracked_onion(unsigned char *onion, uint32_t onionlen);
 
 int decide_aci_type(uint32_t local_addr, uint16_t local_port,
                     uint32_t remote_addr, uint16_t remote_port) {
@@ -756,7 +757,7 @@ void init_tracked_tree(void) {
 /* see if this onion has been seen before. if so, return 1, else
  * return 0 and add the sha1 of this onion to the tree.
  */
-int find_tracked_onion(unsigned char *onion, uint32_t onionlen) {
+static int find_tracked_onion(unsigned char *onion, uint32_t onionlen) {
   static struct tracked_onion *head_tracked_onions = NULL; /* linked list of tracked onions */
   static struct tracked_onion *tail_tracked_onions = NULL;
 
@@ -764,7 +765,7 @@ int find_tracked_onion(unsigned char *onion, uint32_t onionlen) {
   struct tracked_onion *to;
 
   /* first take this opportunity to see if there are any expired
-   * onions in the tree. we know this in O(1) because the linked list
+   * onions in the tree. we know this is fast because the linked list
    * 'tracked_onions' is ordered by when they were seen.
    */
   while(head_tracked_onions && (head_tracked_onions->expire < now)) {

+ 1 - 3
src/or/or.h

@@ -650,7 +650,7 @@ int connection_dir_handle_listener_read(connection_t *conn);
 
 int connection_dns_finished_flushing(connection_t *conn);
 int connection_dns_process_inbuf(connection_t *conn);
-int dns_tor_to_master(connection_t *exitconn);
+int dns_resolve(connection_t *exitconn);
 int dns_master_start(void);
 
 /********************************* main.c ***************************/
@@ -665,7 +665,6 @@ connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port);
 connection_t *connection_exact_get_by_addr_port(uint32_t addr, uint16_t port);
 
 connection_t *connection_get_by_type(int type);
-connection_t *connection_get_pendingresolve_by_address(char *address);
 
 void connection_watch_events(connection_t *conn, short events);
 void connection_stop_reading(connection_t *conn);
@@ -724,7 +723,6 @@ int decrypt_onion(unsigned char *onion, uint32_t onionlen, crypto_pk_env_t *prke
 void pad_onion(unsigned char *onion, uint32_t onionlen, int n);
 
 void init_tracked_tree(void);
-int find_tracked_onion(unsigned char *onion, uint32_t onionlen);
 
 /********************************* routers.c ***************************/