[or-cvs] entries in the dns cache now expire

Roger Dingledine arma at seul.org
Wed Jun 25 07:19:32 UTC 2003


Update of /home/or/cvsroot/src/or
In directory moria.mit.edu:/home/arma/work/onion/cvs/src/or

Modified Files:
	dns.c main.c or.h 
Log Message:
entries in the dns cache now expire
  (expiry time set to 100 seconds so we can play with it)
exit connections are now informed when pending resolves fail
we kill off the oldest busy worker when we're under attack and need to
  resolve something new



Index: dns.c
===================================================================
RCS file: /home/or/cvsroot/src/or/dns.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- dns.c	25 Jun 2003 00:31:41 -0000	1.14
+++ dns.c	25 Jun 2003 07:19:30 -0000	1.15
@@ -20,7 +20,8 @@
 int num_workers_busy=0;
 
 static int dns_assign_to_worker(connection_t *exitconn);
-static int dns_found_answer(char *question, uint32_t answer);
+static void dns_cancel_pending_resolve(char *question);
+static void dns_found_answer(char *question, uint32_t answer);
 static void dnsworker_main(int fd);
 static int dns_spawn_worker(void);
 static void spawn_enough_workers(void);
@@ -62,6 +63,9 @@
   spawn_enough_workers();
 }
 
+static struct cached_resolve *oldest_cached_resolve = NULL; /* linked list, */
+static struct cached_resolve *newest_cached_resolve = NULL; /* oldest to newest */
+
 /* See if the question 'exitconn->address' has been answered. if so,
  * if resolve valid, put it into exitconn->addr and exec to
  * connection_exit_connect. If resolve failed, return -1.
@@ -76,10 +80,24 @@
   struct cached_resolve *resolve;
   struct cached_resolve search;
   struct pending_connection_t *pending_connection;
+  uint32_t now = time(NULL);
 
-  strncpy(search.question, exitconn->address, MAX_ADDRESSLEN);
+  /* first take this opportunity to see if there are any expired
+   * resolves in the tree. this is fast because the linked list
+   * oldest_cached_resolve is ordered by when they came in.
+   */
+  while(oldest_cached_resolve && (oldest_cached_resolve->expire < now)) {
+    resolve = oldest_cached_resolve;
+    log(LOG_DEBUG,"Forgetting old cached resolve (expires %d)", resolve->expire);
+    oldest_cached_resolve = resolve->next;
+    if(!oldest_cached_resolve) /* if there are no more, */
+      newest_cached_resolve = NULL; /* then make sure the list's tail knows that too */
+    SPLAY_REMOVE(cache_tree, &cache_root, resolve);
+    free(resolve);
+  }
 
-  /* check the tree to see if 'question' is already there. */
+  /* now check the tree to see if 'question' is already there. */
+  strncpy(search.question, exitconn->address, MAX_ADDRESSLEN);
   resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
   if(resolve) { /* already there */
     switch(resolve->state) {
@@ -100,6 +118,8 @@
     resolve = tor_malloc(sizeof(struct cached_resolve));
     memset(resolve, 0, sizeof(struct cached_resolve));
     resolve->state = CACHE_STATE_PENDING;
+    resolve->expire = now + 100; /* XXX for testing. when we're confident, switch it back */
+//    resolve->expire = now + 86400; /* now + 1 day */
     strncpy(resolve->question, exitconn->address, MAX_ADDRESSLEN);
 
     /* add us to the pending list */
@@ -108,6 +128,14 @@
     pending_connection->next = resolve->pending_connections;
     resolve->pending_connections = pending_connection;
 
+    /* add us to the linked list of resolves */
+    if (!oldest_cached_resolve) {
+      oldest_cached_resolve = resolve;
+    } else {
+      newest_cached_resolve->next = resolve;
+    }
+    newest_cached_resolve = resolve;
+
     SPLAY_INSERT(cache_tree, &cache_root, resolve);
     return dns_assign_to_worker(exitconn);
   }
@@ -126,6 +154,7 @@
 
   if(!dnsconn) {
     log(LOG_INFO,"dns_assign_to_worker(): no idle dns workers. Failing.");
+    dns_cancel_pending_resolve(exitconn->address);
     return -1;
   }
 
@@ -139,6 +168,7 @@
      connection_write_to_buf(dnsconn->address, len, dnsconn) < 0) {
     log(LOG_NOTICE,"dns_assign_to_worker(): Write failed. Closing worker and failing resolve.");
     dnsconn->marked_for_close = 1;
+    dns_cancel_pending_resolve(exitconn->address);
     return -1;
   }
 
@@ -146,7 +176,51 @@
   return 0;
 }
 
