Typos. A bunch of typos I've been collecting.
[vpp.git] / src / vnet / dns / dns.c
index 90079e1..2951679 100644 (file)
@@ -55,10 +55,7 @@ dns_cache_clear (dns_main_t * dm)
   pool_foreach (ep, dm->entries,
   ({
     vec_free (ep->name);
-    vec_free (ep->api_clients_to_notify);
-    vec_free (ep->api_client_contexts);
-    vec_free (ep->ip4_peers_to_notify);
-    vec_free (ep->ip6_peers_to_notify);
+    vec_free (ep->pending_requests);
   }));
   /* *INDENT-ON* */
 
@@ -75,6 +72,7 @@ dns_enable_disable (dns_main_t * dm, int is_enable)
 {
   vlib_thread_main_t *tm = &vlib_thread_main;
   u32 n_vlib_mains = tm->n_vlib_mains;
+  vlib_main_t *vm = dm->vlib_main;
 
   if (is_enable)
     {
@@ -82,6 +80,23 @@ dns_enable_disable (dns_main_t * dm, int is_enable)
          && (vec_len (dm->ip6_name_servers) == 0))
        return VNET_API_ERROR_NO_NAME_SERVERS;
 
+      if (dm->udp_ports_registered == 0)
+       {
+         udp_register_dst_port (vm, UDP_DST_PORT_dns_reply,
+                                dns46_reply_node.index, 1 /* is_ip4 */ );
+
+         udp_register_dst_port (vm, UDP_DST_PORT_dns_reply6,
+                                dns46_reply_node.index, 0 /* is_ip4 */ );
+
+         udp_register_dst_port (vm, UDP_DST_PORT_dns,
+                                dns4_request_node.index, 1 /* is_ip4 */ );
+
+         udp_register_dst_port (vm, UDP_DST_PORT_dns6,
+                                dns6_request_node.index, 0 /* is_ip4 */ );
+
+         dm->udp_ports_registered = 1;
+       }
+
       if (dm->cache_entry_by_name == 0)
        {
          if (n_vlib_mains > 1)
@@ -200,9 +215,9 @@ static void vl_api_dns_name_server_add_del_t_handler
   REPLY_MACRO (VL_API_DNS_NAME_SERVER_ADD_DEL_REPLY);
 }
 
-static void
-send_dns4_request (dns_main_t * dm,
-                  dns_cache_entry_t * ep, ip4_address_t * server)
+void
+vnet_dns_send_dns4_request (dns_main_t * dm,
+                           dns_cache_entry_t * ep, ip4_address_t * server)
 {
   vlib_main_t *vm = dm->vlib_main;
   f64 now = vlib_time_now (vm);
@@ -250,12 +265,12 @@ send_dns4_request (dns_main_t * dm,
     {
       clib_warning
        ("route to %U exists, fei %d, get_resolving_interface returned"
-        " ~0", fei, format_ip4_address, &prefix.fp_addr);
+        " ~0", format_ip4_address, &prefix.fp_addr, fei);
       return;
     }
 
   /* *INDENT-OFF* */
-  foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnummbered */,
+  foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnumbered */,
   ({
     src_address = ip_interface_address_get_address (lm4, ia);
     goto found_src_address;
@@ -281,9 +296,9 @@ found_src_address:
   vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;   /* default VRF for now */
 
   ip = vlib_buffer_get_current (b);
-  memset (ip, 0, sizeof (*ip));
+  clib_memset (ip, 0, sizeof (*ip));
   udp = (udp_header_t *) (ip + 1);
-  memset (udp, 0, sizeof (*udp));
+  clib_memset (udp, 0, sizeof (*udp));
 
   dns_request = (u8 *) (udp + 1);
 
@@ -316,9 +331,9 @@ found_src_address:
   ep->retry_timer = now + 2.0;
 }
 
-static void
-send_dns6_request (dns_main_t * dm,
-                  dns_cache_entry_t * ep, ip6_address_t * server)
+void
+vnet_dns_send_dns6_request (dns_main_t * dm,
+                           dns_cache_entry_t * ep, ip6_address_t * server)
 {
   vlib_main_t *vm = dm->vlib_main;
   f64 now = vlib_time_now (vm);
@@ -363,7 +378,7 @@ send_dns6_request (dns_main_t * dm,
   sw_if_index = fib_entry_get_resolving_interface (fei);
 
   /* *INDENT-OFF* */
-  foreach_ip_interface_address(lm6, ia, sw_if_index, 1 /* honor unnummbered */,
+  foreach_ip_interface_address(lm6, ia, sw_if_index, 1 /* honor unnumbered */,
   ({
     src_address = ip_interface_address_get_address (lm6, ia);
     goto found_src_address;
@@ -387,9 +402,9 @@ found_src_address:
     VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED;
 
   ip = vlib_buffer_get_current (b);
-  memset (ip, 0, sizeof (*ip));
+  clib_memset (ip, 0, sizeof (*ip));
   udp = (udp_header_t *) (ip + 1);
-  memset (udp, 0, sizeof (*udp));
+  clib_memset (udp, 0, sizeof (*udp));
 
   dns_request = (u8 *) (udp + 1);
 
@@ -473,7 +488,7 @@ name_to_labels (u8 * name)
  * Produces a non-NULL-terminated u8 *vector. %v format is your friend.
  */
 u8 *
-labels_to_name (u8 * label, u8 * full_text, u8 ** parse_from_here)
+vnet_dns_labels_to_name (u8 * label, u8 * full_text, u8 ** parse_from_here)
 {
   u8 *reply = 0;
   u16 offset;
@@ -520,9 +535,13 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
   dns_header_t *h;
   dns_query_t *qp;
   u16 tmp;
-  u8 *request;
+  u8 *request, *name_copy;
   u32 qp_offset;
 
+  /* This can easily happen if sitting in GDB, etc. */
+  if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID || ep->server_fails > 1)
+    return;
+
   /* Construct the dns request, if we haven't been here already */
   if (vec_len (ep->dns_request) == 0)
     {
@@ -532,14 +551,29 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
        * per label is 63, enforce that.
        */
       request = name_to_labels (ep->name);
+      name_copy = vec_dup (request);
       qp_offset = vec_len (request);
 
+      /*
+       * At least when testing against "known good" DNS servers:
+       * it turns out that sending 2x requests - one for an A-record
+       * and another for a AAAA-record - seems to work better than
+       * sending a DNS_TYPE_ALL request.
+       */
+
       /* Add space for the query header */
-      vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
+      vec_validate (request, 2 * qp_offset + 2 * sizeof (dns_query_t) - 1);
 
       qp = (dns_query_t *) (request + qp_offset);
 
-      qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
+      qp->type = clib_host_to_net_u16 (DNS_TYPE_A);
+      qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+      qp++;
+      clib_memcpy (qp, name_copy, vec_len (name_copy));
+      qp = (dns_query_t *) (((u8 *) qp) + vec_len (name_copy));
+      vec_free (name_copy);
+
+      qp->type = clib_host_to_net_u16 (DNS_TYPE_AAAA);
       qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
 
       /* Punch in space for the dns_header_t */
@@ -553,7 +587,7 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
       /* Ask for a recursive lookup */
       tmp = DNS_RD | DNS_OPCODE_QUERY;
       h->flags = clib_host_to_net_u16 (tmp);
-      h->qdcount = clib_host_to_net_u16 (1);
+      h->qdcount = clib_host_to_net_u16 (2);
       h->nscount = 0;
       h->arcount = 0;
 
@@ -569,8 +603,8 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
        {
          if (vec_len (dm->ip6_name_servers))
            {
-             send_dns6_request (dm, ep,
-                                dm->ip6_name_servers + ep->server_rotor);
+             vnet_dns_send_dns6_request
+               (dm, ep, dm->ip6_name_servers + ep->server_rotor);
              goto out;
            }
          else
@@ -578,7 +612,8 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
        }
       if (vec_len (dm->ip4_name_servers))
        {
-         send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
+         vnet_dns_send_dns4_request
+           (dm, ep, dm->ip4_name_servers + ep->server_rotor);
          goto out;
        }
     }
@@ -605,9 +640,11 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
     }
 
   if (ep->server_af == 1 /* ip6 */ )
-    send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor);
+    vnet_dns_send_dns6_request
+      (dm, ep, dm->ip6_name_servers + ep->server_rotor);
   else
-    send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
+    vnet_dns_send_dns4_request
+      (dm, ep, dm->ip4_name_servers + ep->server_rotor);
 
 out:
 
@@ -628,7 +665,6 @@ vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index)
     return VNET_API_ERROR_NO_SUCH_ENTRY;
 
   ep = pool_elt_at_index (dm->entries, index);
-
   if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_VALID))
     {
       for (i = 0; i < vec_len (dm->unresolved_entries); i++)
@@ -644,10 +680,7 @@ vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index)
 found:
   hash_unset_mem (dm->cache_entry_by_name, ep->name);
   vec_free (ep->name);
-  vec_free (ep->api_clients_to_notify);
-  vec_free (ep->api_client_contexts);
-  vec_free (ep->ip4_peers_to_notify);
-  vec_free (ep->ip6_peers_to_notify);
+  vec_free (ep->pending_requests);
   pool_put (dm->entries, ep);
 
   return 0;
@@ -687,6 +720,15 @@ delete_random_entry (dns_main_t * dm)
   if (dm->is_enabled == 0)
     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
 
+  /*
+   * Silence spurious coverity warning. We know pool_elts >> 0, or
+   * we wouldn't be here...
+   */
+#ifdef __COVERITY__
+  if (pool_elts (dm->entries) == 0)
+    return VNET_API_ERROR_UNSPECIFIED;
+#endif
+
   dns_cache_lock (dm);
   limit = pool_elts (dm->entries);
   start_index = random_u32 (&dm->random_seed) % limit;
@@ -744,7 +786,7 @@ dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data)
     }
 
   pool_get (dm->entries, ep);
-  memset (ep, 0, sizeof (*ep));
+  clib_memset (ep, 0, sizeof (*ep));
 
   /* Note: consumes the name vector */
   ep->name = name;
@@ -756,15 +798,16 @@ dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data)
   return 0;
 }
 
