diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 354ab8f0b953bd95aa91e25540b58b9fe8e37acc..acfa52dfe51b9de83ccd0d4c6be9f418195ca887 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -91,12 +91,13 @@ int connection_edge_update_circuit_isolation(const entry_connection_t *conn,
                                              int dry_run);
 void circuit_clear_isolation(origin_circuit_t *circ);
 
-#ifdef CONNECTION_EDGE_PRIVATE
-
+/* DOCDOC*/
 #define BEGIN_FLAG_IPV6_OK        (1u<<0)
 #define BEGIN_FLAG_IPV4_NOT_OK    (1u<<1)
 #define BEGIN_FLAG_IPV6_PREFERRED (1u<<2)
 
+#ifdef CONNECTION_EDGE_PRIVATE
+
 /*DOCDOC*/
 typedef struct begin_cell_t {
   char *address;
diff --git a/src/or/dns.c b/src/or/dns.c
index edcd5bf67f71623c39a492c17349bfdf7848182b..0758279471a8c11da2ff796bc5985a00b54deff7 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -117,7 +117,7 @@ typedef struct pending_connection_t {
 /* Possible states for a cached resolve_t */
 /** We are waiting for the resolver system to tell us an answer here.
  * When we get one, or when we time out, the state of this cached_resolve_t
- * will become "DONE" and we'll possibly add a CACHED_VALID or a CACHED_FAILED
+ * will become "DONE" and we'll possibly add a CACHED
  * entry. This cached_resolve_t will be in the hash table so that we will
  * know not to launch more requests for this addr, but rather to add more
  * connections to the pending list for the addr. */
@@ -128,10 +128,12 @@ typedef struct pending_connection_t {
 #define CACHE_STATE_DONE 1
 /** We are caching an answer for this address. This should have no pending
  * connections, and should appear in the hash table. */
-#define CACHE_STATE_CACHED_VALID 2
-/** We are caching a failure for this address. This should have no pending
- * connections, and should appear in the hash table */
-#define CACHE_STATE_CACHED_FAILED 3
+#define CACHE_STATE_CACHED 2
+
+/* DOCDOC */
+#define RES_STATUS_INFLIGHT 1
+#define RES_STATUS_DONE_OK 2
+#define RES_STATUS_DONE_ERR 3
 
 /** A DNS request: possibly completed, possibly pending; cached_resolve
  * structs are stored at the OR side in a hash table, and as a linked
@@ -141,17 +143,29 @@ typedef struct cached_resolve_t {
   HT_ENTRY(cached_resolve_t) node;
   uint32_t magic;
   char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */
+
+  union {
+    uint32_t addr_ipv4; /**< IPv4 addr for <b>address</b>. */
+    int err_ipv4;
+  } result_ipv4;
   union {
-    struct {
-      struct in6_addr addr6; /**< IPv6 addr for <b>address</b>. */
-      uint32_t addr;  /**< IPv4 addr for <b>address</b>. */
-    } a;
-    char *hostname; /**< Hostname for <b>address</b> (if a reverse lookup) */
-  } result;
-  uint8_t state; /**< Is this cached entry pending/done/valid/failed? */
-  uint8_t is_reverse; /**< Is this a reverse (addr-to-hostname) lookup? */
+    struct in6_addr addr_ipv6;
+    int err_ipv6;
+  } result_ipv6;
+  union {
+    char *hostname;
+    int err_hostname;
+  } result_ptr;
+  unsigned int res_status_ipv4 : 2;
+  unsigned int res_status_ipv6 : 2;
+  unsigned int res_status_hostname : 2;
+
+  uint8_t state; /**< Is this cached entry pending/done/informative? */
+  //uint8_t is_reverse; /**< Is this a reverse (addr-to-hostname) lookup? */
   time_t expire; /**< Remove items from cache after this time. */
-  uint32_t ttl; /**< What TTL did the nameserver tell us? */
+  uint32_t ttl_ipv4; /**< What TTL did the nameserver tell us? */
+  uint32_t ttl_ipv6; /**< What TTL did the nameserver tell us? */
+  uint32_t ttl_hostname; /**< What TTL did the nameserver tell us? */
   /** Connections that want to know when we get an answer for this resolve. */
   pending_connection_t *pending_connections;
   /** Position of this element in the heap*/
@@ -159,18 +173,26 @@ typedef struct cached_resolve_t {
 } cached_resolve_t;
 
 static void purge_expired_resolves(time_t now);
-static void dns_found_answer(const char *address, uint8_t is_reverse,
-                             uint32_t addr,
-                             const char *hostname, char outcome,
+static void dns_found_answer(const char *address, uint8_t query_type,
+                             int dns_answer,
+                             const tor_addr_t *addr,
+                             const char *hostname,
                              uint32_t ttl);
 static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type);
-static int launch_resolve(edge_connection_t *exitconn);
+static int launch_resolve(cached_resolve_t *resolve);
 static void add_wildcarded_test_address(const char *address);
 static int configure_nameservers(int force);
 static int answer_is_wildcarded(const char *ip);
 static int dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
                             or_circuit_t *oncirc, char **resolved_to_hostname,
                             int *made_connection_pending_out);
+static int set_exitconn_info_from_resolve(edge_connection_t *exitconn,
+                                          const cached_resolve_t *resolve,
+                                          char **hostname_out);
+static int evdns_err_is_transient(int err);
+static void inform_pending_connections(cached_resolve_t *resolve);
+static void make_pending_resolve_cached(cached_resolve_t *cached);
+
 #ifdef DEBUG_DNS_CACHE
 static void assert_cache_ok_(void);
 #define assert_cache_ok() assert_cache_ok_()
@@ -182,6 +204,11 @@ static void assert_resolve_ok(cached_resolve_t *resolve);
 /** Hash table of cached_resolve objects. */
 static HT_HEAD(cache_map, cached_resolve_t) cache_root;
 
+/*DOCDOC*/
+static uint64_t n_ipv6_requests_made = 0;
+static uint64_t n_ipv6_timeouts = 0;
+static int dns_is_broken_for_ipv6 = 0;
+
 /** Function to compare hashed resolves on their addresses; used to
  * implement hash tables. */
 static INLINE int
@@ -346,8 +373,8 @@ free_cached_resolve_(cached_resolve_t *r)
     r->pending_connections = victim->next;
     tor_free(victim);
   }