-static int dns_found_answer(char *question, uint32_t answer) {
+static void dns_cancel_pending_resolve(char *question) {
+  struct pending_connection_t *pend;
+  struct cached_resolve search;
+  struct cached_resolve *resolve, *tmp;
+
+  strncpy(search.question, question, MAX_ADDRESSLEN);
+
+  resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
+  if(!resolve) {
+    log_fn(LOG_ERR,"Answer to unasked question '%s'? Dropping.", question);
+    return;
+  }
+
+  assert(resolve->state == CACHE_STATE_PENDING);
+
+  /* mark all pending connections to fail */
+  while(resolve->pending_connections) {
+    pend = resolve->pending_connections;
+    pend->conn->marked_for_close = 1;
+    resolve->pending_connections = pend->next;
+    free(pend);
+  }
+
+  /* remove resolve from the linked list */
+  if(resolve == oldest_cached_resolve) {
+    oldest_cached_resolve = resolve->next;
+    if(oldest_cached_resolve == NULL)
+      newest_cached_resolve = NULL;
+  } else {
+    /* FFFF make it a doubly linked list if this becomes too slow */
+    for(tmp=oldest_cached_resolve; tmp && tmp->next != resolve; tmp=tmp->next) ;
+    assert(tmp); /* it's got to be in the list, or we screwed up somewhere else */
+    tmp->next = resolve->next; /* unlink it */
+
+    if(newest_cached_resolve == resolve)
+      newest_cached_resolve = tmp;
+  }
+
+  /* remove resolve from the tree */
+  SPLAY_REMOVE(cache_tree, &cache_root, resolve);
+
+  free(resolve);
+}
+
+static void dns_found_answer(char *question, uint32_t answer) {
   struct pending_connection_t *pend;
   struct cached_resolve search;
   struct cached_resolve *resolve;
@@ -155,8 +229,8 @@
 
   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;
+    log_fn(LOG_ERR,"Answer to unasked question '%s'? Dropping.", question);
+    return;
   }
 
   assert(resolve->state == CACHE_STATE_PENDING);
@@ -166,7 +240,7 @@
    */
   if(resolve->state != CACHE_STATE_PENDING) {
     log(LOG_ERR,"dns_found_answer(): BUG: resolve '%s' in state %d (not pending). Dropping.",question, resolve->state);
-    return 0;
+    return;
   }
 
   resolve->answer = ntohl(answer);
@@ -184,7 +258,6 @@
     resolve->pending_connections = pend->next;
     free(pend);
   }
-  return 0;
 }
 
 /******************************************************************/
@@ -202,7 +275,8 @@
 
   if(conn->inbuf_reached_eof) {
     log(LOG_ERR,"connection_dnsworker_process_inbuf(): Read eof. Worker dying.");
-    /* XXX if the dns request is pending, go through and either repeat or mark it failed */
+    if(conn->state == DNSWORKER_STATE_BUSY)
+      dns_cancel_pending_resolve(conn->address);
     return -1;
   }
 
@@ -320,16 +394,20 @@
     /* We always want at least one worker idle.
      * So find the oldest busy worker and kill it.
      */
+    dnsconn = connection_get_by_type_state_lastwritten(CONN_TYPE_DNSWORKER, DNSWORKER_STATE_BUSY);
+    assert(dnsconn);
+
+    /* tell the exit connection that it's failed */
+    dns_cancel_pending_resolve(dnsconn->address);
 
+    dnsconn->marked_for_close = 1;
+    num_workers_busy--;
   }
 
   if(num_workers_busy >= MIN_DNSWORKERS)
     num_workers_needed = num_workers_busy+1;
   else
     num_workers_needed = MIN_DNSWORKERS;
-
-  if(num_workers_needed >= MAX_DNSWORKERS)
-    num_workers_needed = MAX_DNSWORKERS;
 
   while(num_workers < num_workers_needed) {
     if(dns_spawn_worker() < 0) {

Index: main.c
===================================================================
RCS file: /home/or/cvsroot/src/or/main.c,v
retrieving revision 1.73
retrieving revision 1.74
diff -u -d -r1.73 -r1.74
--- main.c	24 Jun 2003 23:14:39 -0000	1.73
+++ main.c	25 Jun 2003 07:19:30 -0000	1.74
@@ -151,9 +151,8 @@
 
   for(i=0;i<nfds;i++) {
     conn = connection_array[i];
-    assert(conn);
     if(conn->addr == addr && conn->port == port && !conn->marked_for_close)
-       return conn;
+      return conn;
   }
   return NULL;
 }
@@ -165,7 +164,7 @@
   for(i=0;i<nfds;i++) {
     conn = connection_array[i];
     if(conn->type == type && !conn->marked_for_close)
-       return conn;
+      return conn;
   }
   return NULL;
 }
@@ -177,9 +176,22 @@
   for(i=0;i<nfds;i++) {
     conn = connection_array[i];
     if(conn->type == type && conn->state == state && !conn->marked_for_close)
-       return conn;
+      return conn;
   }
   return NULL;
+}
+
+connection_t *connection_get_by_type_state_lastwritten(int type, int state) {
+  int i;
+  connection_t *conn, *best=NULL;
+
+  for(i=0;i<nfds;i++) {
+    conn = connection_array[i];
+    if(conn->type == type && conn->state == state && !conn->marked_for_close)
+      if(!best || conn->timestamp_lastwritten < best->timestamp_lastwritten)
+        best = conn;
+  }
+  return best;
 }
 
 void connection_watch_events(connection_t *conn, short events) {

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/src/or/or.h,v
retrieving revision 1.94
retrieving revision 1.95
diff -u -d -r1.94 -r1.95
--- or.h	21 Jun 2003 19:29:32 -0000	1.94
+++ or.h	25 Jun 2003 07:19:30 -0000	1.95
@@ -635,11 +635,10 @@
 
 /********************************* dns.c ***************************/
 
+void dns_init(void);
 int connection_dns_finished_flushing(connection_t *conn);
 int connection_dns_process_inbuf(connection_t *conn);
-void dns_init(void);
 int dns_resolve(connection_t *exitconn);
-int dns_master_start(void);
 
 /********************************* main.c ***************************/
 
@@ -656,6 +655,7 @@
 
 connection_t *connection_get_by_type(int type);
 connection_t *connection_get_by_type_state(int type, int state);
+connection_t *connection_get_by_type_state_lastwritten(int type, int state);
 
 void connection_watch_events(connection_t *conn, short events);
 void connection_stop_reading(connection_t *conn);



More information about the tor-commits mailing list