-static int
-dns_resolve_name (dns_main_t * dm,
-                 u8 * name, u32 client_index, u32 client_context,
-                 dns_cache_entry_t ** retp)
+int
+vnet_dns_resolve_name (dns_main_t * dm, u8 * name, dns_pending_request_t * t,
+                      dns_cache_entry_t ** retp)
 {
   dns_cache_entry_t *ep;
   int rv;
   f64 now;
   uword *p;
+  dns_pending_request_t *pr;
+  int count;
 
   now = vlib_time_now (dm->vlib_main);
 
@@ -772,6 +815,7 @@ dns_resolve_name (dns_main_t * dm,
   *retp = 0;
 
   dns_cache_lock (dm);
+search_again:
   p = hash_get_mem (dm->cache_entry_by_name, name);
   if (p)
     {
@@ -782,18 +826,72 @@ dns_resolve_name (dns_main_t * dm,
          if (((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0)
              && (now > ep->expiration_time))
            {
-             clib_warning ("Re-resolve %s", name);
+             int i;
+             u32 *indices_to_delete = 0;
+
+             /*
+              * Take out the rest of the resolution chain
+              * This isn't optimal, but it won't happen very often.
+              */
+             while (ep)
+               {
+                 if ((ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME))
+                   {
+                     vec_add1 (indices_to_delete, ep - dm->entries);
+
+                     p = hash_get_mem (dm->cache_entry_by_name, ep->cname);
+                     if (!p)
+                       break;
+                     ep = pool_elt_at_index (dm->entries, p[0]);
+                   }
+                 else
+                   {
+                     vec_add1 (indices_to_delete, ep - dm->entries);
+                     break;
+                   }
+               }
+             for (i = 0; i < vec_len (indices_to_delete); i++)
+               {
+                 /* Reenable to watch re-resolutions */
+                 if (0)
+                   {
+                     ep = pool_elt_at_index (dm->entries,
+                                             indices_to_delete[i]);
+                     clib_warning ("Re-resolve %s", ep->name);
+                   }
+
+                 vnet_dns_delete_entry_by_index_nolock
+                   (dm, indices_to_delete[i]);
+               }
+             vec_free (indices_to_delete);
              /* Yes, kill it... */
-             vnet_dns_delete_entry_by_index_nolock (dm, p[0]);
              goto re_resolve;
            }
 
+         if (ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
+           {
+             name = ep->cname;
+             goto search_again;
+           }
+
          /* Note: caller must drop the lock! */
          *retp = ep;
          return (0);
        }
+      else
+       {
+         /*
+          * Resolution pending. Add request to the pending vector
+          * by copying the template request
+          */
+         vec_add2 (ep->pending_requests, pr, 1);
+         memcpy (pr, t, sizeof (*pr));
+         dns_cache_unlock (dm);
+         return (0);
+       }
     }
 
+re_resolve:
   if (pool_elts (dm->entries) == dm->name_cache_size)
     {
       /* Will only fail if the cache is totally filled w/ static entries... */
@@ -805,10 +903,9 @@ dns_resolve_name (dns_main_t * dm,
        }
     }
 
-re_resolve:
   /* add new hash table entry */
   pool_get (dm->entries, ep);
-  memset (ep, 0, sizeof (*ep));
+  clib_memset (ep, 0, sizeof (*ep));
 
   ep->name = format (0, "%s%c", name, 0);
   _vec_len (ep->name) = vec_len (ep->name) - 1;
@@ -816,34 +913,62 @@ re_resolve:
   hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
 
   vec_add1 (dm->unresolved_entries, ep - dm->entries);
-  vec_add1 (ep->api_clients_to_notify, client_index);
-  vec_add1 (ep->api_client_contexts, client_context);
+  vec_add2 (ep->pending_requests, pr, 1);
+
+  pr->request_type = t->request_type;
+
+  /* Remember details so we can reply later... */
+  if (t->request_type == DNS_API_PENDING_NAME_TO_IP ||
+      t->request_type == DNS_API_PENDING_IP_TO_NAME)
+    {
+      pr->client_index = t->client_index;
+      pr->client_context = t->client_context;
+    }
+  else
+    {
+      pr->client_index = ~0;
+      pr->is_ip6 = t->is_ip6;
+      pr->dst_port = t->dst_port;
+      pr->id = t->id;
+      pr->name = t->name;
+      if (t->is_ip6)
+       count = 16;
+      else
+       count = 4;
+      clib_memcpy (pr->dst_address, t->dst_address, count);
+    }
+
   vnet_send_dns_request (dm, ep);
   dns_cache_unlock (dm);
-
   return 0;
 }
 
+#define foreach_notification_to_move            \
+_(pending_requests)
+
 /**
  * Handle cname indirection. JFC. Called with the cache locked.
  * returns 0 if the reply is not a CNAME.
  */
 
 int
-vnet_dns_cname_indirection_nolock (dns_main_t * dm, dns_cache_entry_t * ep,
-                                  u8 * reply)
+vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply)
 {
   dns_header_t *h;
   dns_query_t *qp;
   dns_rr_t *rr;
   u8 *curpos;
   u8 *pos, *pos2;
+  u8 *cname_pos = 0;
   int len, i;
   u8 *cname = 0;
   u8 *request = 0;
+  u8 *name_copy;
   u32 qp_offset;
   u16 flags;
   u16 rcode;
+  dns_cache_entry_t *ep, *next_ep;
+  f64 now;
 
   h = (dns_header_t *) reply;
   flags = clib_net_to_host_u16 (h->flags);
@@ -860,7 +985,7 @@ vnet_dns_cname_indirection_nolock (dns_main_t * dm, dns_cache_entry_t * ep,
     case DNS_RCODE_SERVER_FAILURE:
     case DNS_RCODE_NOT_IMPLEMENTED:
     case DNS_RCODE_REFUSED:
-      return 0;
+      return -1;
     }
 
   curpos = (u8 *) (h + 1);
@@ -875,8 +1000,7 @@ vnet_dns_cname_indirection_nolock (dns_main_t * dm, dns_cache_entry_t * ep,
          pos += len;
          len = *pos++;
        }
-      qp = (dns_query_t *) pos;
-      pos += sizeof (*qp);
+      pos += sizeof (dns_query_t);
     }
   pos2 = pos;
   /* expect a pointer chase here for a CNAME record */
@@ -885,26 +1009,112 @@ vnet_dns_cname_indirection_nolock (dns_main_t * dm, dns_cache_entry_t * ep,
   else
     return 0;
 
-  rr = (dns_rr_t *) pos;
+  /* Walk the answer(s) to see what to do next */
+  for (i = 0; i < clib_net_to_host_u16 (h->anscount); i++)
+    {
+      rr = (dns_rr_t *) pos;
+      switch (clib_net_to_host_u16 (rr->type))
+       {
+         /* Real address record? Done.. */
+       case DNS_TYPE_A:
+       case DNS_TYPE_AAAA:
+         return 0;
+         /*
+          * Maybe chase a CNAME pointer?
+          * It's not unheard-of for name-servers to return
+          * both CNAME and A/AAAA records...
+          */
+       case DNS_TYPE_CNAME:
+         cname_pos = pos;
+         break;
 
-  /* This is a real record, not a CNAME record */
-  if (clib_net_to_host_u16 (rr->type) != DNS_TYPE_CNAME)
-    return 0;
+         /* Some other junk, e.g. a nameserver... */
+       default:
+         break;
+       }
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      /* Skip name... */
+      if ((pos2[0] & 0xc0) == 0xc0)
+       pos += 2;
+    }
+
+  /* Neither a CNAME nor a real address. Try another server */
+  if (cname_pos == 0)
+    {
+      flags &= ~DNS_RCODE_MASK;
+      flags |= DNS_RCODE_NAME_ERROR;
+      h->flags = clib_host_to_net_u16 (flags);
+      return -1;
+    }
 
-  /* Crap. Chase the CNAME name chain. */
+  /* This is a CNAME record, chase the name chain. */
+  pos = cname_pos;
+
+  /* The last request is no longer pending.. */
+  for (i = 0; i < vec_len (dm->unresolved_entries); i++)
+    if (ep_index == dm->unresolved_entries[i])
+      {
+       vec_delete (dm->unresolved_entries, 1, i);
+       goto found_last_request;
+      }
+  clib_warning ("pool elt %d supposedly pending, but not found...", ep_index);
+
+found_last_request:
+
+  now = vlib_time_now (dm->vlib_main);
+  cname = vnet_dns_labels_to_name (rr->rdata, reply, &pos2);
+  /* Save the cname */
+  vec_add1 (cname, 0);
+  _vec_len (cname) -= 1;
+  ep = pool_elt_at_index (dm->entries, ep_index);
+  ep->cname = cname;
+  ep->flags |= (DNS_CACHE_ENTRY_FLAG_CNAME | DNS_CACHE_ENTRY_FLAG_VALID);
+  /* Save the response */
+  if (ep->dns_response)
+    vec_free (ep->dns_response);
+  ep->dns_response = reply;
+  /* Set up expiration time */
+  ep->expiration_time = now + clib_net_to_host_u32 (rr->ttl);
+
+  pool_get (dm->entries, next_ep);
+
+  /* Need to recompute ep post pool-get */
+  ep = pool_elt_at_index (dm->entries, ep_index);
+
+  clib_memset (next_ep, 0, sizeof (*next_ep));
+  next_ep->name = vec_dup (cname);
+  vec_add1 (next_ep->name, 0);
+  _vec_len (next_ep->name) -= 1;
+
+  hash_set_mem (dm->cache_entry_by_name, next_ep->name,
+               next_ep - dm->entries);
+
+  /* Use the same server */
+  next_ep->server_rotor = ep->server_rotor;
+  next_ep->server_af = ep->server_af;
+
+  /* Move notification data to the next name in the chain */
+#define _(a) next_ep->a = ep->a; ep->a = 0;
+  foreach_notification_to_move;
+#undef _
 
-  cname = labels_to_name (rr->rdata, reply, &pos2);
   request = name_to_labels (cname);
-  vec_free (cname);
+  name_copy = vec_dup (request);
 
   qp_offset = vec_len (request);
 
   /* Add space for the query header */
-  vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
+  vec_validate (request, 2 * qp_offset + 2 * sizeof (dns_query_t) - 1);
 
   qp = (dns_query_t *) (request + qp_offset);
 
-  qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
+  qp->type = clib_host_to_net_u16 (DNS_TYPE_A);
+  qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+  clib_memcpy (qp, name_copy, vec_len (name_copy));
+  qp = (dns_query_t *) (((u8 *) qp) + vec_len (name_copy));
+  vec_free (name_copy);
+
+  qp->type = clib_host_to_net_u16 (DNS_TYPE_AAAA);
   qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
 
   /* Punch in space for the dns_header_t */
@@ -913,30 +1123,25 @@ vnet_dns_cname_indirection_nolock (dns_main_t * dm, dns_cache_entry_t * ep,
   h = (dns_header_t *) request;
 
   /* Transaction ID = pool index */
-  h->id = clib_host_to_net_u16 (ep - dm->entries);
+  h->id = clib_host_to_net_u16 (next_ep - dm->entries);
 
   /* Ask for a recursive lookup */
   h->flags = clib_host_to_net_u16 (DNS_RD | DNS_OPCODE_QUERY);
-  h->qdcount = clib_host_to_net_u16 (1);
+  h->qdcount = clib_host_to_net_u16 (2);
   h->nscount = 0;
   h->arcount = 0;
 
-  vec_free (ep->dns_request);
-  ep->dns_request = request;
-  ep->retry_timer = vlib_time_now (dm->vlib_main) + 2.0;
-  ep->retry_count = 0;
+  next_ep->dns_request = request;
+  next_ep->retry_timer = now + 2.0;
+  next_ep->retry_count = 0;
 
   /*
    * Enable this to watch recursive resolution happen...
    * fformat (stdout, "%U", format_dns_reply, request, 2);
    */
 
-  if (ep->server_af == 1 /* ip6 */ )
-    send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor);
-  else
-    send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
-
-  vec_free (reply);
+  vec_add1 (dm->unresolved_entries, next_ep - dm->entries);
+  vnet_send_dns_request (dm, next_ep);
   return (1);
 }
 
@@ -950,10 +1155,11 @@ vnet_dns_response_to_reply (u8 * response,
   dns_rr_t *rr;
   int i, limit;
   u8 len;
-  u8 *curpos, *pos;
+  u8 *curpos, *pos, *pos2;
   u16 flags;
   u16 rcode;
   u32 ttl;
+  int pointer_chase;
 
   h = (dns_header_t *) response;
   flags = clib_net_to_host_u16 (h->flags);
@@ -1009,28 +1215,42 @@ vnet_dns_response_to_reply (u8 * response,
 
   for (i = 0; i < limit; i++)
     {
-      pos = curpos;
+      pos = pos2 = curpos;
+      pointer_chase = 0;
 
       /* Expect pointer chases in the answer section... */
-      if ((pos[0] & 0xC0) == 0xC0)
-       curpos += 2;
-      else
+      if ((pos2[0] & 0xC0) == 0xC0)
        {
-         len = *pos++;
-         while (len)
+         pos = pos2 + 2;
+         pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
+         pointer_chase = 1;
+       }
+
+      len = *pos2++;
+
+      while (len)
+       {
+         pos2 += len;
+         if ((pos2[0] & 0xc0) == 0xc0)
            {
-             if ((pos[0] & 0xC0) == 0xC0)
-               {
-                 curpos = pos + 2;
-                 break;
-               }
-             pos += len;
-             len = *pos++;
+             /*
+              * If we've already done one pointer chase,
+              * do not move the pos pointer.
+              */
+             if (pointer_chase == 0)
+               pos = pos2 + 2;
+             pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
+             len = *pos2++;
+             pointer_chase = 1;
            }
-         curpos = pos;
+         else
+           len = *pos2++;
        }
 
-      rr = (dns_rr_t *) curpos;
+      if (pointer_chase == 0)
+       pos = pos2;
+
+      rr = (dns_rr_t *) pos;
 
       switch (clib_net_to_host_u16 (rr->type))
        {
@@ -1050,13 +1270,15 @@ vnet_dns_response_to_reply (u8 * response,
            *min_ttlp = ttl;
          rmp->ip6_set = 1;
          break;
+
        default:
          break;
        }
       /* Might as well stop ASAP */
       if (rmp->ip4_set && rmp->ip6_set)
        break;
-      curpos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      curpos = pos;
     }
 
   if ((rmp->ip4_set + rmp->ip6_set) == 0)
@@ -1064,18 +1286,159 @@ vnet_dns_response_to_reply (u8 * response,
   return 0;
 }
 
+int
+vnet_dns_response_to_name (u8 * response,
+                          vl_api_dns_resolve_ip_reply_t * rmp,
+                          u32 * min_ttlp)
+{
+  dns_header_t *h;
+  dns_query_t *qp;
+  dns_rr_t *rr;
+  int i, limit;
+  u8 len;
+  u8 *curpos, *pos, *pos2;
+  u16 flags;
+  u16 rcode;
+  u8 *name;
+  u32 ttl;
+  u8 *junk __attribute__ ((unused));
+  int name_set = 0;
+  int pointer_chase;
+
+  h = (dns_header_t *) response;
+  flags = clib_net_to_host_u16 (h->flags);
+  rcode = flags & DNS_RCODE_MASK;
+
+  /* See if the response is OK, etc. */
+  switch (rcode)
+    {
+    default:
+    case DNS_RCODE_NO_ERROR:
+      break;
+
+    case DNS_RCODE_NAME_ERROR:
+    case DNS_RCODE_FORMAT_ERROR:
+      return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME;
+
+    case DNS_RCODE_SERVER_FAILURE:
+    case DNS_RCODE_NOT_IMPLEMENTED:
+    case DNS_RCODE_REFUSED:
+      return VNET_API_ERROR_NAME_SERVER_NEXT_SERVER;
+    }
+
+  /* No answers? Loser... */
+  if (clib_net_to_host_u16 (h->anscount) < 1)
+    return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
+
+  curpos = (u8 *) (h + 1);
+
+  /* Skip the name we asked about */
+  pos = curpos;
+  len = *pos++;
+  /* Should never happen, but stil... */
+  if ((len & 0xC0) == 0xC0)
+    curpos += 2;
+  else
+    {
+      /* skip the name / label-set */
+      while (len)
+       {
+         pos += len;
+         len = *pos++;
+       }
+      curpos = pos;
+    }
+  /* Skip queries */
+  limit = clib_net_to_host_u16 (h->qdcount);
+  qp = (dns_query_t *) curpos;
+  qp += limit;
+  curpos = (u8 *) qp;
+
+  /* Parse answers */
+  limit = clib_net_to_host_u16 (h->anscount);
+
+  for (i = 0; i < limit; i++)
+    {
+      pos = pos2 = curpos;
+      pointer_chase = 0;
+
+      /* Expect pointer chases in the answer section... */
+      if ((pos2[0] & 0xC0) == 0xC0)
+       {
+         pos = pos2 + 2;
+         pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
+         pointer_chase = 1;
+       }
+
+      len = *pos2++;
+
+      while (len)
+       {
+         pos2 += len;
+         if ((pos2[0] & 0xc0) == 0xc0)
+           {
+             /*
+              * If we've already done one pointer chase,
+              * do not move the pos pointer.
+              */
+             if (pointer_chase == 0)
+               pos = pos2 + 2;
+             pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
+             len = *pos2++;
+             pointer_chase = 1;
+           }
+         else
+           len = *pos2++;
+       }
+
+      if (pointer_chase == 0)
+       pos = pos2;
+
+      rr = (dns_rr_t *) pos;
+
+      switch (clib_net_to_host_u16 (rr->type))
+       {
+       case DNS_TYPE_PTR:
+         name = vnet_dns_labels_to_name (rr->rdata, response, &junk);
+         memcpy (rmp->name, name, vec_len (name));
+         ttl = clib_net_to_host_u32 (rr->ttl);
+         if (min_ttlp)
+           *min_ttlp = ttl;
+         rmp->name[vec_len (name)] = 0;
+         name_set = 1;
+         break;
+       default:
+         break;
+       }
+      /* Might as well stop ASAP */
+      if (name_set == 1)
+       break;
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      curpos = pos;
+    }
+
+  if (name_set == 0)
+    return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME;
+  return 0;
+}
+
 static void
 vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp)
 {
   dns_main_t *dm = &dns_main;
   vl_api_dns_resolve_name_reply_t *rmp;
   dns_cache_entry_t *ep;
+  dns_pending_request_t _t0, *t0 = &_t0;
   int rv;
 
   /* Sanitize the name slightly */
   mp->name[ARRAY_LEN (mp->name) - 1] = 0;
 
-  rv = dns_resolve_name (dm, mp->name, mp->client_index, mp->context, &ep);
+  t0->request_type = DNS_API_PENDING_NAME_TO_IP;
+  t0->client_index = mp->client_index;
+  t0->client_context = mp->context;
+
+  rv = vnet_dns_resolve_name (dm, mp->name, t0, &ep);
 
   /* Error, e.g. not enabled? Tell the user */
   if (rv < 0)
@@ -1103,6 +1466,86 @@ vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp)
   dns_cache_unlock (dm);
 }
 
+static void
+vl_api_dns_resolve_ip_t_handler (vl_api_dns_resolve_ip_t * mp)
+{
+  dns_main_t *dm = &dns_main;
+  vl_api_dns_resolve_ip_reply_t *rmp;
+  dns_cache_entry_t *ep;
+  int rv;
+  int i, len;
+  u8 *lookup_name = 0;
+  u8 digit, nybble;
+  dns_pending_request_t _t0, *t0 = &_t0;
+
+  if (mp->is_ip6)
+    {
+      for (i = 15; i >= 0; i--)
+       {
+         digit = mp->address[i];
+         nybble = (digit & 0x0F);
+         if (nybble > 9)
+           vec_add1 (lookup_name, (nybble - 10) + 'a');
+         else
+           vec_add1 (lookup_name, nybble + '0');
+         vec_add1 (lookup_name, '.');
+         nybble = (digit & 0xF0) >> 4;
+         if (nybble > 9)
+           vec_add1 (lookup_name, (nybble - 10) + 'a');
+         else
+           vec_add1 (lookup_name, nybble + '0');
+         vec_add1 (lookup_name, '.');
+       }
+      len = vec_len (lookup_name);
+      vec_validate (lookup_name, len + 8);
+      memcpy (lookup_name + len, "ip6.arpa", 8);
+    }
+  else
+    {
+      for (i = 3; i >= 0; i--)
+       {
+         digit = mp->address[i];
+         lookup_name = format (lookup_name, "%d.", digit);
+       }
+      lookup_name = format (lookup_name, "in-addr.arpa");
+    }
+
+  vec_add1 (lookup_name, 0);
+
+  t0->request_type = DNS_API_PENDING_IP_TO_NAME;
+  t0->client_index = mp->client_index;
+  t0->client_context = mp->context;
+
+  rv = vnet_dns_resolve_name (dm, lookup_name, t0, &ep);
+
+  vec_free (lookup_name);
+
+  /* Error, e.g. not enabled? Tell the user */
+  if (rv < 0)
+    {
+      REPLY_MACRO (VL_API_DNS_RESOLVE_IP_REPLY);
+      return;
+    }
+
+  /* Resolution pending? Don't reply... */
+  if (ep == 0)
+    return;
+
+  /* *INDENT-OFF* */
+  REPLY_MACRO2(VL_API_DNS_RESOLVE_IP_REPLY,
+  ({
+    rv = vnet_dns_response_to_name (ep->dns_response, rmp, 0 /* ttl-ptr */);
+    rmp->retval = clib_host_to_net_u32 (rv);
+  }));
+  /* *INDENT-ON* */
+
+  /*
+   * vnet_dns_resolve_name leaves the cache locked when it returns
+   * a cached result, so unlock it here.
+   */
+  dns_cache_unlock (dm);
+}
+
 #define vl_msg_name_crc_list
 #include <vpp/api/vpe_all_api_h.h>
 #undef vl_msg_name_crc_list
@@ -1118,7 +1561,8 @@ setup_message_id_table (api_main_t * am)
 #define foreach_dns_api_msg                             \
 _(DNS_ENABLE_DISABLE, dns_enable_disable)               \
 _(DNS_NAME_SERVER_ADD_DEL, dns_name_server_add_del)     \
-_(DNS_RESOLVE_NAME, dns_resolve_name)
+_(DNS_RESOLVE_NAME, dns_resolve_name)                  \
+_(DNS_RESOLVE_IP, dns_resolve_ip)
 
 static clib_error_t *
 dns_api_hookup (vlib_main_t * vm)
@@ -1171,19 +1615,6 @@ dns_init (vlib_main_t * vm)
   dm->max_ttl_in_seconds = 86400;
   dm->random_seed = 0xDEADDABE;
 
-  udp_register_dst_port (vm, UDP_DST_PORT_dns_reply, dns46_reply_node.index,
-                        1 /* is_ip4 */ );
-
-  udp_register_dst_port (vm, UDP_DST_PORT_dns_reply6, dns46_reply_node.index,
-                        0 /* is_ip4 */ );
-
-#if 0
-  udp_register_dst_port (vm, UDP_DST_PORT_dns, dns4_request_node.index,
-                        1 /* is_ip4 */ );
-  udp_register_dst_port (vm, UDP_DST_PORT_dns6, dns6_request_node.index,
-                        0 /* is_ip4 */ );
-#endif
-
   return 0;
 }
 
@@ -1369,8 +1800,9 @@ format_dns_reply_data (u8 * s, va_list * args)
   u8 *pos, *pos2;
   dns_rr_t *rr;
   int i;
-  int initial_pointer_chase = 0;
+  int pointer_chase = 0;
   u16 *tp;
+  u16 rrtype_host_byte_order;
 
   pos = pos2 = *curpos;
 
@@ -1378,11 +1810,11 @@ format_dns_reply_data (u8 * s, va_list * args)
     s = format (s, "    ");
 
   /* chase pointer? almost always yes here... */
-  if (pos2[0] == 0xc0)
+  if ((pos2[0] & 0xc0) == 0xc0)
     {
-      pos2 = reply + pos2[1];
-      pos += 2;
-      initial_pointer_chase = 1;
+      pos = pos2 + 2;
+      pos2 = reply + ((pos2[0] & 0x3f) << 8) + pos2[1];
+      pointer_chase = 1;
     }
 
   len = *pos2++;
@@ -1395,7 +1827,20 @@ format_dns_reply_data (u8 * s, va_list * args)
            vec_add1 (s, *pos2);
          pos2++;
        }
-      len = *pos2++;
+      if ((pos2[0] & 0xc0) == 0xc0)
+       {
+         /*
+          * If we've already done one pointer chase,
+          * do not move the pos pointer.
+          */
+         if (pointer_chase == 0)
+           pos = pos2 + 2;
+         pos2 = reply + ((pos2[0] & 0x3f) << 8) + pos2[1];
+         len = *pos2++;
+         pointer_chase = 1;
+       }
+      else
+       len = *pos2++;
       if (len)
        {
          if (verbose > 1)
@@ -1408,12 +1853,13 @@ format_dns_reply_data (u8 * s, va_list * args)
        }
     }
 
