diff options
author | Roger Dingledine <arma@torproject.org> | 2003-06-25 07:19:30 +0000 |
---|---|---|
committer | Roger Dingledine <arma@torproject.org> | 2003-06-25 07:19:30 +0000 |
commit | 5d13db862e4ef827bf7b3bc4042ce2f6dfdd3ad2 (patch) | |
tree | 8ae6b895dc4e8203871d811045474a5ab9f98137 /src/or/dns.c | |
parent | 6c9e3762b56f1382a6f789a0cd22e32a2fe640ba (diff) | |
download | tor-5d13db862e4ef827bf7b3bc4042ce2f6dfdd3ad2.tar tor-5d13db862e4ef827bf7b3bc4042ce2f6dfdd3ad2.tar.gz |
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
svn:r356
Diffstat (limited to 'src/or/dns.c')
-rw-r--r-- | src/or/dns.c | 102 |
1 files changed, 90 insertions, 12 deletions
diff --git a/src/or/dns.c b/src/or/dns.c index 5a66f8342..16c55ee6f 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -20,7 +20,8 @@ int num_workers=0; 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 @@ void dns_init(void) { 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 @@ int dns_resolve(connection_t *exitconn) { 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 @@ int dns_resolve(connection_t *exitconn) { 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 @@ int dns_resolve(connection_t *exitconn) { 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 @@ static int dns_assign_to_worker(connection_t *exitconn) { 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 @@ static int dns_assign_to_worker(connection_t *exitconn) { 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 @@ static int dns_assign_to_worker(connection_t *exitconn) { 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 @@ static int dns_found_answer(char *question, uint32_t answer) { 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 @@ static int dns_found_answer(char *question, uint32_t answer) { */ 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 @@ static int dns_found_answer(char *question, uint32_t answer) { resolve->pending_connections = pend->next; free(pend); } - return 0; } /******************************************************************/ @@ -202,7 +275,8 @@ int connection_dns_process_inbuf(connection_t *conn) { 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,7 +394,14 @@ static void spawn_enough_workers(void) { /* 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) @@ -328,9 +409,6 @@ static void spawn_enough_workers(void) { 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) { log(LOG_ERR,"spawn_enough_workers(): spawn failed!"); |