-  if (r->is_reverse)
-    tor_free(r->result.hostname);
+  if (r->res_status_hostname == RES_STATUS_DONE_OK)
+    tor_free(r->result_ptr.hostname);
   r->magic = 0xFF00FF00;
   tor_free(r);
 }
@@ -371,6 +398,65 @@ compare_cached_resolves_by_expiry_(const void *_a, const void *_b)
  * will expire. */
 static smartlist_t *cached_resolve_pqueue = NULL;
 
+static void
+cached_resolve_add_answer(cached_resolve_t *resolve,
+                          int query_type,
+                          int dns_result,
+                          const tor_addr_t *answer_addr,
+                          const char *answer_hostname,
+                          uint32_t ttl)
+{
+  if (query_type == DNS_PTR) {
+    if (resolve->res_status_hostname != RES_STATUS_INFLIGHT)
+      return;
+
+    if (dns_result == DNS_ERR_NONE && answer_hostname) {
+      resolve->result_ptr.hostname = tor_strdup(answer_hostname);
+      resolve->res_status_hostname = RES_STATUS_DONE_OK;
+    } else {
+      resolve->result_ptr.err_hostname = dns_result;
+      resolve->res_status_hostname = RES_STATUS_DONE_ERR;
+    }
+    resolve->ttl_hostname = ttl;
+  } else if (query_type == DNS_IPv4_A) {
+    if (resolve->res_status_ipv4 != RES_STATUS_INFLIGHT)
+      return;
+
+    if (dns_result == DNS_ERR_NONE && answer_addr) {
+      tor_assert(tor_addr_family(answer_addr) == AF_INET);
+      resolve->result_ipv4.addr_ipv4 = tor_addr_to_ipv4h(answer_addr);
+      resolve->res_status_ipv4 = RES_STATUS_DONE_OK;
+    } else {
+      resolve->result_ipv4.err_ipv4 = dns_result;
+      resolve->res_status_ipv4 = RES_STATUS_DONE_ERR;
+    }
+
+  } else if (query_type == DNS_IPv6_AAAA) {
+    if (resolve->res_status_ipv6 != RES_STATUS_INFLIGHT)
+      return;
+
+    if (dns_result == DNS_ERR_NONE && answer_addr) {
+      tor_assert(tor_addr_family(answer_addr) == AF_INET6);
+      memcpy(&resolve->result_ipv6.addr_ipv6,
+             tor_addr_to_in6(answer_addr),
+             sizeof(struct in6_addr));
+      resolve->res_status_ipv6 = RES_STATUS_DONE_OK;
+    } else {
+      resolve->result_ipv6.err_ipv6 = dns_result;
+      resolve->res_status_ipv6 = RES_STATUS_DONE_ERR;
+    }
+  }
+}
+
+/*DOCDOC*/
+static int
+cached_resolve_have_all_answers(const cached_resolve_t *resolve)
+{
+  return (resolve->res_status_ipv4 != RES_STATUS_INFLIGHT &&
+          resolve->res_status_ipv6 != RES_STATUS_INFLIGHT &&
+          resolve->res_status_hostname != RES_STATUS_INFLIGHT);
+}
+
 /** Set an expiry time for a cached_resolve_t, and add it to the expiry
  * priority queue */
 static void
@@ -436,8 +522,7 @@ purge_expired_resolves(time_t now)
                 "Expiring a dns resolve %s that's still pending. Forgot to "
                 "cull it? DNS resolve didn't tell us about the timeout?",
                 escaped_safe_str(resolve->address));
-    } else if (resolve->state == CACHE_STATE_CACHED_VALID ||
-               resolve->state == CACHE_STATE_CACHED_FAILED) {
+    } else if (resolve->state == CACHE_STATE_CACHED) {
       log_debug(LD_EXIT,
                 "Forgetting old cached resolve (address %s, expires %lu)",
                 escaped_safe_str(resolve->address),
@@ -466,8 +551,7 @@ purge_expired_resolves(time_t now)
       }
     }
 
-    if (resolve->state == CACHE_STATE_CACHED_VALID ||
-        resolve->state == CACHE_STATE_CACHED_FAILED ||
+    if (resolve->state == CACHE_STATE_CACHED ||
         resolve->state == CACHE_STATE_PENDING) {
       removed = HT_REMOVE(cache_map, &cache_root, resolve);
       if (removed != resolve) {
@@ -482,8 +566,8 @@ purge_expired_resolves(time_t now)
       cached_resolve_t *tmp = HT_FIND(cache_map, &cache_root, resolve);
       tor_assert(tmp != resolve);
     }
-    if (resolve->is_reverse)
-      tor_free(resolve->result.hostname);
+    if (resolve->res_status_hostname == RES_STATUS_DONE_OK)
+      tor_free(resolve->result_ptr.hostname);
     resolve->magic = 0xF0BBF0BB;
     tor_free(resolve);
   }
@@ -702,10 +786,10 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
   cached_resolve_t *resolve;
   cached_resolve_t search;
   pending_connection_t *pending_connection;
+  int is_reverse = 0;
   const routerinfo_t *me;
   tor_addr_t addr;
   time_t now = time(NULL);
-  uint8_t is_reverse = 0;
   int r;
   assert_connection_ok(TO_CONN(exitconn), 0);
   tor_assert(!SOCKET_OK(exitconn->base_.s));
@@ -773,6 +857,7 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
     //log_notice(LD_EXIT, "Looks like an address %s",
     //exitconn->base_.address);
   }
+  exitconn->is_reverse_dns_lookup = is_reverse;
 
   /* now check the hash table to see if 'address' is already there. */
   strlcpy(search.address, exitconn->base_.address, sizeof(search.address));
@@ -791,23 +876,13 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
                   "resolve of %s", exitconn->base_.s,
                   escaped_safe_str(exitconn->base_.address));
         return 0;
-      case CACHE_STATE_CACHED_VALID:
-        log_debug(LD_EXIT,"Connection (fd %d) found cached answer for %s",
+      case CACHE_STATE_CACHED:
+        log_debug(LD_EXIT,"Connection (fd %d) found cachedresult for %s",
                   exitconn->base_.s,
                   escaped_safe_str(resolve->address));
-        exitconn->address_ttl = resolve->ttl;
-        if (resolve->is_reverse) {
-          tor_assert(is_resolve);
-          *hostname_out = tor_strdup(resolve->result.hostname);
-        } else {
-          tor_addr_from_ipv4h(&exitconn->base_.addr, resolve->result.a.addr);
-        }
-        return 1;
-      case CACHE_STATE_CACHED_FAILED:
-        log_debug(LD_EXIT,"Connection (fd %d) found cached error for %s",
-                  exitconn->base_.s,
-                  escaped_safe_str(exitconn->base_.address));
-        return -1;
+
+        return set_exitconn_info_from_resolve(exitconn, resolve, hostname_out);
+
       case CACHE_STATE_DONE:
         log_err(LD_BUG, "Found a 'DONE' dns resolve still in the cache.");
         tor_fragile_assert();
@@ -820,7 +895,6 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
   resolve->magic = CACHED_RESOLVE_MAGIC;
   resolve->state = CACHE_STATE_PENDING;
   resolve->minheap_idx = -1;
-  resolve->is_reverse = is_reverse;
   strlcpy(resolve->address, exitconn->base_.address, sizeof(resolve->address));
 
   /* add this connection to the pending list */
@@ -837,7 +911,93 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
             escaped_safe_str(exitconn->base_.address));
   assert_cache_ok();
 
-  return launch_resolve(exitconn);
+  return launch_resolve(resolve);
+}
+
+/* DOCDOC must set address_ttl, addr, etc.*/
+static int
+set_exitconn_info_from_resolve(edge_connection_t *exitconn,
+                               const cached_resolve_t *resolve,
+                               char **hostname_out)
+{
+  int ipv4_ok, ipv6_ok, answer_with_ipv4, r;
+  tor_assert(exitconn);
+  tor_assert(resolve);
+
+  if (exitconn->is_reverse_dns_lookup) {
+    exitconn->address_ttl = resolve->ttl_hostname;
+    if (resolve->res_status_hostname == RES_STATUS_DONE_OK) {
+      *hostname_out = tor_strdup(resolve->result_ptr.hostname);
+      return 1;
+    } else {
+      return -1;
+    }
+  }
+
+  /* If we're here then the connection wants one or either of ipv4, ipv6, and
+   * we can give it one or both. */
+  ipv4_ok = (resolve->res_status_ipv4 == RES_STATUS_DONE_OK) &&
+    ! (exitconn->begincell_flags & BEGIN_FLAG_IPV4_NOT_OK);
+  ipv6_ok = (resolve->res_status_ipv6 == RES_STATUS_DONE_OK) &&
+    (exitconn->begincell_flags & BEGIN_FLAG_IPV6_OK) &&
+    get_options()->IPv6Exit;
+
+  /* Now decide which one to actually give. */
+  if (ipv4_ok && ipv6_ok) {
+    /* If we have both, see if our exit policy has an opinion. */
+    const uint16_t port = exitconn->base_.port;
+    int ipv4_allowed, ipv6_allowed;
+    tor_addr_t a4, a6;
+    tor_addr_from_ipv4h(&a4, resolve->result_ipv4.addr_ipv4);
+    tor_addr_from_in6(&a6, &resolve->result_ipv6.addr_ipv6);
+    ipv4_allowed = !router_compare_to_my_exit_policy(&a4, port);
+    ipv6_allowed = !router_compare_to_my_exit_policy(&a6, port);
+    if (ipv4_allowed && !ipv6_allowed) {
+      answer_with_ipv4 = 1;
+    } else if (ipv6_allowed && !ipv4_allowed) {
+      answer_with_ipv4 = 0;
+    } else {
+      /* Our exit policy would permit both.  Answer with whichever the user
+       * prefers */
+      answer_with_ipv4 = !(exitconn->begincell_flags &
+                           BEGIN_FLAG_IPV6_PREFERRED);
+    }
+  } else {
+    /* Otherwise if one is okay, send it back. */
+    if (ipv4_ok) {
+      answer_with_ipv4 = 1;
+    } else if (ipv6_ok) {
+      answer_with_ipv4 = 0;
+    } else {
+      /* Neither one was okay. Choose based on user preference. */
+      answer_with_ipv4 = !(exitconn->begincell_flags &
+                           BEGIN_FLAG_IPV6_PREFERRED);
+    }
+  }
+
+  /* Finally, we write the answer back. */
+  r = 1;
+  if (answer_with_ipv4) {
+    if (resolve->res_status_ipv4 == RES_STATUS_DONE_OK) {
+      tor_addr_from_ipv4h(&exitconn->base_.addr,
+                          resolve->result_ipv4.addr_ipv4);
+    } else {
+      r = evdns_err_is_transient(resolve->result_ipv4.err_ipv4) ? -2 : -1;
+    }
+
+    exitconn->address_ttl = resolve->ttl_ipv4;
+  } else {
+    if (resolve->res_status_ipv6 == RES_STATUS_DONE_OK) {
+      tor_addr_from_in6(&exitconn->base_.addr,
+                        &resolve->result_ipv6.addr_ipv6);
+    } else {
+      r = evdns_err_is_transient(resolve->result_ipv6.err_ipv6) ? -2 : -1;
+    }
+
+    exitconn->address_ttl = resolve->ttl_ipv6;
+  }
+
+  return r;
 }
 
 /** Log an error and abort if conn is waiting for a DNS resolve.
@@ -1011,47 +1171,6 @@ dns_cancel_pending_resolve(const char *address)
   resolve->state = CACHE_STATE_DONE;
 }
 
-/** Helper: adds an entry to the DNS cache mapping <b>address</b> to the ipv4
- * address <b>addr</b> (if is_reverse is 0) or the hostname <b>hostname</b> (if
- * is_reverse is 1).  <b>ttl</b> is a cache ttl; <b>outcome</b> is one of
- * DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
- **/
-static void
-add_answer_to_cache(const char *address, uint8_t is_reverse, uint32_t addr,
-                    const char *hostname, char outcome, uint32_t ttl)
-{
-  cached_resolve_t *resolve;
-  if (outcome == DNS_RESOLVE_FAILED_TRANSIENT)
-    return;
-
-  //log_notice(LD_EXIT, "Adding to cache: %s -> %s (%lx, %s), %d",
-  //           address, is_reverse?"(reverse)":"", (unsigned long)addr,
-  //           hostname?hostname:"NULL",(int)outcome);
-
-  resolve = tor_malloc_zero(sizeof(cached_resolve_t));
-  resolve->magic = CACHED_RESOLVE_MAGIC;
-  resolve->state = (outcome == DNS_RESOLVE_SUCCEEDED) ?
-    CACHE_STATE_CACHED_VALID : CACHE_STATE_CACHED_FAILED;
-  strlcpy(resolve->address, address, sizeof(resolve->address));
-  resolve->is_reverse = is_reverse;
-  if (is_reverse) {
-    if (outcome == DNS_RESOLVE_SUCCEEDED) {
-      tor_assert(hostname);
-      resolve->result.hostname = tor_strdup(hostname);
-    } else {
-      tor_assert(! hostname);
-      resolve->result.hostname = NULL;
-    }
-  } else {
-    tor_assert(!hostname);
-    resolve->result.a.addr = addr;
-  }
-  resolve->ttl = ttl;
-  assert_resolve_ok(resolve);
-  HT_INSERT(cache_map, &cache_root, resolve);
-  set_expiry(resolve, time(NULL) + dns_get_expiry_ttl(ttl));
-}
-
 /** Return true iff <b>address</b> is one of the addresses we use to verify
  * that well-known sites aren't being hijacked by our DNS servers. */
 static INLINE int
@@ -1070,14 +1189,13 @@ is_test_address(const char *address)
  * DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
  */
 static void
-dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
-                 const char *hostname, char outcome, uint32_t ttl)
+dns_found_answer(const char *address, uint8_t query_type,
+                 int dns_answer,
+                 const tor_addr_t *addr,
+                 const char *hostname, uint32_t ttl)
 {
-  pending_connection_t *pend;
   cached_resolve_t search;
-  cached_resolve_t *resolve, *removed;
-  edge_connection_t *pendconn;
-  circuit_t *circ;
+  cached_resolve_t *resolve;
 
   assert_cache_ok();
 
@@ -1087,9 +1205,8 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
   if (!resolve) {
     int is_test_addr = is_test_address(address);
     if (!is_test_addr)
-      log_info(LD_EXIT,"Resolved unasked address %s; caching anyway.",
+      log_info(LD_EXIT,"Resolved unasked address %s; ignoring.",
                escaped_safe_str(address));
-    add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
     return;
   }
   assert_resolve_ok(resolve);
@@ -1105,17 +1222,32 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
     tor_assert(resolve->pending_connections == NULL);
     return;
   }
-  /* Removed this assertion: in fact, we'll sometimes get a double answer
-   * to the same question.  This can happen when we ask one worker to resolve
-   * X.Y.Z., then we cancel the request, and then we ask another worker to
-   * resolve X.Y.Z. */
-  /* tor_assert(resolve->state == CACHE_STATE_PENDING); */
+
+  cached_resolve_add_answer(resolve, query_type, dns_answer,
+                            addr, hostname, ttl);
+
+  if (cached_resolve_have_all_answers(resolve)) {
+    inform_pending_connections(resolve);
+
+    make_pending_resolve_cached(resolve);
+  }
+}
+
+/*DOCDOC*/
+static void
+inform_pending_connections(cached_resolve_t *resolve)
+{
+  pending_connection_t *pend;
+  edge_connection_t *pendconn;
+  int r;
 
   while (resolve->pending_connections) {
+    char *hostname = NULL;
     pend = resolve->pending_connections;
     pendconn = pend->conn; /* don't pass complex things to the
                               connection_mark_for_close macro */
     assert_connection_ok(TO_CONN(pendconn),time(NULL));
+
     if (pendconn->base_.marked_for_close) {
       /* prevent double-remove. */
       pendconn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED;
@@ -1123,10 +1255,12 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
       tor_free(pend);
       continue;
     }
-    tor_addr_from_ipv4h(&pendconn->base_.addr, addr);
-    pendconn->address_ttl = ttl;
 
-    if (outcome != DNS_RESOLVE_SUCCEEDED) {
+    r = set_exitconn_info_from_resolve(pendconn,
+                                       resolve,
+                                       &hostname);
+
+    if (r < 0) {
       /* prevent double-remove. */
       pendconn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED;
       if (pendconn->base_.purpose == EXIT_PURPOSE_CONNECT) {
@@ -1134,15 +1268,15 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
         /* This detach must happen after we send the end cell. */
         circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
       } else {
-        send_resolved_cell(pendconn, outcome == DNS_RESOLVE_FAILED_PERMANENT ?
+        send_resolved_cell(pendconn, r == -1 ?
                           RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT);
         /* This detach must happen after we send the resolved cell. */
         circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
       }
       connection_free(TO_CONN(pendconn));
     } else {
+      circuit_t *circ;
       if (pendconn->base_.purpose == EXIT_PURPOSE_CONNECT) {
-        tor_assert(!is_reverse);
         /* prevent double-remove. */
         pend->conn->base_.state = EXIT_CONN_STATE_CONNECTING;
 
@@ -1161,7 +1295,7 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
         /* prevent double-remove.  This isn't really an accurate state,
          * but it does the right thing. */
         pendconn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED;
-        if (is_reverse)
+        if (pendconn->is_reverse_dns_lookup)
           send_resolved_hostname_cell(pendconn, hostname);
         else
           send_resolved_cell(pendconn, RESOLVED_TYPE_AUTO);
@@ -1174,9 +1308,16 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
     resolve->pending_connections = pend->next;
     tor_free(pend);
   }
+}
+
+/*DOCDOC*/
+static void
+make_pending_resolve_cached(cached_resolve_t *resolve)
+{
+  cached_resolve_t *removed;
 
   resolve->state = CACHE_STATE_DONE;
-  removed = HT_REMOVE(cache_map, &cache_root, &search);
+  removed = HT_REMOVE(cache_map, &cache_root, resolve);
   if (removed != resolve) {
     log_err(LD_BUG, "The pending resolve we found wasn't removable from"
             " the cache. Tried to purge %s (%p); instead got %s (%p).",
@@ -1185,8 +1326,42 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
   }
   assert_resolve_ok(resolve);
   assert_cache_ok();
+  /* The resolve will eventually just hit the time-out in the expiry queue and
+  * expire. See fd0bafb0dedc7e2 for a brief explanation of how this got that
+  * way.  XXXXX we could do better!*/
+
+  {
+    cached_resolve_t *new_resolve = tor_memdup(resolve,
+                                               sizeof(cached_resolve_t));
+    uint32_t ttl = UINT32_MAX;
+    new_resolve->expiry = 0; /* So that set_expiry won't croak. */
+    if (resolve->res_status_hostname == RES_STATUS_DONE_OK)
+      new_resolve->result_ptr.hostname =
+        tor_strdup(resolve->result_ptr.hostname);
+
+    new_resolve->state = CACHE_STATE_CACHED;
+
+    assert_resolve_ok(new_resolve);
+    HT_INSERT(cache_map, &cache_root, new_resolve);
+
+    if ((resolve->res_status_ipv4 == RES_STATUS_DONE_OK ||
+         resolve->res_status_ipv4 == RES_STATUS_DONE_ERR) &&
+        resolve->ttl_ipv4 < ttl)
+      ttl = resolve->ttl_ipv4;
+
+    if ((resolve->res_status_ipv6 == RES_STATUS_DONE_OK ||
+         resolve->res_status_ipv6 == RES_STATUS_DONE_ERR) &&
+        resolve->ttl_ipv6 < ttl)
+      ttl = resolve->ttl_ipv6;
+
+    if ((resolve->res_status_hostname == RES_STATUS_DONE_OK ||
+         resolve->res_status_hostname == RES_STATUS_DONE_ERR) &&
+        resolve->ttl_hostname < ttl)
+      ttl = resolve->ttl_hostname;
+
+    set_expiry(new_resolve, time(NULL) + dns_get_expiry_ttl(ttl));
+  }
 
-  add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
   assert_cache_ok();
 }
 
@@ -1342,10 +1517,6 @@ configure_nameservers(int force)
   return -1;
 }
 
-static uint64_t n_ipv6_requests_made = 0;
-static uint64_t n_ipv6_timeouts = 0;
-static int dns_is_broken_for_ipv6 = 0;
-
 /** For eventdns: Called when we get an answer for a request we launched.
  * See eventdns.h for arguments; 'arg' holds the address we tried to resolve.
  */
@@ -1353,8 +1524,9 @@ static void
 evdns_callback(int result, char type, int count, int ttl, void *addresses,
                void *arg)
 {
-  char *string_address = arg;
-  uint8_t is_reverse = 0;
+  char *arg_ = arg;
+  uint8_t orig_query_type = arg_[0];
+  char *string_address = arg_ + 1;
   int status = DNS_RESOLVE_FAILED_PERMANENT;
   tor_addr_t addr;
   const char *hostname = NULL;
@@ -1382,7 +1554,6 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
   if (result == DNS_ERR_NONE) {
     if (type == DNS_IPv4_A && count) {
       char answer_buf[INET_NTOA_BUF_LEN+1];
-      struct in_addr in;
       char *escaped_address;
       uint32_t *addrs = addresses;
       tor_addr_from_ipv4n(&addr, addrs[0]);
@@ -1397,7 +1568,7 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
                   safe_str(escaped_address),
                   escaped_safe_str(answer_buf));
         was_wildcarded = 1;
-        addr = 0;
+        tor_addr_make_null(&addr, AF_INET); /* ???? */
         status = DNS_RESOLVE_FAILED_PERMANENT;
       } else {
         log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
@@ -1430,7 +1601,6 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
       tor_free(escaped_address);
     } else if (type == DNS_PTR && count) {
       char *escaped_address;
-      is_reverse = 1;
       hostname = ((char**)addresses)[0];
       status = DNS_RESOLVE_SUCCEEDED;
       escaped_address = esc_for_log(string_address);
@@ -1456,23 +1626,76 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
       add_wildcarded_test_address(string_address);
     }
   }
+
+  if (orig_query_type && type && orig_query_type != type) {
+    log_warn(LD_BUG, "Weird; orig_query_type == %d but type == %d",
+             (int)orig_query_type, (int)type);
+  }
   if (result != DNS_ERR_SHUTDOWN)
-    dns_found_answer(string_address, is_reverse, addr, hostname, status, ttl);
-  tor_free(string_address);
+    dns_found_answer(string_address, orig_query_type,
+                     result, &addr, hostname, ttl);
+
+  tor_free(arg_);
+}
+
+/**DOCDOC*/
+static int
+launch_one_resolve(const char *address, uint8_t query_type,
+                   const tor_addr_t *ptr_address)
+{
+  const int options = get_options()->ServerDNSSearchDomains ? 0
+    : DNS_QUERY_NO_SEARCH;
+  const size_t addr_len = strlen(address);
+  struct evdns_request *req = 0;
+  char *addr = tor_malloc(addr_len + 2);
+  addr[0] = (char) query_type;
+  memcpy(addr+1, address, addr_len + 1);
+
+  switch (query_type) {
+  case DNS_IPv4_A:
+    req = evdns_base_resolve_ipv4(the_evdns_base,
+                                  address, options, evdns_callback, addr);
+    break;
+  case DNS_IPv6_AAAA:
+    req = evdns_base_resolve_ipv6(the_evdns_base,
+                                  address, options, evdns_callback, addr);
+    ++n_ipv6_requests_made;
+    break;
+  case DNS_PTR:
+    if (tor_addr_family(ptr_address) == AF_INET)
+      req = evdns_base_resolve_reverse(the_evdns_base,
+                                       tor_addr_to_in(ptr_address),
+                                       DNS_QUERY_NO_SEARCH,
+                                       evdns_callback, addr);
+    else if (tor_addr_family(ptr_address) == AF_INET6)
+      req = evdns_base_resolve_reverse_ipv6(the_evdns_base,
+                                            tor_addr_to_in6(ptr_address),
+                                            DNS_QUERY_NO_SEARCH,
+                                            evdns_callback, addr);
+    else
+      log_warn(LD_BUG, "Called with PTR query and unexpected address family");
+    break;
+  default:
+    log_warn(LD_BUG, "Called with unexpectd query type %d", (int)query_type);
+    break;
+  }
+
+  if (req) {
+    return 0;
+  } else {
+    tor_free(addr);
+    return -1;
+  }
 }
 
 /** For eventdns: start resolving as necessary to find the target for
  * <b>exitconn</b>.  Returns -1 on error, -2 on transient error,
  * 0 on "resolve launched." */
 static int
-launch_resolve(edge_connection_t *exitconn)
+launch_resolve(cached_resolve_t *resolve)
 {
-  char *addr;
-  struct evdns_request *req = NULL;
   tor_addr_t a;
   int r;
-  int options = get_options()->ServerDNSSearchDomains ? 0
-    : DNS_QUERY_NO_SEARCH;
 
   if (get_options()->DisableNetwork)
     return -1;
@@ -1486,40 +1709,45 @@ launch_resolve(edge_connection_t *exitconn)
     }
   }
 
-  addr = tor_strdup(exitconn->base_.address);
-
   r = tor_addr_parse_PTR_name(
-                            &a, exitconn->base_.address, AF_UNSPEC, 0);
+                            &a, resolve->address, AF_UNSPEC, 0);
 
   tor_assert(the_evdns_base);
   if (r == 0) {
     log_info(LD_EXIT, "Launching eventdns request for %s",
-             escaped_safe_str(exitconn->base_.address));
-    req = evdns_base_resolve_ipv4(the_evdns_base,
-                                exitconn->base_.address, options,
-                                evdns_callback, addr);
+             escaped_safe_str(resolve->address));
+    resolve->res_status_ipv4 = RES_STATUS_INFLIGHT;
+    if (get_options()->IPv6Exit)
+      resolve->res_status_ipv6 = RES_STATUS_INFLIGHT;
+
+    if (launch_one_resolve(resolve->address, DNS_IPv4_A, NULL) < 0) {
+      resolve->res_status_ipv4 = 0;
+      r = -1;
+    }
+
+    if (r==0 && get_options()->IPv6Exit) {
+      /* We ask for an IPv6 address for *everything*. */
+      if (launch_one_resolve(resolve->address, DNS_IPv6_AAAA, NULL) < 0) {
+        resolve->res_status_ipv6 = 0;
+        r = -1;
+      }
+    }
   } else if (r == 1) {
+    r = 0;
     log_info(LD_EXIT, "Launching eventdns reverse request for %s",
-             escaped_safe_str(exitconn->base_.address));
-    if (tor_addr_family(&a) == AF_INET)
-      req = evdns_base_resolve_reverse(the_evdns_base,
-                                tor_addr_to_in(&a), DNS_QUERY_NO_SEARCH,
-                                evdns_callback, addr);
-    else
-      req = evdns_base_resolve_reverse_ipv6(the_evdns_base,
-                                     tor_addr_to_in6(&a), DNS_QUERY_NO_SEARCH,
-                                     evdns_callback, addr);
+             escaped_safe_str(resolve->address));
+    resolve->res_status_hostname = RES_STATUS_INFLIGHT;
+    if (launch_one_resolve(resolve->address, DNS_PTR, &a) < 0) {
+      resolve->res_status_hostname = 0;
+      r = -1;
+    }
   } else if (r == -1) {
     log_warn(LD_BUG, "Somehow a malformed in-addr.arpa address reached here.");
   }
 
-  r = 0;
-  if (!req) {
+  if (r < 0) {
     log_fn(LOG_PROTOCOL_WARN, LD_EXIT, "eventdns rejected address %s.",
-             escaped_safe_str(addr));
-    r = -1;
-    tor_free(addr); /* There is no evdns request in progress; stop
-                     * addr from getting leaked. */
+           escaped_safe_str(resolve->address));
   }
   return r;
 }
@@ -1699,7 +1927,6 @@ static void
 launch_test_addresses(int fd, short event, void *args)
 {
   const or_options_t *options = get_options();
-  struct evdns_request *req;
   (void)fd;
   (void)event;
   (void)args;
@@ -1717,23 +1944,14 @@ launch_test_addresses(int fd, short event, void *args)
     tor_assert(the_evdns_base);
     SMARTLIST_FOREACH_BEGIN(options->ServerDNSTestAddresses,
                             const char *, address) {
-      char *a = tor_strdup(address);
-      req = evdns_base_resolve_ipv4(the_evdns_base,
-                         address, DNS_QUERY_NO_SEARCH, evdns_callback, a);
-
-      if (!req) {
+      if (launch_one_resolve(address, DNS_IPv4_A, NULL) < 0) {
         log_info(LD_EXIT, "eventdns rejected test address %s",
                  escaped_safe_str(address));
-        tor_free(a);
       }
-      a = tor_strdup(address);
-      req = evdns_base_resolve_ipv6(the_evdns_base,
-                           address, DNS_QUERY_NO_SEARCH, evdns_callback, a);
-      ++n_ipv6_requests_made;
-      if (!req) {
+
+      if (launch_one_resolve(address, DNS_IPv6_AAAA, NULL) < 0) {
         log_info(LD_EXIT, "eventdns rejected test address %s",
                  escaped_safe_str(address));
-        tor_free(a);
       }
     } SMARTLIST_FOREACH_END(address);
   }
@@ -1857,11 +2075,14 @@ assert_resolve_ok(cached_resolve_t *resolve)
   }
   if (resolve->state == CACHE_STATE_PENDING ||
       resolve->state == CACHE_STATE_DONE) {
+#if 0
     tor_assert(!resolve->ttl);
     if (resolve->is_reverse)
-      tor_assert(!resolve->result.hostname);
+      tor_assert(!resolve->hostname);
     else
-      tor_assert(!resolve->result.a.addr);
+      tor_assert(!resolve->result_ipv4.addr_ipv4);
+#endif
+    /*XXXXX ADD MORE */
   }
 }
 
diff --git a/src/or/or.h b/src/or/or.h
index c863edbf216aee9f995f47411938b3ba447cd56b..f22c7dffbaae86b0de54011435a5a9b6f4f752dc 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1440,6 +1440,8 @@ typedef struct edge_connection_t {
 
   /** True iff this connection is for a DNS request only. */
   unsigned int is_dns_request:1;
+  /* DOCDOC exit only */
+  unsigned int is_reverse_dns_lookup:1;
 
   unsigned int edge_has_sent_end:1; /**< For debugging; only used on edge
                          * connections.  Set once we've set the stream end,