-  if (initial_pointer_chase == 0)
+  if (pointer_chase == 0)
     pos = pos2;
 
   rr = (dns_rr_t *) pos;
+  rrtype_host_byte_order = clib_net_to_host_u16 (rr->type);
 
-  switch (clib_net_to_host_u16 (rr->type))
+  switch (rrtype_host_byte_order)
     {
     case DNS_TYPE_A:
       if (verbose > 1)
@@ -1461,6 +1907,31 @@ format_dns_reply_data (u8 * s, va_list * args)
       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
       break;
 
+    case DNS_TYPE_HINFO:
+      {
+       /* Two counted strings. DGMS */
+       u8 *len;
+       u8 *curpos;
+       int i;
+       if (verbose > 1)
+         {
+           s = format (s, "HINFO: ");
+           len = rr->rdata;
+           curpos = len + 1;
+           for (i = 0; i < *len; i++)
+             vec_add1 (s, *curpos++);
+
+           vec_add1 (s, ' ');
+           len = curpos++;
+           for (i = 0; i < *len; i++)
+             vec_add1 (s, *curpos++);
+
+           vec_add1 (s, '\n');
+         }
+      }
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      break;
+
     case DNS_TYPE_NAMESERVER:
       if (verbose > 1)
        {
@@ -1468,8 +1939,11 @@ format_dns_reply_data (u8 * s, va_list * args)
          pos2 = rr->rdata;
 
          /* chase pointer? */
-         if (pos2[0] == 0xc0)
-           pos2 = reply + pos2[1];
+         if ((pos2[0] & 0xc0) == 0xc0)
+           {
+             pos = pos2 + 2;
+             pos2 = reply + ((pos2[0] & 0x3f) << 8) + pos2[1];
+           }
 
          len = *pos2++;
 
@@ -1528,12 +2002,16 @@ format_dns_reply_data (u8 * s, va_list * args)
       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
       break;
 
+    case DNS_TYPE_PTR:
     case DNS_TYPE_CNAME:
       if (verbose > 1)
        {
          tp = (u16 *) rr->rdata;
 
-         s = format (s, "CNAME: ");
+         if (rrtype_host_byte_order == DNS_TYPE_CNAME)
+           s = format (s, "CNAME: ");
+         else
+           s = format (s, "PTR: ");
 
          pos2 = rr->rdata;
 
@@ -1700,8 +2178,11 @@ format_dns_cache (u8 * s, va_list * args)
              else
                ss = "    ";
 
-             s = format (s, "%s%s -> %U", ss, ep->name,
-                         format_dns_reply, ep->dns_response, verbose);
+             if (verbose < 2 && ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
+               s = format (s, "%s%s -> %s", ss, ep->name, ep->cname);
+             else
+               s = format (s, "%s%s -> %U", ss, ep->name,
+                           format_dns_reply, ep->dns_response, verbose);
              if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
                {
                  f64 time_left = ep->expiration_time - now;
@@ -1733,10 +2214,13 @@ format_dns_cache (u8 * s, va_list * args)
         else
           ss = "    ";
 
-        s = format (s, "%s%s -> %U", ss, ep->name,
-                    format_dns_reply,
-                    ep->dns_response,
-                    verbose);
+        if (verbose < 2 && ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
+          s = format (s, "%s%s -> %s", ss, ep->name, ep->cname);
+        else
+          s = format (s, "%s%s -> %U", ss, ep->name,
+                      format_dns_reply,
+                      ep->dns_response,
+                      verbose);
         if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
           {
             f64 time_left = ep->expiration_time - now;
@@ -1744,6 +2228,10 @@ format_dns_cache (u8 * s, va_list * args)
               s = format (s, "  TTL left %.1f", time_left);
             else
               s = format (s, "  EXPIRED");
+
+            if (verbose > 2)
+              s = format (s, "    %d client notifications pending\n",
+                          vec_len(ep->pending_requests));
           }
       }
     else
@@ -1962,7 +2450,78 @@ static u8 dns_reply_data_initializer[] = {
   0x03, 0x63, 0x6f, 0x6d, 0x06, 0x61, 0x6b, 0x61, 0x64,
   0x6e, 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00,
 };
-#else
+
+/* bind8 (linux widget, w/ nasty double pointer chasees */
+static u8 dns_reply_data_initializer[] = {
+  /* 0 */
+  0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x08,
+  /* 8 */
+  0x00, 0x06, 0x00, 0x06, 0x0a, 0x6f, 0x72, 0x69,
+  /* 16 */
+  0x67, 0x69, 0x6e, 0x2d, 0x77, 0x77, 0x77, 0x05,
+  /* 24 */
+  0x63, 0x69, 0x73, 0x63, 0x6f, 0x03, 0x63, 0x6f,
+  /* 32 */
+  0x6d, 0x00, 0x00, 0xff, 0x00, 0x01, 0x0a, 0x6f,
+  /* 40 */
+  0x72, 0x69, 0x67, 0x69, 0x6e, 0x2d, 0x77, 0x77,
+  /* 48 */
+  0x77, 0x05, 0x43, 0x49, 0x53, 0x43, 0x4f, 0xc0,
+
+  /* 56 */
+  0x1d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05,
+
+  /* 64 */
+  0x9a, 0x00, 0x18, 0x15, 0x72, 0x63, 0x64,
+  0x6e, 0x39, 0x2d, 0x31, 0x34, 0x70, 0x2d, 0x64, 0x63,
+  0x7a, 0x30, 0x35, 0x6e, 0x2d, 0x67, 0x73, 0x73, 0x31,
+  0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02, 0x00, 0x01, 0x00,
+  0x00, 0x05, 0x9a, 0x00, 0x1a, 0x17, 0x61, 0x6c, 0x6c,
+  0x6e, 0x30, 0x31, 0x2d, 0x61, 0x67, 0x30, 0x39, 0x2d,
+  0x64, 0x63, 0x7a, 0x30, 0x33, 0x6e, 0x2d, 0x67, 0x73,
+  0x73, 0x31, 0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02, 0x00,
+  0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x10, 0x0d, 0x72,
+  0x74, 0x70, 0x35, 0x2d, 0x64, 0x6d, 0x7a, 0x2d, 0x67,
+  0x73, 0x73, 0x31, 0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02,
+  0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x18, 0x15,
+  0x6d, 0x74, 0x76, 0x35, 0x2d, 0x61, 0x70, 0x31, 0x30,
+  0x2d, 0x64, 0x63, 0x7a, 0x30, 0x36, 0x6e, 0x2d, 0x67,
+  0x73, 0x73, 0x31, 0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02,
+  0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x1b, 0x18,
+  0x73, 0x6e, 0x67, 0x64, 0x63, 0x30, 0x31, 0x2d, 0x61,
+  0x62, 0x30, 0x37, 0x2d, 0x64, 0x63, 0x7a, 0x30, 0x31,
+  0x6e, 0x2d, 0x67, 0x73, 0x73, 0x31, 0xc0, 0x17, 0xc0,
+  0x26, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05, 0x9a,
+  0x00, 0x1a, 0x17, 0x61, 0x65, 0x72, 0x30, 0x31, 0x2d,
+  0x72, 0x34, 0x63, 0x32, 0x35, 0x2d, 0x64, 0x63, 0x7a,
+  0x30, 0x31, 0x6e, 0x2d, 0x67, 0x73, 0x73, 0x31, 0xc0,
+  0x17, 0xc0, 0x26, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+  0x00, 0x81, 0x00, 0x04, 0x48, 0xa3, 0x04, 0xa1, 0xc0,
+  0x26, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x82,
+  0x00, 0x10, 0x20, 0x01, 0x04, 0x20, 0x12, 0x01, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a,
+  0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05,
+  0x9a, 0x00, 0x02, 0xc0, 0xf4, 0xc0, 0x0c, 0x00, 0x02,
+  0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x02, 0xc0,
+  0xcd, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
+  0x05, 0x9a, 0x00, 0x02, 0xc0, 0x8d, 0xc0, 0x0c, 0x00,
+  0x02, 0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x02,
+  0xc0, 0x43, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00,
+  0x00, 0x05, 0x9a, 0x00, 0x02, 0xc0, 0xa9, 0xc0, 0x0c,
+  0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00,
+  0x02, 0xc0, 0x67, 0xc0, 0x8d, 0x00, 0x01, 0x00, 0x01,
+  0x00, 0x00, 0x07, 0x08, 0x00, 0x04, 0x40, 0x66, 0xf6,
+  0x05, 0xc0, 0xa9, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+  0x07, 0x08, 0x00, 0x04, 0xad, 0x24, 0xe0, 0x64, 0xc0,
+  0x43, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x07, 0x08,
+  0x00, 0x04, 0x48, 0xa3, 0x04, 0x1c, 0xc0, 0xf4, 0x00,
+  0x01, 0x00, 0x01, 0x00, 0x00, 0x07, 0x08, 0x00, 0x04,
+  0xad, 0x26, 0xd4, 0x6c, 0xc0, 0x67, 0x00, 0x01, 0x00,
+  0x01, 0x00, 0x00, 0x07, 0x08, 0x00, 0x04, 0xad, 0x25,
+  0x90, 0x64, 0xc0, 0xcd, 0x00, 0x01, 0x00, 0x01, 0x00,
+  0x00, 0x07, 0x08, 0x00, 0x04, 0xad, 0x27, 0x70, 0x44,
+};
+
 /* google.com */
 static u8 dns_reply_data_initializer[] =
   { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x6,
@@ -1996,6 +2555,21 @@ static u8 dns_reply_data_initializer[] =
   0x57,
   0x0, 0x9, 0x0, 0x14, 0x4, 0x61, 0x6c, 0x74, 0x31, 0xc0, 0x9b
 };
+
+#else
+/* www.weatherlink.com */
+static u8 dns_reply_data_initializer[] = {
+  0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
+  0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x0b,
+  0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x6c, 0x69,
+  0x6e, 0x6b, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0xff,
+  0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00,
+  0x00, 0x0c, 0x9e, 0x00, 0x1f, 0x0e, 0x64, 0x33, 0x6b,
+  0x72, 0x30, 0x67, 0x75, 0x62, 0x61, 0x31, 0x64, 0x76,
+  0x77, 0x66, 0x0a, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x66,
+  0x72, 0x6f, 0x6e, 0x74, 0x03, 0x6e, 0x65, 0x74, 0x00,
+};
+
 #endif
 
 static clib_error_t *
@@ -2025,7 +2599,7 @@ test_dns_fmt_command_fn (vlib_main_t * vm,
 
   vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
 
-  memset (rmp, 0, sizeof (*rmp));
+  clib_memset (rmp, 0, sizeof (*rmp));
 
   rv = vnet_dns_response_to_reply (dns_reply_data, rmp, 0 /* ttl-ptr */ );
 
@@ -2103,8 +2677,305 @@ VLIB_CLI_COMMAND (test_dns_unfmt_command) =
   .function = test_dns_unfmt_command_fn,
 };
 /* *INDENT-ON* */
+
+static clib_error_t *
+test_dns_expire_command_fn (vlib_main_t * vm,
+                           unformat_input_t * input,
+                           vlib_cli_command_t * cmd)
+{
+  dns_main_t *dm = &dns_main;
+  u8 *name = 0;
+  uword *p;
+  clib_error_t *e;
+  dns_cache_entry_t *ep;
+
+  if (unformat (input, "%v", &name))
+    {
+      vec_add1 (name, 0);
+      _vec_len (name) -= 1;
+    }
+  else
+    return clib_error_return (0, "no name provided");
+
+  dns_cache_lock (dm);
+
+  p = hash_get_mem (dm->cache_entry_by_name, name);
+  if (!p)
+    {
+      dns_cache_unlock (dm);
+      e = clib_error_return (0, "%s is not in the cache...", name);
+      vec_free (name);
+      return e;
+    }
+
+  ep = pool_elt_at_index (dm->entries, p[0]);
+
+  ep->expiration_time = 0;
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (test_dns_expire_command) =
+{
+  .path = "test dns expire",
+  .short_help = "test dns expire <name>",
+  .function = test_dns_expire_command_fn,
+};
+/* *INDENT-ON* */
 #endif
 
+void
+vnet_send_dns6_reply (dns_main_t * dm, dns_pending_request_t * pr,
+                     dns_cache_entry_t * ep, vlib_buffer_t * b0)
+{
+  clib_warning ("Unimplemented...");
+}
+
+
+void
+vnet_send_dns4_reply (dns_main_t * dm, dns_pending_request_t * pr,
+                     dns_cache_entry_t * ep, vlib_buffer_t * b0)
+{
+  vlib_main_t *vm = dm->vlib_main;
+  u32 bi = 0;
+  fib_prefix_t prefix;
+  fib_node_index_t fei;
+  u32 sw_if_index, fib_index;
+  ip4_main_t *im4 = &ip4_main;
+  ip_lookup_main_t *lm4 = &im4->lookup_main;
+  ip_interface_address_t *ia = 0;
+  ip4_address_t *src_address;
+  ip4_header_t *ip;
+  udp_header_t *udp;
+  dns_header_t *dh;
+  vlib_frame_t *f;
+  u32 *to_next;
+  u8 *dns_response;
+  u8 *reply;
+  vl_api_dns_resolve_name_reply_t _rnr, *rnr = &_rnr;
+  vl_api_dns_resolve_ip_reply_t _rir, *rir = &_rir;
+  u32 ttl, tmp;
+  u32 qp_offset;
+  dns_query_t *qp;
+  dns_rr_t *rr;
+  u8 *rrptr;
+  int is_fail = 0;
+
+  ASSERT (ep && ep->dns_response);
+
+  if (pr->request_type == DNS_PEER_PENDING_NAME_TO_IP)
+    {
+      /* Quick and dirty way to dig up the A-record address. $$ FIXME */
+      clib_memset (rnr, 0, sizeof (*rnr));
+      if (vnet_dns_response_to_reply (ep->dns_response, rnr, &ttl))
+       {
+         /* clib_warning ("response_to_reply failed..."); */
+         is_fail = 1;
+       }
+      if (rnr->ip4_set == 0)
+       {
+         /* clib_warning ("No A-record..."); */
+         is_fail = 1;
+       }
+    }
+  else if (pr->request_type == DNS_PEER_PENDING_IP_TO_NAME)
+    {
+      clib_memset (rir, 0, sizeof (*rir));
+      if (vnet_dns_response_to_name (ep->dns_response, rir, &ttl))
+       {
+         /* clib_warning ("response_to_name failed..."); */
+         is_fail = 1;
+       }
+    }
+  else
+    {
+      clib_warning ("Unknown request type %d", pr->request_type);
+      return;
+    }
+
+  /* Initialize a buffer */
+  if (b0 == 0)
+    {
+      if (vlib_buffer_alloc (vm, &bi, 1) != 1)
+       return;
+      b0 = vlib_get_buffer (vm, bi);
+    }
+
+  if (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
+    vlib_buffer_free_one (vm, b0->next_buffer);
+
+  /*
+   * Reset the buffer. We recycle the DNS request packet in the cache
+   * hit case, and reply immediately from the request node.
+   *
+   * In the resolution-required / deferred case, resetting a freshly-allocated
+   * buffer won't hurt. We hope.
+   */
+  b0->flags |= (VNET_BUFFER_F_LOCALLY_ORIGINATED
+               | VLIB_BUFFER_TOTAL_LENGTH_VALID);
+  b0->current_data = 0;
+  b0->current_length = 0;
+  b0->total_length_not_including_first_buffer = 0;
+  vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;  /* "local0" */
+  vnet_buffer (b0)->sw_if_index[VLIB_TX] = 0;  /* default VRF for now */
+
+  /* Find a FIB path to the peer we're trying to answer */
+  clib_memcpy (&prefix.fp_addr.ip4, pr->dst_address, sizeof (ip4_address_t));
+  prefix.fp_proto = FIB_PROTOCOL_IP4;
+  prefix.fp_len = 32;
+
+  fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ );
+  if (fib_index == (u32) ~ 0)
+    {
+      clib_warning ("no fib table");
+      return;
+    }
+
+  fei = fib_table_lookup (fib_index, &prefix);
+
+  /* Couldn't find route to destination. Bail out. */
+  if (fei == FIB_NODE_INDEX_INVALID)
+    {
+      clib_warning ("no route to DNS server");
+      return;
+    }
+
+  sw_if_index = fib_entry_get_resolving_interface (fei);
+
+  if (sw_if_index == ~0)
+    {
+      clib_warning
+       ("route to %U exists, fei %d, get_resolving_interface returned"
+        " ~0", fei, format_ip4_address, &prefix.fp_addr);
+      return;
+    }
+
+  /* *INDENT-OFF* */
+  foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnumbered */,
+  ({
+    src_address = ip_interface_address_get_address (lm4, ia);
+    goto found_src_address;
+  }));
+  /* *INDENT-ON* */
+
+  clib_warning ("FIB BUG");
+  return;
+
+found_src_address:
+
+  ip = vlib_buffer_get_current (b0);
+  udp = (udp_header_t *) (ip + 1);
+  dns_response = (u8 *) (udp + 1);
+  clib_memset (ip, 0, sizeof (*ip) + sizeof (*udp));
+
+  /*
+   * Start with the variadic portion of the exercise.
+   * Turn the name into a set of DNS "labels". Max length
+   * per label is 63, enforce that.
+   */
+  reply = name_to_labels (pr->name);
+  vec_free (pr->name);
+
+  qp_offset = vec_len (reply);
+
+  /* Add space for the query header */
+  vec_validate (reply, qp_offset + sizeof (dns_query_t) - 1);
+
+  qp = (dns_query_t *) (reply + qp_offset);
+
+  if (pr->request_type == DNS_PEER_PENDING_NAME_TO_IP)
+    qp->type = clib_host_to_net_u16 (DNS_TYPE_A);
+  else
+    qp->type = clib_host_to_net_u16 (DNS_TYPE_PTR);
+
+  qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+
+  /* Punch in space for the dns_header_t */
+  vec_insert (reply, sizeof (dns_header_t), 0);
+
+  dh = (dns_header_t *) reply;
+
+  /* Transaction ID = pool index */
+  dh->id = pr->id;
+
+  /* Announce that we did a recursive lookup */
+  tmp = DNS_AA | DNS_RA | DNS_RD | DNS_OPCODE_QUERY | DNS_QR;
+  if (is_fail)
+    tmp |= DNS_RCODE_NAME_ERROR;
+  dh->flags = clib_host_to_net_u16 (tmp);
+  dh->qdcount = clib_host_to_net_u16 (1);
+  dh->anscount = (is_fail == 0) ? clib_host_to_net_u16 (1) : 0;
+  dh->nscount = 0;
+  dh->arcount = 0;
+
+  /* If the name resolution worked, cough up an appropriate RR */
+  if (is_fail == 0)
+    {
+      /* Add the answer. First, a name pointer (0xC00C) */
+      vec_add1 (reply, 0xC0);
+      vec_add1 (reply, 0x0C);
+
+      /* Now, add single A-rec RR */
+      if (pr->request_type == DNS_PEER_PENDING_NAME_TO_IP)
+       {
+         vec_add2 (reply, rrptr, sizeof (dns_rr_t) + sizeof (ip4_address_t));
+         rr = (dns_rr_t *) rrptr;
+
+         rr->type = clib_host_to_net_u16 (DNS_TYPE_A);
+         rr->class = clib_host_to_net_u16 (1 /* internet */ );
+         rr->ttl = clib_host_to_net_u32 (ttl);
+         rr->rdlength = clib_host_to_net_u16 (sizeof (ip4_address_t));
+         clib_memcpy (rr->rdata, rnr->ip4_address, sizeof (ip4_address_t));
+       }
+      else
+       {
+         /* Or a single PTR RR */
+         u8 *vecname = format (0, "%s", rir->name);
+         u8 *label_vec = name_to_labels (vecname);
+         vec_free (vecname);
+
+         vec_add2 (reply, rrptr, sizeof (dns_rr_t) + vec_len (label_vec));
+         rr = (dns_rr_t *) rrptr;
+         rr->type = clib_host_to_net_u16 (DNS_TYPE_PTR);
+         rr->class = clib_host_to_net_u16 (1 /* internet */ );
+         rr->ttl = clib_host_to_net_u32 (ttl);
+         rr->rdlength = clib_host_to_net_u16 (vec_len (label_vec));
+         clib_memcpy (rr->rdata, label_vec, vec_len (label_vec));
+         vec_free (label_vec);
+       }
+    }
+  clib_memcpy (dns_response, reply, vec_len (reply));
+
+  /* Set the packet length */
+  b0->current_length = sizeof (*ip) + sizeof (*udp) + vec_len (reply);
+
+  /* IP header */
+  ip->ip_version_and_header_length = 0x45;
+  ip->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
+  ip->ttl = 255;
+  ip->protocol = IP_PROTOCOL_UDP;
+  ip->src_address.as_u32 = src_address->as_u32;
+  clib_memcpy (ip->dst_address.as_u8, pr->dst_address,
+              sizeof (ip4_address_t));
+  ip->checksum = ip4_header_checksum (ip);
+
+  /* UDP header */
+  udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
+  udp->dst_port = pr->dst_port;
+  udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
+                                     vec_len (reply));
+  udp->checksum = 0;
+  vec_free (reply);
+
+  /* Ship it to ip4_lookup */
+  f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
+  to_next = vlib_frame_vector_args (f);
+  to_next[0] = bi;
+  f->n_vectors = 1;
+  vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *