Functional improvements, bug fixes
[vpp.git] / src / vnet / dns / dns.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/dns/dns.h>
17
18 #include <vnet/vnet.h>
19 #include <vnet/fib/fib.h>
20 #include <vlibmemory/api.h>
21
22 #include <vnet/udp/udp.h>
23
24 #include <vnet/vnet_msg_enum.h>
25
26 #define vl_typedefs             /* define message structures */
27 #include <vnet/vnet_all_api_h.h>
28 #undef vl_typedefs
29
30 #define vl_endianfun            /* define message structures */
31 #include <vnet/vnet_all_api_h.h>
32 #undef vl_endianfun
33
34 /* instantiate all the print functions we know about */
35 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
36 #define vl_printfun
37 #include <vnet/vnet_all_api_h.h>
38 #undef vl_printfun
39
40 #include <vlibapi/api_helper_macros.h>
41
42 dns_main_t dns_main;
43
44 static int
45 dns_cache_clear (dns_main_t * dm)
46 {
47   dns_cache_entry_t *ep;
48
49   if (dm->is_enabled == 0)
50     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
51
52   dns_cache_lock (dm);
53
54   /* *INDENT-OFF* */
55   pool_foreach (ep, dm->entries,
56   ({
57     vec_free (ep->name);
58     vec_free (ep->pending_requests);
59   }));
60   /* *INDENT-ON* */
61
62   pool_free (dm->entries);
63   hash_free (dm->cache_entry_by_name);
64   dm->cache_entry_by_name = hash_create_string (0, sizeof (uword));
65   vec_free (dm->unresolved_entries);
66   dns_cache_unlock (dm);
67   return 0;
68 }
69
70 static int
71 dns_enable_disable (dns_main_t * dm, int is_enable)
72 {
73   vlib_thread_main_t *tm = &vlib_thread_main;
74   u32 n_vlib_mains = tm->n_vlib_mains;
75
76   if (is_enable)
77     {
78       if (vec_len (dm->ip4_name_servers) == 0
79           && (vec_len (dm->ip6_name_servers) == 0))
80         return VNET_API_ERROR_NO_NAME_SERVERS;
81
82       if (dm->cache_entry_by_name == 0)
83         {
84           if (n_vlib_mains > 1)
85             dm->cache_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
86                                                      CLIB_CACHE_LINE_BYTES);
87
88           dm->cache_entry_by_name = hash_create_string (0, sizeof (uword));
89         }
90
91       dm->is_enabled = 1;
92     }
93   else
94     {
95       dns_cache_clear (dm);
96       dm->is_enabled = 0;
97     }
98   return 0;
99 }
100
101 static void vl_api_dns_enable_disable_t_handler
102   (vl_api_dns_enable_disable_t * mp)
103 {
104   vl_api_dns_enable_disable_reply_t *rmp;
105   dns_main_t *dm = &dns_main;
106   int rv;
107
108   rv = dns_enable_disable (dm, mp->enable);
109
110   REPLY_MACRO (VL_API_DNS_ENABLE_DISABLE_REPLY);
111 }
112
113 static int
114 dns6_name_server_add_del (dns_main_t * dm,
115                           u8 * server_address_as_u8, int is_add)
116 {
117   int i;
118   ip6_address_t *ap;
119
120   if (is_add)
121     {
122       /* Already there? done... */
123       for (i = 0; i < vec_len (dm->ip6_name_servers); i++)
124         {
125           if (!memcmp (dm->ip6_name_servers + i, server_address_as_u8,
126                        sizeof (ip6_address_t)))
127             return 0;
128         }
129
130       vec_add2 (dm->ip6_name_servers, ap, 1);
131       clib_memcpy (ap, server_address_as_u8, sizeof (*ap));
132     }
133   else
134     {
135       for (i = 0; i < vec_len (dm->ip6_name_servers); i++)
136         {
137           if (!memcmp (dm->ip6_name_servers + i, server_address_as_u8,
138                        sizeof (ip6_address_t)))
139             {
140               vec_delete (dm->ip6_name_servers, 1, i);
141               return 0;
142             }
143         }
144       return VNET_API_ERROR_NAME_SERVER_NOT_FOUND;
145     }
146   return 0;
147 }
148
149 static int
150 dns4_name_server_add_del (dns_main_t * dm,
151                           u8 * server_address_as_u8, int is_add)
152 {
153   int i;
154   ip4_address_t *ap;
155
156   if (is_add)
157     {
158       /* Already there? done... */
159       for (i = 0; i < vec_len (dm->ip4_name_servers); i++)
160         {
161           if (!memcmp (dm->ip4_name_servers + i, server_address_as_u8,
162                        sizeof (ip4_address_t)))
163             return 0;
164         }
165
166       vec_add2 (dm->ip4_name_servers, ap, 1);
167       clib_memcpy (ap, server_address_as_u8, sizeof (*ap));
168     }
169   else
170     {
171       for (i = 0; i < vec_len (dm->ip4_name_servers); i++)
172         {
173           if (!memcmp (dm->ip4_name_servers + i, server_address_as_u8,
174                        sizeof (ip4_address_t)))
175             {
176               vec_delete (dm->ip4_name_servers, 1, i);
177               return 0;
178             }
179         }
180       return VNET_API_ERROR_NAME_SERVER_NOT_FOUND;
181     }
182   return 0;
183 }
184
185 static void vl_api_dns_name_server_add_del_t_handler
186   (vl_api_dns_name_server_add_del_t * mp)
187 {
188   dns_main_t *dm = &dns_main;
189   vl_api_dns_name_server_add_del_reply_t *rmp;
190   int rv;
191
192   if (mp->is_ip6)
193     rv = dns6_name_server_add_del (dm, mp->server_address, mp->is_add);
194   else
195     rv = dns4_name_server_add_del (dm, mp->server_address, mp->is_add);
196
197   REPLY_MACRO (VL_API_DNS_NAME_SERVER_ADD_DEL_REPLY);
198 }
199
200 void
201 vnet_dns_send_dns4_request (dns_main_t * dm,
202                             dns_cache_entry_t * ep, ip4_address_t * server)
203 {
204   vlib_main_t *vm = dm->vlib_main;
205   f64 now = vlib_time_now (vm);
206   u32 bi;
207   vlib_buffer_t *b;
208   ip4_header_t *ip;
209   fib_prefix_t prefix;
210   fib_node_index_t fei;
211   u32 sw_if_index, fib_index;
212   udp_header_t *udp;
213   ip4_main_t *im4 = &ip4_main;
214   ip_lookup_main_t *lm4 = &im4->lookup_main;
215   ip_interface_address_t *ia = 0;
216   ip4_address_t *src_address;
217   u8 *dns_request;
218   vlib_frame_t *f;
219   u32 *to_next;
220
221   ASSERT (ep->dns_request);
222
223   /* Find a FIB path to the server */
224   clib_memcpy (&prefix.fp_addr.ip4, server, sizeof (*server));
225   prefix.fp_proto = FIB_PROTOCOL_IP4;
226   prefix.fp_len = 32;
227
228   fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ );
229   if (fib_index == (u32) ~ 0)
230     {
231       clib_warning ("no fib table");
232       return;
233     }
234
235   fei = fib_table_lookup (fib_index, &prefix);
236
237   /* Couldn't find route to destination. Bail out. */
238   if (fei == FIB_NODE_INDEX_INVALID)
239     {
240       clib_warning ("no route to DNS server");
241       return;
242     }
243
244   sw_if_index = fib_entry_get_resolving_interface (fei);
245
246   if (sw_if_index == ~0)
247     {
248       clib_warning
249         ("route to %U exists, fei %d, get_resolving_interface returned"
250          " ~0", format_ip4_address, &prefix.fp_addr, fei);
251       return;
252     }
253
254   /* *INDENT-OFF* */
255   foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnummbered */,
256   ({
257     src_address = ip_interface_address_get_address (lm4, ia);
258     goto found_src_address;
259   }));
260   /* *INDENT-ON* */
261
262   clib_warning ("FIB BUG");
263   return;
264
265 found_src_address:
266
267   /* Go get a buffer */
268   if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1)
269     return;
270
271   b = vlib_get_buffer (vm, bi);
272   b->current_length = sizeof (ip4_header_t) + sizeof (udp_header_t) +
273     vec_len (ep->dns_request);
274   b->total_length_not_including_first_buffer = 0;
275   b->flags =
276     VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED;
277   vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;    /* "local0" */
278   vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;    /* default VRF for now */
279
280   ip = vlib_buffer_get_current (b);
281   memset (ip, 0, sizeof (*ip));
282   udp = (udp_header_t *) (ip + 1);
283   memset (udp, 0, sizeof (*udp));
284
285   dns_request = (u8 *) (udp + 1);
286
287   /* IP header */
288   ip->ip_version_and_header_length = 0x45;
289   ip->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b));
290   ip->ttl = 255;
291   ip->protocol = IP_PROTOCOL_UDP;
292   ip->src_address.as_u32 = src_address->as_u32;
293   ip->dst_address.as_u32 = server->as_u32;
294   ip->checksum = ip4_header_checksum (ip);
295
296   /* UDP header */
297   udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns_reply);
298   udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
299   udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
300                                       vec_len (ep->dns_request));
301   udp->checksum = 0;
302
303   /* The actual DNS request */
304   clib_memcpy (dns_request, ep->dns_request, vec_len (ep->dns_request));
305
306   /* Ship it to ip4_lookup */
307   f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
308   to_next = vlib_frame_vector_args (f);
309   to_next[0] = bi;
310   f->n_vectors = 1;
311   vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
312
313   ep->retry_timer = now + 2.0;
314 }
315
316 void
317 vnet_dns_send_dns6_request (dns_main_t * dm,
318                             dns_cache_entry_t * ep, ip6_address_t * server)
319 {
320   vlib_main_t *vm = dm->vlib_main;
321   f64 now = vlib_time_now (vm);
322   u32 bi;
323   vlib_buffer_t *b;
324   ip6_header_t *ip;
325   fib_prefix_t prefix;
326   fib_node_index_t fei;
327   u32 sw_if_index, fib_index;
328   udp_header_t *udp;
329   ip6_main_t *im6 = &ip6_main;
330   ip_lookup_main_t *lm6 = &im6->lookup_main;
331   ip_interface_address_t *ia = 0;
332   ip6_address_t *src_address;
333   u8 *dns_request;
334   vlib_frame_t *f;
335   u32 *to_next;
336   int junk __attribute__ ((unused));
337
338   ASSERT (ep->dns_request);
339
340   /* Find a FIB path to the server */
341   clib_memcpy (&prefix.fp_addr, server, sizeof (*server));
342   prefix.fp_proto = FIB_PROTOCOL_IP6;
343   prefix.fp_len = 32;
344
345   fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ );
346   if (fib_index == (u32) ~ 0)
347     {
348       clib_warning ("no fib table");
349       return;
350     }
351
352   fei = fib_table_lookup (fib_index, &prefix);
353
354   /* Couldn't find route to destination. Bail out. */
355   if (fei == FIB_NODE_INDEX_INVALID)
356     {
357       clib_warning ("no route to DNS server");
358     }
359
360   sw_if_index = fib_entry_get_resolving_interface (fei);
361
362   /* *INDENT-OFF* */
363   foreach_ip_interface_address(lm6, ia, sw_if_index, 1 /* honor unnummbered */,
364   ({
365     src_address = ip_interface_address_get_address (lm6, ia);
366     goto found_src_address;
367   }));
368   /* *INDENT-ON* */
369
370   clib_warning ("FIB BUG");
371   return;
372
373 found_src_address:
374
375   /* Go get a buffer */
376   if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1)
377     return;
378
379   b = vlib_get_buffer (vm, bi);
380   b->current_length = sizeof (ip6_header_t) + sizeof (udp_header_t) +
381     vec_len (ep->dns_request);
382   b->total_length_not_including_first_buffer = 0;
383   b->flags =
384     VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED;
385
386   ip = vlib_buffer_get_current (b);
387   memset (ip, 0, sizeof (*ip));
388   udp = (udp_header_t *) (ip + 1);
389   memset (udp, 0, sizeof (*udp));
390
391   dns_request = (u8 *) (udp + 1);
392
393   /* IP header */
394   ip->ip_version_traffic_class_and_flow_label =
395     clib_host_to_net_u32 (0x6 << 28);
396
397   ip->payload_length =
398     clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b)
399                           - sizeof (ip6_header_t));
400   ip->hop_limit = 255;
401   ip->protocol = IP_PROTOCOL_UDP;
402   clib_memcpy (&ip->src_address, src_address, sizeof (ip6_address_t));
403   clib_memcpy (&ip->dst_address, server, sizeof (ip6_address_t));
404
405   /* UDP header */
406   udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns_reply);
407   udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
408   udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
409                                       vec_len (ep->dns_request));
410   udp->checksum = 0;
411   udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip, &junk);
412
413   /* The actual DNS request */
414   clib_memcpy (dns_request, ep->dns_request, vec_len (ep->dns_request));
415
416   /* Ship it to ip6_lookup */
417   f = vlib_get_frame_to_node (vm, ip6_lookup_node.index);
418   to_next = vlib_frame_vector_args (f);
419   to_next[0] = bi;
420   f->n_vectors = 1;
421
422   ep->retry_timer = now + 2.0;
423 }
424
425 /**
426  * Translate "foo.com" into "0x3 f o o 0x3 c o m 0x0"
427  * A historical / hysterical micro-TLV scheme. DGMS.
428  */
429 u8 *
430 name_to_labels (u8 * name)
431 {
432   int i;
433   int last_label_index;
434   u8 *rv;
435
436   rv = vec_dup (name);
437
438   /* punch in space for the first length */
439   vec_insert (rv, 1, 0);
440   last_label_index = 0;
441   i = 1;
442
443   while (i < vec_len (rv))
444     {
445       if (rv[i] == '.')
446         {
447           rv[last_label_index] = (i - last_label_index) - 1;
448           if ((i - last_label_index) > 63)
449             clib_warning ("stupid name, label length %d",
450                           i - last_label_index);
451           last_label_index = i;
452           rv[i] = 0;
453         }
454       i++;
455     }
456   /* Set the last real label length */
457   rv[last_label_index] = (i - last_label_index) - 1;
458
459   /*
460    * Add a [sic] NULL root label. Otherwise, the name parser can't figure out
461    * where to stop.
462    */
463   vec_add1 (rv, 0);
464   return rv;
465 }
466
467 /**
468  * arc-function for the above.
469  * Translate "0x3 f o o 0x3 c o m 0x0" into "foo.com"
470  * Produces a non-NULL-terminated u8 *vector. %v format is your friend.
471  */
472 u8 *
473 vnet_dns_labels_to_name (u8 * label, u8 * full_text, u8 ** parse_from_here)
474 {
475   u8 *reply = 0;
476   u16 offset;
477   u8 len;
478   int i;
479
480   *parse_from_here = 0;
481
482   /* chase initial pointer? */
483   if ((label[0] & 0xC0) == 0xC0)
484     {
485       *parse_from_here = label + 2;
486       offset = ((label[0] & 0x3f) << 8) + label[1];
487       label = full_text + offset;
488     }
489
490   len = *label++;
491
492   while (len)
493     {
494       for (i = 0; i < len; i++)
495         vec_add1 (reply, *label++);
496
497       /* chase pointer? */
498       if ((label[0] & 0xC0) == 0xC0)
499         {
500           *parse_from_here = label + 2;
501           offset = ((label[0] & 0x3f) << 8) + label[1];
502           label = full_text + offset;
503         }
504
505       len = *label++;
506       if (len)
507         vec_add1 (reply, '.');
508     }
509   if (*parse_from_here == 0)
510     *parse_from_here = label;
511   return reply;
512 }
513
514 void
515 vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
516 {
517   dns_header_t *h;
518   dns_query_t *qp;
519   u16 tmp;
520   u8 *request, *name_copy;
521   u32 qp_offset;
522
523   /* This can easily happen if sitting in GDB, etc. */
524   if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID || ep->server_fails > 1)
525     return;
526
527   /* Construct the dns request, if we haven't been here already */
528   if (vec_len (ep->dns_request) == 0)
529     {
530       /*
531        * Start with the variadic portion of the exercise.
532        * Turn the name into a set of DNS "labels". Max length
533        * per label is 63, enforce that.
534        */
535       request = name_to_labels (ep->name);
536       name_copy = vec_dup (request);
537       qp_offset = vec_len (request);
538
539       /*
540        * At least when testing against "known good" DNS servers:
541        * it turns out that sending 2x requests - one for an A-record
542        * and another for a AAAA-record - seems to work better than
543        * sending a DNS_TYPE_ALL request.
544        */
545
546       /* Add space for the query header */
547       vec_validate (request, 2 * qp_offset + 2 * sizeof (dns_query_t) - 1);
548
549       qp = (dns_query_t *) (request + qp_offset);
550
551       qp->type = clib_host_to_net_u16 (DNS_TYPE_A);
552       qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
553       qp++;
554       clib_memcpy (qp, name_copy, vec_len (name_copy));
555       qp = (dns_query_t *) (((u8 *) qp) + vec_len (name_copy));
556       vec_free (name_copy);
557
558       qp->type = clib_host_to_net_u16 (DNS_TYPE_AAAA);
559       qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
560
561       /* Punch in space for the dns_header_t */
562       vec_insert (request, sizeof (dns_header_t), 0);
563
564       h = (dns_header_t *) request;
565
566       /* Transaction ID = pool index */
567       h->id = clib_host_to_net_u16 (ep - dm->entries);
568
569       /* Ask for a recursive lookup */
570       tmp = DNS_RD | DNS_OPCODE_QUERY;
571       h->flags = clib_host_to_net_u16 (tmp);
572       h->qdcount = clib_host_to_net_u16 (2);
573       h->nscount = 0;
574       h->arcount = 0;
575
576       ep->dns_request = request;
577     }
578
579   /* Work out which server / address family we're going to use */
580
581   /* Retry using current server */
582   if (ep->retry_count++ < DNS_RETRIES_PER_SERVER)
583     {
584       if (ep->server_af == 1 /* ip6 */ )
585         {
586           if (vec_len (dm->ip6_name_servers))
587             {
588               vnet_dns_send_dns6_request
589                 (dm, ep, dm->ip6_name_servers + ep->server_rotor);
590               goto out;
591             }
592           else
593             ep->server_af = 0;
594         }
595       if (vec_len (dm->ip4_name_servers))
596         {
597           vnet_dns_send_dns4_request
598             (dm, ep, dm->ip4_name_servers + ep->server_rotor);
599           goto out;
600         }
601     }
602   else                          /* switch to a new server */
603     {
604       ep->retry_count = 1;
605       ep->server_rotor++;
606       if (ep->server_af == 1 /* ip6 */ )
607         {
608           if (ep->server_rotor >= vec_len (dm->ip6_name_servers))
609             {
610               ep->server_rotor = 0;
611               ep->server_af = vec_len (dm->ip4_name_servers) > 0 ? 0 : 1;
612             }
613         }
614       else
615         {
616           if (ep->server_rotor >= vec_len (dm->ip4_name_servers))
617             {
618               ep->server_rotor = 0;
619               ep->server_af = vec_len (dm->ip6_name_servers) > 0 ? 1 : 0;
620             }
621         }
622     }
623
624   if (ep->server_af == 1 /* ip6 */ )
625     vnet_dns_send_dns6_request
626       (dm, ep, dm->ip6_name_servers + ep->server_rotor);
627   else
628     vnet_dns_send_dns4_request
629       (dm, ep, dm->ip4_name_servers + ep->server_rotor);
630
631 out:
632
633   vlib_process_signal_event_mt (dm->vlib_main, dns_resolver_node.index,
634                                 DNS_RESOLVER_EVENT_PENDING, 0);
635 }
636
637 int
638 vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index)
639 {
640   dns_cache_entry_t *ep;
641   int i;
642
643   if (dm->is_enabled == 0)
644     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
645
646   if (pool_is_free_index (dm->entries, index))
647     return VNET_API_ERROR_NO_SUCH_ENTRY;
648
649   ep = pool_elt_at_index (dm->entries, index);
650   if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_VALID))
651     {
652       for (i = 0; i < vec_len (dm->unresolved_entries); i++)
653         if (index == dm->unresolved_entries[i])
654           {
655             vec_delete (dm->unresolved_entries, 1, i);
656             goto found;
657           }
658       clib_warning ("pool elt %d supposedly pending, but not found...",
659                     index);
660     }
661
662 found:
663   hash_unset_mem (dm->cache_entry_by_name, ep->name);
664   vec_free (ep->name);
665   vec_free (ep->pending_requests);
666   pool_put (dm->entries, ep);
667
668   return 0;
669 }
670
671 static int
672 dns_delete_by_name (dns_main_t * dm, u8 * name)
673 {
674   int rv;
675   uword *p;
676
677   if (dm->is_enabled == 0)
678     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
679
680   dns_cache_lock (dm);
681   p = hash_get_mem (dm->cache_entry_by_name, name);
682   if (!p)
683     {
684       dns_cache_unlock (dm);
685       return VNET_API_ERROR_NO_SUCH_ENTRY;
686     }
687   rv = vnet_dns_delete_entry_by_index_nolock (dm, p[0]);
688
689   dns_cache_unlock (dm);
690
691   return rv;
692 }
693
694 static int
695 delete_random_entry (dns_main_t * dm)
696 {
697   int rv;
698   u32 victim_index, start_index, i;
699   u32 limit;
700   dns_cache_entry_t *ep;
701
702   if (dm->is_enabled == 0)
703     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
704
705   /*
706    * Silence spurious coverity warning. We know pool_elts >> 0, or
707    * we wouldn't be here...
708    */
709 #ifdef __COVERITY__
710   if (pool_elts (dm->entries) == 0)
711     return VNET_API_ERROR_UNSPECIFIED;
712 #endif
713
714   dns_cache_lock (dm);
715   limit = pool_elts (dm->entries);
716   start_index = random_u32 (&dm->random_seed) % limit;
717
718   for (i = 0; i < limit; i++)
719     {
720       victim_index = (start_index + i) % limit;
721
722       if (!pool_is_free_index (dm->entries, victim_index))
723         {
724           ep = pool_elt_at_index (dm->entries, victim_index);
725           /* Delete only valid, non-static entries */
726           if ((ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
727               && ((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0))
728             {
729               rv = vnet_dns_delete_entry_by_index_nolock (dm, victim_index);
730               dns_cache_unlock (dm);
731               return rv;
732             }
733         }
734     }
735   dns_cache_unlock (dm);
736
737   clib_warning ("Couldn't find an entry to delete?");
738   return VNET_API_ERROR_UNSPECIFIED;
739 }
740
741 static int
742 dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data)
743 {
744   dns_cache_entry_t *ep;
745   uword *p;
746   int rv;
747
748   if (dm->is_enabled == 0)
749     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
750
751   dns_cache_lock (dm);
752   p = hash_get_mem (dm->cache_entry_by_name, name);
753   if (p)
754     {
755       dns_cache_unlock (dm);
756       return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
757     }
758
759   if (pool_elts (dm->entries) == dm->name_cache_size)
760     {
761       /* Will only fail if the cache is totally filled w/ static entries... */
762       rv = delete_random_entry (dm);
763       if (rv)
764         {
765           dns_cache_unlock (dm);
766           return rv;
767         }
768     }
769
770   pool_get (dm->entries, ep);
771   memset (ep, 0, sizeof (*ep));
772
773   /* Note: consumes the name vector */
774   ep->name = name;
775   hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
776   ep->flags = DNS_CACHE_ENTRY_FLAG_VALID | DNS_CACHE_ENTRY_FLAG_STATIC;
777   ep->dns_response = dns_reply_data;
778
779   dns_cache_unlock (dm);
780   return 0;
781 }
782
783 int
784 vnet_dns_resolve_name (dns_main_t * dm, u8 * name, dns_pending_request_t * t,
785                        dns_cache_entry_t ** retp)
786 {
787   dns_cache_entry_t *ep;
788   int rv;
789   f64 now;
790   uword *p;
791   dns_pending_request_t *pr;
792   int count;
793
794   now = vlib_time_now (dm->vlib_main);
795
796   /* In case we can't actually answer the question right now... */
797   *retp = 0;
798
799   dns_cache_lock (dm);
800 search_again:
801   p = hash_get_mem (dm->cache_entry_by_name, name);
802   if (p)
803     {
804       ep = pool_elt_at_index (dm->entries, p[0]);
805       if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
806         {
807           /* Has the entry expired? */
808           if (((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0)
809               && (now > ep->expiration_time))
810             {
811               int i;
812               u32 *indices_to_delete = 0;
813
814               /*
815                * Take out the rest of the resolution chain
816                * This isn't optimal, but it won't happen very often.
817                */
818               while (ep)
819                 {
820                   if ((ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME))
821                     {
822                       vec_add1 (indices_to_delete, ep - dm->entries);
823
824                       p = hash_get_mem (dm->cache_entry_by_name, ep->cname);
825                       if (!p)
826                         break;
827                       ep = pool_elt_at_index (dm->entries, p[0]);
828                     }
829                   else
830                     {
831                       vec_add1 (indices_to_delete, ep - dm->entries);
832                       break;
833                     }
834                 }
835               for (i = 0; i < vec_len (indices_to_delete); i++)
836                 {
837                   /* Reenable to watch re-resolutions */
838                   if (0)
839                     {
840                       ep = pool_elt_at_index (dm->entries,
841                                               indices_to_delete[i]);
842                       clib_warning ("Re-resolve %s", ep->name);
843                     }
844
845                   vnet_dns_delete_entry_by_index_nolock
846                     (dm, indices_to_delete[i]);
847                 }
848               vec_free (indices_to_delete);
849               /* Yes, kill it... */
850               goto re_resolve;
851             }
852
853           if (ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
854             {
855               name = ep->cname;
856               goto search_again;
857             }
858
859           /* Note: caller must drop the lock! */
860           *retp = ep;
861           return (0);
862         }
863       else
864         {
865           /*
866            * Resolution pending. Add request to the pending vector
867            * by copying the template request
868            */
869           vec_add2 (ep->pending_requests, pr, 1);
870           memcpy (pr, t, sizeof (*pr));
871           dns_cache_unlock (dm);
872           return (0);
873         }
874     }
875
876 re_resolve:
877   if (pool_elts (dm->entries) == dm->name_cache_size)
878     {
879       /* Will only fail if the cache is totally filled w/ static entries... */
880       rv = delete_random_entry (dm);
881       if (rv)
882         {
883           dns_cache_unlock (dm);
884           return rv;
885         }
886     }
887
888   /* add new hash table entry */
889   pool_get (dm->entries, ep);
890   memset (ep, 0, sizeof (*ep));
891
892   ep->name = format (0, "%s%c", name, 0);
893   _vec_len (ep->name) = vec_len (ep->name) - 1;
894
895   hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
896
897   vec_add1 (dm->unresolved_entries, ep - dm->entries);
898   vec_add2 (ep->pending_requests, pr, 1);
899
900   pr->request_type = t->request_type;
901
902   /* Remember details so we can reply later... */
903   if (t->request_type == DNS_API_PENDING_NAME_TO_IP ||
904       t->request_type == DNS_API_PENDING_IP_TO_NAME)
905     {
906       pr->client_index = t->client_index;
907       pr->client_context = t->client_context;
908     }
909   else
910     {
911       pr->client_index = ~0;
912       pr->is_ip6 = t->is_ip6;
913       pr->dst_port = t->dst_port;
914       pr->id = t->id;
915       pr->name = t->name;
916       if (t->is_ip6)
917         count = 16;
918       else
919         count = 4;
920       clib_memcpy (pr->dst_address, t->dst_address, count);
921     }
922
923   vnet_send_dns_request (dm, ep);
924   dns_cache_unlock (dm);
925   return 0;
926 }
927
928 #define foreach_notification_to_move            \
929 _(pending_requests)
930
931 /**
932  * Handle cname indirection. JFC. Called with the cache locked.
933  * returns 0 if the reply is not a CNAME.
934  */
935
936 int
937 vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply)
938 {
939   dns_header_t *h;
940   dns_query_t *qp;
941   dns_rr_t *rr;
942   u8 *curpos;
943   u8 *pos, *pos2;
944   int len, i;
945   u8 *cname = 0;
946   u8 *request = 0;
947   u32 qp_offset;
948   u16 flags;
949   u16 rcode;
950   dns_cache_entry_t *ep, *next_ep;
951   f64 now;
952
953   h = (dns_header_t *) reply;
954   flags = clib_net_to_host_u16 (h->flags);
955   rcode = flags & DNS_RCODE_MASK;
956
957   /* See if the response is OK */
958   switch (rcode)
959     {
960     case DNS_RCODE_NO_ERROR:
961       break;
962
963     case DNS_RCODE_NAME_ERROR:
964     case DNS_RCODE_FORMAT_ERROR:
965     case DNS_RCODE_SERVER_FAILURE:
966     case DNS_RCODE_NOT_IMPLEMENTED:
967     case DNS_RCODE_REFUSED:
968       return -1;
969     }
970
971   curpos = (u8 *) (h + 1);
972   pos = curpos;
973   len = *pos++;
974
975   /* Skip the questions */
976   for (i = 0; i < clib_net_to_host_u16 (h->qdcount); i++)
977     {
978       while (len)
979         {
980           pos += len;
981           len = *pos++;
982         }
983       pos += sizeof (dns_query_t);
984     }
985   pos2 = pos;
986   /* expect a pointer chase here for a CNAME record */
987   if ((pos2[0] & 0xC0) == 0xC0)
988     pos += 2;
989   else
990     return 0;
991
992   /* Walk the answer(s) to see what to do next */
993   for (i = 0; i < clib_net_to_host_u16 (h->anscount); i++)
994     {
995       rr = (dns_rr_t *) pos;
996       switch (clib_net_to_host_u16 (rr->type))
997         {
998           /* Real address record? Done.. */
999         case DNS_TYPE_A:
1000         case DNS_TYPE_AAAA:
1001           return 0;
1002           /* Chase a CNAME pointer? */
1003         case DNS_TYPE_CNAME:
1004           goto chase_chain;
1005
1006           /* Some other junk, e.g. a nameserver... */
1007         default:
1008           break;
1009         }
1010       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1011     }
1012
1013   /* Neither a CNAME nor a real address. Try another server */
1014   flags &= ~DNS_RCODE_MASK;
1015   flags |= DNS_RCODE_NAME_ERROR;
1016   h->flags = clib_host_to_net_u16 (flags);
1017   return -1;
1018
1019 chase_chain:
1020   /* This is a CNAME record, chase the name chain. */
1021
1022   /* The last request is no longer pending.. */
1023   for (i = 0; i < vec_len (dm->unresolved_entries); i++)
1024     if (ep_index == dm->unresolved_entries[i])
1025       {
1026         vec_delete (dm->unresolved_entries, 1, i);
1027         goto found_last_request;
1028       }
1029   clib_warning ("pool elt %d supposedly pending, but not found...", ep_index);
1030
1031 found_last_request:
1032
1033   now = vlib_time_now (dm->vlib_main);
1034   cname = vnet_dns_labels_to_name (rr->rdata, reply, &pos2);
1035   /* Save the cname */
1036   vec_add1 (cname, 0);
1037   _vec_len (cname) -= 1;
1038   ep = pool_elt_at_index (dm->entries, ep_index);
1039   ep->cname = cname;
1040   ep->flags |= (DNS_CACHE_ENTRY_FLAG_CNAME | DNS_CACHE_ENTRY_FLAG_VALID);
1041   /* Save the response */
1042   if (ep->dns_response)
1043     vec_free (ep->dns_response);
1044   ep->dns_response = reply;
1045   /* Set up expiration time */
1046   ep->expiration_time = now + clib_net_to_host_u32 (rr->ttl);
1047
1048   pool_get (dm->entries, next_ep);
1049
1050   /* Need to recompute ep post pool-get */
1051   ep = pool_elt_at_index (dm->entries, ep_index);
1052
1053   memset (next_ep, 0, sizeof (*next_ep));
1054   next_ep->name = vec_dup (cname);
1055   vec_add1 (next_ep->name, 0);
1056   _vec_len (next_ep->name) -= 1;
1057
1058   hash_set_mem (dm->cache_entry_by_name, next_ep->name,
1059                 next_ep - dm->entries);
1060
1061   /* Use the same server */
1062   next_ep->server_rotor = ep->server_rotor;
1063   next_ep->server_af = ep->server_af;
1064
1065   /* Move notification data to the next name in the chain */
1066 #define _(a) next_ep->a = ep->a; ep->a = 0;
1067   foreach_notification_to_move;
1068 #undef _
1069
1070   request = name_to_labels (cname);
1071
1072   qp_offset = vec_len (request);
1073
1074   /* Add space for the query header */
1075   vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
1076
1077   qp = (dns_query_t *) (request + qp_offset);
1078
1079   qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
1080   qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1081
1082   /* Punch in space for the dns_header_t */
1083   vec_insert (request, sizeof (dns_header_t), 0);
1084
1085   h = (dns_header_t *) request;
1086
1087   /* Transaction ID = pool index */
1088   h->id = clib_host_to_net_u16 (next_ep - dm->entries);
1089
1090   /* Ask for a recursive lookup */
1091   h->flags = clib_host_to_net_u16 (DNS_RD | DNS_OPCODE_QUERY);
1092   h->qdcount = clib_host_to_net_u16 (1);
1093   h->nscount = 0;
1094   h->arcount = 0;
1095
1096   next_ep->dns_request = request;
1097   next_ep->retry_timer = now + 2.0;
1098   next_ep->retry_count = 0;
1099
1100   /*
1101    * Enable this to watch recursive resolution happen...
1102    * fformat (stdout, "%U", format_dns_reply, request, 2);
1103    */
1104
1105   vec_add1 (dm->unresolved_entries, next_ep - dm->entries);
1106   vnet_send_dns_request (dm, next_ep);
1107   return (1);
1108 }
1109
1110 int
1111 vnet_dns_response_to_reply (u8 * response,
1112                             vl_api_dns_resolve_name_reply_t * rmp,
1113                             u32 * min_ttlp)
1114 {
1115   dns_header_t *h;
1116   dns_query_t *qp;
1117   dns_rr_t *rr;
1118   int i, limit;
1119   u8 len;
1120   u8 *curpos, *pos, *pos2;
1121   u16 flags;
1122   u16 rcode;
1123   u32 ttl;
1124   int pointer_chase;
1125
1126   h = (dns_header_t *) response;
1127   flags = clib_net_to_host_u16 (h->flags);
1128   rcode = flags & DNS_RCODE_MASK;
1129
1130   /* See if the response is OK, etc. */
1131   switch (rcode)
1132     {
1133     default:
1134     case DNS_RCODE_NO_ERROR:
1135       break;
1136
1137     case DNS_RCODE_NAME_ERROR:
1138     case DNS_RCODE_FORMAT_ERROR:
1139       return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME;
1140
1141     case DNS_RCODE_SERVER_FAILURE:
1142     case DNS_RCODE_NOT_IMPLEMENTED:
1143     case DNS_RCODE_REFUSED:
1144       return VNET_API_ERROR_NAME_SERVER_NEXT_SERVER;
1145     }
1146
1147   /* No answers? Loser... */
1148   if (clib_net_to_host_u16 (h->anscount) < 1)
1149     return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
1150
1151   curpos = (u8 *) (h + 1);
1152
1153   /* Skip the name we asked about */
1154   pos = curpos;
1155   len = *pos++;
1156   /* Should never happen, but stil... */
1157   if ((len & 0xC0) == 0xC0)
1158     curpos += 2;
1159   else
1160     {
1161       /* skip the name / label-set */
1162       while (len)
1163         {
1164           pos += len;
1165           len = *pos++;
1166         }
1167       curpos = pos;
1168     }
1169   /* Skip queries */
1170   limit = clib_net_to_host_u16 (h->qdcount);
1171   qp = (dns_query_t *) curpos;
1172   qp += limit;
1173   curpos = (u8 *) qp;
1174
1175   /* Parse answers */
1176   limit = clib_net_to_host_u16 (h->anscount);
1177
1178   for (i = 0; i < limit; i++)
1179     {
1180       pos = pos2 = curpos;
1181       pointer_chase = 0;
1182
1183       /* Expect pointer chases in the answer section... */
1184       if ((pos2[0] & 0xC0) == 0xC0)
1185         {
1186           pos = pos2 + 2;
1187           pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
1188           pointer_chase = 1;
1189         }
1190
1191       len = *pos2++;
1192
1193       while (len)
1194         {
1195           pos2 += len;
1196           if ((pos2[0] & 0xc0) == 0xc0)
1197             {
1198               /*
1199                * If we've already done one pointer chase,
1200                * do not move the pos pointer.
1201                */
1202               if (pointer_chase == 0)
1203                 pos = pos2 + 2;
1204               pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
1205               len = *pos2++;
1206               pointer_chase = 1;
1207             }
1208           else
1209             len = *pos2++;
1210         }
1211
1212       if (pointer_chase == 0)
1213         pos = pos2;
1214
1215       rr = (dns_rr_t *) pos;
1216
1217       switch (clib_net_to_host_u16 (rr->type))
1218         {
1219         case DNS_TYPE_A:
1220           /* Collect an ip4 address. Do not pass go. Do not collect $200 */
1221           memcpy (rmp->ip4_address, rr->rdata, sizeof (ip4_address_t));
1222           rmp->ip4_set = 1;
1223           ttl = clib_net_to_host_u32 (rr->ttl);
1224           if (min_ttlp && *min_ttlp > ttl)
1225             *min_ttlp = ttl;
1226           break;
1227         case DNS_TYPE_AAAA:
1228           /* Collect an ip6 address. Do not pass go. Do not collect $200 */
1229           memcpy (rmp->ip6_address, rr->rdata, sizeof (ip6_address_t));
1230           ttl = clib_net_to_host_u32 (rr->ttl);
1231           if (min_ttlp && *min_ttlp > ttl)
1232             *min_ttlp = ttl;
1233           rmp->ip6_set = 1;
1234           break;
1235
1236         default:
1237           break;
1238         }
1239       /* Might as well stop ASAP */
1240       if (rmp->ip4_set && rmp->ip6_set)
1241         break;
1242       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1243       curpos = pos;
1244     }
1245
1246   if ((rmp->ip4_set + rmp->ip6_set) == 0)
1247     return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
1248   return 0;
1249 }
1250
1251 int
1252 vnet_dns_response_to_name (u8 * response,
1253                            vl_api_dns_resolve_ip_reply_t * rmp,
1254                            u32 * min_ttlp)
1255 {
1256   dns_header_t *h;
1257   dns_query_t *qp;
1258   dns_rr_t *rr;
1259   int i, limit;
1260   u8 len;
1261   u8 *curpos, *pos, *pos2;
1262   u16 flags;
1263   u16 rcode;
1264   u8 *name;
1265   u32 ttl;
1266   u8 *junk __attribute__ ((unused));
1267   int name_set = 0;
1268   int pointer_chase;
1269
1270   h = (dns_header_t *) response;
1271   flags = clib_net_to_host_u16 (h->flags);
1272   rcode = flags & DNS_RCODE_MASK;
1273
1274   /* See if the response is OK, etc. */
1275   switch (rcode)
1276     {
1277     default:
1278     case DNS_RCODE_NO_ERROR:
1279       break;
1280
1281     case DNS_RCODE_NAME_ERROR:
1282     case DNS_RCODE_FORMAT_ERROR:
1283       return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME;
1284
1285     case DNS_RCODE_SERVER_FAILURE:
1286     case DNS_RCODE_NOT_IMPLEMENTED:
1287     case DNS_RCODE_REFUSED:
1288       return VNET_API_ERROR_NAME_SERVER_NEXT_SERVER;
1289     }
1290
1291   /* No answers? Loser... */
1292   if (clib_net_to_host_u16 (h->anscount) < 1)
1293     return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
1294
1295   curpos = (u8 *) (h + 1);
1296
1297   /* Skip the name we asked about */
1298   pos = curpos;
1299   len = *pos++;
1300   /* Should never happen, but stil... */
1301   if ((len & 0xC0) == 0xC0)
1302     curpos += 2;
1303   else
1304     {
1305       /* skip the name / label-set */
1306       while (len)
1307         {
1308           pos += len;
1309           len = *pos++;
1310         }
1311       curpos = pos;
1312     }
1313   /* Skip queries */
1314   limit = clib_net_to_host_u16 (h->qdcount);
1315   qp = (dns_query_t *) curpos;
1316   qp += limit;
1317   curpos = (u8 *) qp;
1318
1319   /* Parse answers */
1320   limit = clib_net_to_host_u16 (h->anscount);
1321
1322   for (i = 0; i < limit; i++)
1323     {
1324       pos = pos2 = curpos;
1325       pointer_chase = 0;
1326
1327       /* Expect pointer chases in the answer section... */
1328       if ((pos2[0] & 0xC0) == 0xC0)
1329         {
1330           pos = pos2 + 2;
1331           pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
1332           pointer_chase = 1;
1333         }
1334
1335       len = *pos2++;
1336
1337       while (len)
1338         {
1339           pos2 += len;
1340           if ((pos2[0] & 0xc0) == 0xc0)
1341             {
1342               /*
1343                * If we've already done one pointer chase,
1344                * do not move the pos pointer.
1345                */
1346               if (pointer_chase == 0)
1347                 pos = pos2 + 2;
1348               pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
1349               len = *pos2++;
1350               pointer_chase = 1;
1351             }
1352           else
1353             len = *pos2++;
1354         }
1355
1356       if (pointer_chase == 0)
1357         pos = pos2;
1358
1359       rr = (dns_rr_t *) pos;
1360
1361       switch (clib_net_to_host_u16 (rr->type))
1362         {
1363         case DNS_TYPE_PTR:
1364           name = vnet_dns_labels_to_name (rr->rdata, response, &junk);
1365           memcpy (rmp->name, name, vec_len (name));
1366           ttl = clib_net_to_host_u32 (rr->ttl);
1367           if (min_ttlp)
1368             *min_ttlp = ttl;
1369           rmp->name[vec_len (name)] = 0;
1370           name_set = 1;
1371           break;
1372         default:
1373           break;
1374         }
1375       /* Might as well stop ASAP */
1376       if (name_set == 1)
1377         break;
1378       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1379       curpos = pos;
1380     }
1381
1382   if (name_set == 0)
1383     return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME;
1384   return 0;
1385 }
1386
1387 static void
1388 vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp)
1389 {
1390   dns_main_t *dm = &dns_main;
1391   vl_api_dns_resolve_name_reply_t *rmp;
1392   dns_cache_entry_t *ep;
1393   dns_pending_request_t _t0, *t0 = &_t0;
1394   int rv;
1395
1396   /* Sanitize the name slightly */
1397   mp->name[ARRAY_LEN (mp->name) - 1] = 0;
1398
1399   t0->request_type = DNS_API_PENDING_NAME_TO_IP;
1400   t0->client_index = mp->client_index;
1401   t0->client_context = mp->context;
1402
1403   rv = vnet_dns_resolve_name (dm, mp->name, t0, &ep);
1404
1405   /* Error, e.g. not enabled? Tell the user */
1406   if (rv < 0)
1407     {
1408       REPLY_MACRO (VL_API_DNS_RESOLVE_NAME_REPLY);
1409       return;
1410     }
1411
1412   /* Resolution pending? Don't reply... */
1413   if (ep == 0)
1414     return;
1415
1416   /* *INDENT-OFF* */
1417   REPLY_MACRO2(VL_API_DNS_RESOLVE_NAME_REPLY,
1418   ({
1419     rv = vnet_dns_response_to_reply (ep->dns_response, rmp, 0 /* ttl-ptr */);
1420     rmp->retval = clib_host_to_net_u32 (rv);
1421   }));
1422   /* *INDENT-ON* */
1423
1424   /*
1425    * dns_resolve_name leaves the cache locked when it returns
1426    * a cached result, so unlock it here.
1427    */
1428   dns_cache_unlock (dm);
1429 }
1430
1431 static void
1432 vl_api_dns_resolve_ip_t_handler (vl_api_dns_resolve_ip_t * mp)
1433 {
1434   dns_main_t *dm = &dns_main;
1435   vl_api_dns_resolve_ip_reply_t *rmp;
1436   dns_cache_entry_t *ep;
1437   int rv;
1438   int i, len;
1439   u8 *lookup_name = 0;
1440   u8 digit, nybble;
1441   dns_pending_request_t _t0, *t0 = &_t0;
1442
1443   if (mp->is_ip6)
1444     {
1445       for (i = 15; i >= 0; i--)
1446         {
1447           digit = mp->address[i];
1448           nybble = (digit & 0x0F);
1449           if (nybble > 9)
1450             vec_add1 (lookup_name, (nybble - 10) + 'a');
1451           else
1452             vec_add1 (lookup_name, nybble + '0');
1453           vec_add1 (lookup_name, '.');
1454           nybble = (digit & 0xF0) >> 4;
1455           if (nybble > 9)
1456             vec_add1 (lookup_name, (nybble - 10) + 'a');
1457           else
1458             vec_add1 (lookup_name, nybble + '0');
1459           vec_add1 (lookup_name, '.');
1460         }
1461       len = vec_len (lookup_name);
1462       vec_validate (lookup_name, len + 8);
1463       memcpy (lookup_name + len, "ip6.arpa", 8);
1464     }
1465   else
1466     {
1467       for (i = 3; i >= 0; i--)
1468         {
1469           digit = mp->address[i];
1470           lookup_name = format (lookup_name, "%d.", digit);
1471         }
1472       lookup_name = format (lookup_name, "in-addr.arpa");
1473     }
1474
1475   vec_add1 (lookup_name, 0);
1476
1477   t0->request_type = DNS_API_PENDING_IP_TO_NAME;
1478   t0->client_index = mp->client_index;
1479   t0->client_context = mp->context;
1480
1481   rv = vnet_dns_resolve_name (dm, lookup_name, t0, &ep);
1482
1483   vec_free (lookup_name);
1484
1485   /* Error, e.g. not enabled? Tell the user */
1486   if (rv < 0)
1487     {
1488       REPLY_MACRO (VL_API_DNS_RESOLVE_IP_REPLY);
1489       return;
1490     }
1491
1492   /* Resolution pending? Don't reply... */
1493   if (ep == 0)
1494     return;
1495
1496   /* *INDENT-OFF* */
1497   REPLY_MACRO2(VL_API_DNS_RESOLVE_IP_REPLY,
1498   ({
1499     rv = vnet_dns_response_to_name (ep->dns_response, rmp, 0 /* ttl-ptr */);
1500     rmp->retval = clib_host_to_net_u32 (rv);
1501   }));
1502   /* *INDENT-ON* */
1503
1504   /*
1505    * vnet_dns_resolve_name leaves the cache locked when it returns
1506    * a cached result, so unlock it here.
1507    */
1508   dns_cache_unlock (dm);
1509 }
1510
1511 #define vl_msg_name_crc_list
1512 #include <vpp/api/vpe_all_api_h.h>
1513 #undef vl_msg_name_crc_list
1514
1515 static void
1516 setup_message_id_table (api_main_t * am)
1517 {
1518 #define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
1519   foreach_vl_msg_name_crc_dns;
1520 #undef _
1521 }
1522
1523 #define foreach_dns_api_msg                             \
1524 _(DNS_ENABLE_DISABLE, dns_enable_disable)               \
1525 _(DNS_NAME_SERVER_ADD_DEL, dns_name_server_add_del)     \
1526 _(DNS_RESOLVE_NAME, dns_resolve_name)                   \
1527 _(DNS_RESOLVE_IP, dns_resolve_ip)
1528
1529 static clib_error_t *
1530 dns_api_hookup (vlib_main_t * vm)
1531 {
1532 #define _(N,n)                                                  \
1533     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1534                            vl_api_##n##_t_handler,              \
1535                            vl_noop_handler,                     \
1536                            vl_api_##n##_t_endian,               \
1537                            vl_api_##n##_t_print,                \
1538                            sizeof(vl_api_##n##_t), 1);
1539   foreach_dns_api_msg;
1540 #undef _
1541
1542   setup_message_id_table (&api_main);
1543   return 0;
1544 }
1545
1546 VLIB_API_INIT_FUNCTION (dns_api_hookup);
1547
1548
1549 static clib_error_t *
1550 dns_config_fn (vlib_main_t * vm, unformat_input_t * input)
1551 {
1552   dns_main_t *dm = &dns_main;
1553
1554   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1555     {
1556       if (unformat (input, "max-cache-size %u", &dm->name_cache_size))
1557         ;
1558       else if (unformat (input, "max-ttl %u", &dm->max_ttl_in_seconds))
1559         ;
1560       else
1561         return clib_error_return (0, "unknown input `%U'",
1562                                   format_unformat_error, input);
1563     }
1564   return 0;
1565 }
1566
1567 VLIB_CONFIG_FUNCTION (dns_config_fn, "dns");
1568
1569 static clib_error_t *
1570 dns_init (vlib_main_t * vm)
1571 {
1572   dns_main_t *dm = &dns_main;
1573
1574   dm->vlib_main = vm;
1575   dm->vnet_main = vnet_get_main ();
1576   dm->name_cache_size = 65535;
1577   dm->max_ttl_in_seconds = 86400;
1578   dm->random_seed = 0xDEADDABE;
1579
1580   udp_register_dst_port (vm, UDP_DST_PORT_dns_reply, dns46_reply_node.index,
1581                          1 /* is_ip4 */ );
1582
1583   udp_register_dst_port (vm, UDP_DST_PORT_dns_reply6, dns46_reply_node.index,
1584                          0 /* is_ip4 */ );
1585
1586   udp_register_dst_port (vm, UDP_DST_PORT_dns, dns4_request_node.index,
1587                          1 /* is_ip4 */ );
1588
1589   udp_register_dst_port (vm, UDP_DST_PORT_dns6, dns6_request_node.index,
1590                          0 /* is_ip4 */ );
1591   return 0;
1592 }
1593
1594 VLIB_INIT_FUNCTION (dns_init);
1595
1596 uword
1597 unformat_dns_reply (unformat_input_t * input, va_list * args)
1598 {
1599   u8 **result = va_arg (*args, u8 **);
1600   u8 **namep = va_arg (*args, u8 **);
1601   ip4_address_t a4;
1602   ip6_address_t a6;
1603   int a4_set = 0;
1604   int a6_set = 0;
1605   u8 *name;
1606   int name_set = 0;
1607   u8 *ce;
1608   u32 qp_offset;
1609   dns_header_t *h;
1610   dns_query_t *qp;
1611   dns_rr_t *rr;
1612   u8 *rru8;
1613
1614   if (unformat (input, "%v", &name))
1615     name_set = 1;
1616
1617   if (unformat (input, "%U", unformat_ip4_address, &a4))
1618     {
1619       a4_set = 1;
1620       if (unformat (input, "%U", unformat_ip6_address, &a6))
1621         a6_set = 1;
1622     }
1623
1624   if (unformat (input, "%U", unformat_ip6_address, &a6))
1625     {
1626       a6_set = 1;
1627       if (unformat (input, "%U", unformat_ip4_address, &a6))
1628         a4_set = 1;
1629     }
1630
1631   /* Must have a name */
1632   if (!name_set)
1633     return 0;
1634
1635   /* Must have at least one address */
1636   if (!(a4_set + a6_set))
1637     return 0;
1638
1639   /* Build a fake DNS cache entry string, one hemorrhoid at a time */
1640   ce = name_to_labels (name);
1641   qp_offset = vec_len (ce);
1642
1643   /* Add space for the query header */
1644   vec_validate (ce, qp_offset + sizeof (dns_query_t) - 1);
1645   qp = (dns_query_t *) (ce + qp_offset);
1646
1647   qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
1648   qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1649
1650   /* Punch in space for the dns_header_t */
1651   vec_insert (ce, sizeof (dns_header_t), 0);
1652
1653   h = (dns_header_t *) ce;
1654
1655   /* Fake Transaction ID */
1656   h->id = 0xFFFF;
1657
1658   h->flags = clib_host_to_net_u16 (DNS_RD | DNS_RA);
1659   h->qdcount = clib_host_to_net_u16 (1);
1660   h->anscount = clib_host_to_net_u16 (a4_set + a6_set);
1661   h->nscount = 0;
1662   h->arcount = 0;
1663
1664   /* Now append one or two A/AAAA RR's... */
1665   if (a4_set)
1666     {
1667       /* Pointer to the name (DGMS) */
1668       vec_add1 (ce, 0xC0);
1669       vec_add1 (ce, 0x0C);
1670       vec_add2 (ce, rru8, sizeof (*rr) + 4);
1671       rr = (void *) rru8;
1672       rr->type = clib_host_to_net_u16 (DNS_TYPE_A);
1673       rr->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1674       rr->ttl = clib_host_to_net_u32 (86400);
1675       rr->rdlength = clib_host_to_net_u16 (4);
1676       memcpy (rr->rdata, &a4, sizeof (a4));
1677     }
1678   if (a6_set)
1679     {
1680       /* Pointer to the name (DGMS) */
1681       vec_add1 (ce, 0xC0);
1682       vec_add1 (ce, 0x0C);
1683       vec_add2 (ce, rru8, sizeof (*rr) + 16);
1684       rr = (void *) rru8;
1685       rr->type = clib_host_to_net_u16 (DNS_TYPE_AAAA);
1686       rr->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1687       rr->ttl = clib_host_to_net_u32 (86400);
1688       rr->rdlength = clib_host_to_net_u16 (16);
1689       memcpy (rr->rdata, &a6, sizeof (a6));
1690     }
1691   *result = ce;
1692   if (namep)
1693     *namep = name;
1694   else
1695     vec_free (name);
1696
1697   return 1;
1698 }
1699
1700 u8 *
1701 format_dns_query (u8 * s, va_list * args)
1702 {
1703   u8 **curpos = va_arg (*args, u8 **);
1704   int verbose = va_arg (*args, int);
1705   u8 *pos;
1706   dns_query_t *qp;
1707   int len, i;
1708   if (verbose > 1)
1709     s = format (s, "    Name: ");
1710
1711   /* Unwind execrated counted-label sheit */
1712   pos = *curpos;
1713   len = *pos++;
1714
1715   while (len)
1716     {
1717       for (i = 0; i < len; i++)
1718         vec_add1 (s, *pos++);
1719
1720       len = *pos++;
1721       if (len)
1722         vec_add1 (s, '.');
1723       else
1724         {
1725           vec_add1 (s, ':');
1726           vec_add1 (s, ' ');
1727         }
1728     }
1729
1730   qp = (dns_query_t *) pos;
1731   if (verbose > 1)
1732     {
1733       switch (clib_net_to_host_u16 (qp->type))
1734         {
1735         case DNS_TYPE_A:
1736           s = format (s, "type A\n");
1737           break;
1738         case DNS_TYPE_AAAA:
1739           s = format (s, "type AAAA\n");
1740           break;
1741         case DNS_TYPE_ALL:
1742           s = format (s, "type ALL\n");
1743           break;
1744
1745         default:
1746           s = format (s, "type %d\n", clib_net_to_host_u16 (qp->type));
1747           break;
1748         }
1749     }
1750
1751   pos += sizeof (*qp);
1752
1753   *curpos = pos;
1754   return s;
1755 }
1756
1757 /**
1758  * format dns reply data
1759  * verbose > 1, dump everything
1760  * verbose == 1, dump all A and AAAA records
1761  * verbose == 0, dump one A record, and one AAAA record
1762  */
1763
1764 u8 *
1765 format_dns_reply_data (u8 * s, va_list * args)
1766 {
1767   u8 *reply = va_arg (*args, u8 *);
1768   u8 **curpos = va_arg (*args, u8 **);
1769   int verbose = va_arg (*args, int);
1770   int *print_ip4 = va_arg (*args, int *);
1771   int *print_ip6 = va_arg (*args, int *);
1772   int len;
1773   u8 *pos, *pos2;
1774   dns_rr_t *rr;
1775   int i;
1776   int pointer_chase = 0;
1777   u16 *tp;
1778   u16 rrtype_host_byte_order;
1779
1780   pos = pos2 = *curpos;
1781
1782   if (verbose > 1)
1783     s = format (s, "    ");
1784
1785   /* chase pointer? almost always yes here... */
1786   if ((pos2[0] & 0xc0) == 0xc0)
1787     {
1788       pos = pos2 + 2;
1789       pos2 = reply + ((pos2[0] & 0x3f) << 8) + pos2[1];
1790       pointer_chase = 1;
1791     }
1792
1793   len = *pos2++;
1794
1795   while (len)
1796     {
1797       for (i = 0; i < len; i++)
1798         {
1799           if (verbose > 1)
1800             vec_add1 (s, *pos2);
1801           pos2++;
1802         }
1803       if ((pos2[0] & 0xc0) == 0xc0)
1804         {
1805           /*
1806            * If we've already done one pointer chase,
1807            * do not move the pos pointer.
1808            */
1809           if (pointer_chase == 0)
1810             pos = pos2 + 2;
1811           pos2 = reply + ((pos2[0] & 0x3f) << 8) + pos2[1];
1812           len = *pos2++;
1813           pointer_chase = 1;
1814         }
1815       else
1816         len = *pos2++;
1817       if (len)
1818         {
1819           if (verbose > 1)
1820             vec_add1 (s, '.');
1821         }
1822       else
1823         {
1824           if (verbose > 1)
1825             vec_add1 (s, ' ');
1826         }
1827     }
1828
1829   if (pointer_chase == 0)
1830     pos = pos2;
1831
1832   rr = (dns_rr_t *) pos;
1833   rrtype_host_byte_order = clib_net_to_host_u16 (rr->type);
1834
1835   switch (rrtype_host_byte_order)
1836     {
1837     case DNS_TYPE_A:
1838       if (verbose > 1)
1839         {
1840           s = format (s, "A: ttl %d %U\n", clib_net_to_host_u32 (rr->ttl),
1841                       format_ip4_address, rr->rdata);
1842         }
1843       else
1844         {
1845           if (*print_ip4)
1846             s = format (s, "%U [%u] ", format_ip4_address, rr->rdata,
1847                         clib_net_to_host_u32 (rr->ttl));
1848           if (verbose == 0)
1849             *print_ip4 = 0;
1850
1851         }
1852       pos += sizeof (*rr) + 4;
1853       break;
1854
1855     case DNS_TYPE_AAAA:
1856       if (verbose > 1)
1857         {
1858           s = format (s, "AAAA: ttl %d %U\n", clib_net_to_host_u32 (rr->ttl),
1859                       format_ip6_address, rr->rdata);
1860         }
1861       else
1862         {
1863           if (*print_ip6)
1864             s = format (s, "%U [%u] ", format_ip6_address, rr->rdata,
1865                         clib_net_to_host_u32 (rr->ttl));
1866           if (verbose == 0)
1867             *print_ip6 = 0;
1868         }
1869       pos += sizeof (*rr) + 16;
1870       break;
1871
1872     case DNS_TYPE_TEXT:
1873       if (verbose > 1)
1874         {
1875           s = format (s, "TEXT: ");
1876           for (i = 0; i < clib_net_to_host_u16 (rr->rdlength); i++)
1877             vec_add1 (s, rr->rdata[i]);
1878           vec_add1 (s, '\n');
1879         }
1880       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1881       break;
1882
1883     case DNS_TYPE_HINFO:
1884       {
1885         /* Two counted strings. DGMS */
1886         u8 *len;
1887         u8 *curpos;
1888         int i;
1889         if (verbose > 1)
1890           {
1891             s = format (s, "HINFO: ");
1892             len = rr->rdata;
1893             curpos = len + 1;
1894             for (i = 0; i < *len; i++)
1895               vec_add1 (s, *curpos++);
1896
1897             vec_add1 (s, ' ');
1898             len = curpos++;
1899             for (i = 0; i < *len; i++)
1900               vec_add1 (s, *curpos++);
1901
1902             vec_add1 (s, '\n');
1903           }
1904       }
1905       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1906       break;
1907
1908     case DNS_TYPE_NAMESERVER:
1909       if (verbose > 1)
1910         {
1911           s = format (s, "Nameserver: ");
1912           pos2 = rr->rdata;
1913
1914           /* chase pointer? */
1915           if ((pos2[0] & 0xc0) == 0xc0)
1916             {
1917               pos = pos2 + 2;
1918               pos2 = reply + ((pos2[0] & 0x3f) << 8) + pos2[1];
1919             }
1920
1921           len = *pos2++;
1922
1923           while (len)
1924             {
1925               for (i = 0; i < len; i++)
1926                 vec_add1 (s, *pos2++);
1927
1928               /* chase pointer, typically to offset 12... */
1929               if (pos2[0] == 0xC0)
1930                 pos2 = reply + pos2[1];
1931
1932               len = *pos2++;
1933               if (len)
1934                 vec_add1 (s, '.');
1935               else
1936                 vec_add1 (s, '\n');
1937             }
1938         }
1939       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1940       break;
1941
1942     case DNS_TYPE_MAIL_EXCHANGE:
1943       if (verbose > 1)
1944         {
1945           tp = (u16 *) rr->rdata;
1946
1947           s = format (s, "Mail Exchange: Preference %d ", (u32)
1948                       clib_net_to_host_u16 (*tp));
1949
1950           pos2 = rr->rdata + 2;
1951
1952           /* chase pointer? */
1953           if (pos2[0] == 0xc0)
1954             pos2 = reply + pos2[1];
1955
1956           len = *pos2++;
1957
1958           while (len)
1959             {
1960               for (i = 0; i < len; i++)
1961                 vec_add1 (s, *pos2++);
1962
1963               /* chase pointer */
1964               if (pos2[0] == 0xC0)
1965                 pos2 = reply + pos2[1];
1966
1967               len = *pos2++;
1968               if (len)
1969                 vec_add1 (s, '.');
1970               else
1971                 vec_add1 (s, '\n');
1972             }
1973         }
1974
1975       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1976       break;
1977
1978     case DNS_TYPE_PTR:
1979     case DNS_TYPE_CNAME:
1980       if (verbose > 1)
1981         {
1982           tp = (u16 *) rr->rdata;
1983
1984           if (rrtype_host_byte_order == DNS_TYPE_CNAME)
1985             s = format (s, "CNAME: ");
1986           else
1987             s = format (s, "PTR: ");
1988
1989           pos2 = rr->rdata;
1990
1991           /* chase pointer? */
1992           if (pos2[0] == 0xc0)
1993             pos2 = reply + pos2[1];
1994
1995           len = *pos2++;
1996
1997           while (len)
1998             {
1999               for (i = 0; i < len; i++)
2000                 vec_add1 (s, *pos2++);
2001
2002               /* chase pointer */
2003               if (pos2[0] == 0xC0)
2004                 pos2 = reply + pos2[1];
2005
2006               len = *pos2++;
2007               if (len)
2008                 vec_add1 (s, '.');
2009               else
2010                 vec_add1 (s, '\n');
2011             }
2012         }
2013       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
2014       break;
2015
2016     default:
2017       if (verbose > 1)
2018         s = format (s, "type %d: len %d\n",
2019                     (int) clib_net_to_host_u16 (rr->type),
2020                     sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength));
2021       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
2022       break;
2023     }
2024
2025   *curpos = pos;
2026
2027   return s;
2028 }
2029
2030 u8 *
2031 format_dns_reply (u8 * s, va_list * args)
2032 {
2033   u8 *reply_as_u8 = va_arg (*args, u8 *);
2034   int verbose = va_arg (*args, int);
2035   dns_header_t *h;
2036   u16 id, flags;
2037   u8 *curpos;
2038   int i;
2039   int print_ip4 = 1;
2040   int print_ip6 = 1;
2041
2042   h = (dns_header_t *) reply_as_u8;
2043   id = clib_net_to_host_u16 (h->id);
2044   flags = clib_net_to_host_u16 (h->flags);
2045
2046   if (verbose > 1)
2047     {
2048       s = format (s, "DNS %s: id %d\n", (flags & DNS_QR) ? "reply" : "query",
2049                   id);
2050       s = format (s, "  %s %s %s %s\n",
2051                   (flags & DNS_RA) ? "recur" : "no-recur",
2052                   (flags & DNS_RD) ? "recur-des" : "no-recur-des",
2053                   (flags & DNS_TC) ? "trunc" : "no-trunc",
2054                   (flags & DNS_AA) ? "auth" : "non-auth");
2055       s = format (s, "  %d queries, %d answers, %d name-servers,"
2056                   " %d add'l recs\n",
2057                   clib_net_to_host_u16 (h->qdcount),
2058                   clib_net_to_host_u16 (h->anscount),
2059                   clib_net_to_host_u16 (h->nscount),
2060                   clib_net_to_host_u16 (h->arcount));
2061     }
2062
2063   curpos = (u8 *) (h + 1);
2064
2065   if (h->qdcount)
2066     {
2067       if (verbose > 1)
2068         s = format (s, "  Queries:\n");
2069       for (i = 0; i < clib_net_to_host_u16 (h->qdcount); i++)
2070         {
2071           /* The query is variable-length, so curpos is a value-result parm */
2072           s = format (s, "%U", format_dns_query, &curpos, verbose);
2073         }
2074     }
2075   if (h->anscount)
2076     {
2077       if (verbose > 1)
2078         s = format (s, "  Replies:\n");
2079
2080       for (i = 0; i < clib_net_to_host_u16 (h->anscount); i++)
2081         {
2082           /* curpos is a value-result parm */
2083           s = format (s, "%U", format_dns_reply_data, reply_as_u8, &curpos,
2084                       verbose, &print_ip4, &print_ip6);
2085         }
2086     }
2087   return s;
2088 }
2089
2090 u8 *
2091 format_dns_cache (u8 * s, va_list * args)
2092 {
2093   dns_main_t *dm = va_arg (*args, dns_main_t *);
2094   f64 now = va_arg (*args, f64);
2095   int verbose = va_arg (*args, int);
2096   u8 *name = va_arg (*args, u8 *);
2097   dns_cache_entry_t *ep;
2098   char *ss;
2099   uword *p;
2100
2101   if (dm->is_enabled == 0)
2102     {
2103       s = format (s, "The DNS cache is disabled...");
2104       return s;
2105     }
2106
2107   if (pool_elts (dm->entries) == 0)
2108     {
2109       s = format (s, "The DNS cache is empty...");
2110       return s;
2111     }
2112
2113   dns_cache_lock (dm);
2114
2115   if (name)
2116     {
2117       p = hash_get_mem (dm->cache_entry_by_name, name);
2118       if (!p)
2119         {
2120           s = format (s, "%s is not in the cache...", name);
2121           dns_cache_unlock (dm);
2122           return (s);
2123         }
2124
2125       ep = pool_elt_at_index (dm->entries, p[0]);
2126       /* Magic to spit out a C-initializer to research hemorrhoids... */
2127       if (verbose == 3)
2128         {
2129           int i, j;
2130           s = format (s, "static u8 dns_reply_data_initializer[] =\n");
2131           s = format (s, "{\n");
2132           j = 0;
2133           for (i = 0; i < vec_len (ep->dns_response); i++)
2134             {
2135               if (j++ == 8)
2136                 {
2137                   j = 0;
2138                   vec_add1 (s, '\n');
2139                 }
2140               s = format (s, "0x%02x, ", ep->dns_response[i]);
2141             }
2142           s = format (s, "};\n");
2143         }
2144       else
2145         {
2146           if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
2147             {
2148               ASSERT (ep->dns_response);
2149               if (ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC)
2150                 ss = "[S] ";
2151               else
2152                 ss = "    ";
2153
2154               if (verbose < 2 && ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
2155                 s = format (s, "%s%s -> %s", ss, ep->name, ep->cname);
2156               else
2157                 s = format (s, "%s%s -> %U", ss, ep->name,
2158                             format_dns_reply, ep->dns_response, verbose);
2159               if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
2160                 {
2161                   f64 time_left = ep->expiration_time - now;
2162                   if (time_left > 0.0)
2163                     s = format (s, "  TTL left %.1f", time_left);
2164                   else
2165                     s = format (s, "  EXPIRED");
2166                 }
2167             }
2168           else
2169             {
2170               ASSERT (ep->dns_request);
2171               s = format (s, "[P] %U", format_dns_reply, ep->dns_request,
2172                           verbose);
2173             }
2174           vec_add1 (s, '\n');
2175         }
2176       return s;
2177     }
2178
2179   /* *INDENT-OFF* */
2180   pool_foreach (ep, dm->entries,
2181   ({
2182     if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
2183       {
2184         ASSERT (ep->dns_response);
2185         if (ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC)
2186           ss = "[S] ";
2187         else
2188           ss = "    ";
2189
2190         if (verbose < 2 && ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
2191           s = format (s, "%s%s -> %s", ss, ep->name, ep->cname);
2192         else
2193           s = format (s, "%s%s -> %U", ss, ep->name,
2194                       format_dns_reply,
2195                       ep->dns_response,
2196                       verbose);
2197         if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
2198           {
2199             f64 time_left = ep->expiration_time - now;
2200             if (time_left > 0.0)
2201               s = format (s, "  TTL left %.1f", time_left);
2202             else
2203               s = format (s, "  EXPIRED");
2204
2205             if (verbose > 2)
2206               s = format (s, "    %d client notifications pending\n",
2207                           vec_len(ep->pending_requests));
2208           }
2209       }
2210     else
2211       {
2212         ASSERT (ep->dns_request);
2213         s = format (s, "[P] %U", format_dns_reply, ep->dns_request,
2214                     verbose);
2215       }
2216     vec_add1 (s, '\n');
2217   }));
2218   /* *INDENT-ON* */
2219
2220   dns_cache_unlock (dm);
2221
2222   return s;
2223 }
2224
2225 static clib_error_t *
2226 show_dns_cache_command_fn (vlib_main_t * vm,
2227                            unformat_input_t * input, vlib_cli_command_t * cmd)
2228 {
2229   dns_main_t *dm = &dns_main;
2230   int verbose = 0;
2231   u8 *name = 0;
2232   f64 now = vlib_time_now (vm);
2233
2234   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2235     {
2236       if (unformat (input, "verbose %d", &verbose))
2237         ;
2238       else if (unformat (input, "verbose"))
2239         verbose = 1;
2240       else if (unformat (input, "name %s", &name))
2241         ;
2242       else
2243         return clib_error_return (0, "unknown input `%U'",
2244                                   format_unformat_error, input);
2245     }
2246
2247   vlib_cli_output (vm, "%U", format_dns_cache, dm, now, verbose, name);
2248
2249   return 0;
2250 }
2251
2252 /* *INDENT-OFF* */
2253 VLIB_CLI_COMMAND (show_dns_cache_command) =
2254 {
2255   .path = "show dns cache",
2256   .short_help = "show dns cache [verbose [nn]]",
2257   .function = show_dns_cache_command_fn,
2258 };
2259 /* *INDENT-ON* */
2260
2261 static clib_error_t *
2262 dns_cache_add_del_command_fn (vlib_main_t * vm,
2263                               unformat_input_t * input,
2264                               vlib_cli_command_t * cmd)
2265 {
2266   dns_main_t *dm = &dns_main;
2267   u8 *dns_reply_data;
2268   u8 *name;
2269   int is_add = -1;
2270   int is_clear = -1;
2271   int rv;
2272   clib_error_t *error;
2273
2274   if (unformat (input, "add"))
2275     is_add = 1;
2276   if (unformat (input, "del"))
2277     is_add = 0;
2278   if (unformat (input, "clear"))
2279     is_clear = 1;
2280
2281   if (is_add == -1 && is_clear == -1)
2282     return clib_error_return (0, "add / del / clear required...");
2283
2284   if (is_clear == 1)
2285     {
2286       rv = dns_cache_clear (dm);
2287       switch (rv)
2288         {
2289         case 0:
2290           return 0;
2291
2292         case VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED:
2293           error = clib_error_return (0, "Name resolution not enabled");
2294           return error;
2295         }
2296     }
2297
2298   /* Delete (by name)? */
2299   if (is_add == 0)
2300     {
2301       if (unformat (input, "%v", &name))
2302         {
2303           rv = dns_delete_by_name (dm, name);
2304           switch (rv)
2305             {
2306             case VNET_API_ERROR_NO_SUCH_ENTRY:
2307               error = clib_error_return (0, "%v not in the cache...", name);
2308               vec_free (name);
2309               return error;
2310
2311             case VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED:
2312               error = clib_error_return (0, "Name resolution not enabled");
2313               vec_free (name);
2314               return error;
2315
2316             case 0:
2317               vec_free (name);
2318               return 0;
2319
2320             default:
2321               error = clib_error_return (0, "dns_delete_by_name returned %d",
2322                                          rv);
2323               vec_free (name);
2324               return error;
2325             }
2326         }
2327       return clib_error_return (0, "unknown input `%U'",
2328                                 format_unformat_error, input);
2329     }
2330
2331   /* Note: dns_add_static_entry consumes the name vector if OK... */
2332   if (unformat (input, "%U", unformat_dns_reply, &dns_reply_data, &name))
2333     {
2334       rv = dns_add_static_entry (dm, name, dns_reply_data);
2335       switch (rv)
2336         {
2337         case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
2338           vec_free (name);
2339           vec_free (dns_reply_data);
2340           return clib_error_return (0, "%v already in the cache...", name);
2341         case 0:
2342           return 0;
2343
2344         default:
2345           return clib_error_return (0, "dns_add_static_entry returned %d",
2346                                     rv);
2347         }
2348     }
2349
2350   return 0;
2351 }
2352
2353 /* *INDENT-OFF* */
2354 VLIB_CLI_COMMAND (dns_cache_add_del_command) =
2355 {
2356   .path = "dns cache",
2357   .short_help = "dns cache [add|del|clear] <name> [ip4][ip6]",
2358   .function = dns_cache_add_del_command_fn,
2359 };
2360 /* *INDENT-ON* */
2361
2362 #define DNS_FORMAT_TEST 1
2363
2364 #if DNS_FORMAT_TEST > 0
2365 #if 0
2366 /* yahoo.com */
2367 static u8 dns_reply_data_initializer[] =
2368   { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x5,
2369   0x79, 0x61, 0x68, 0x6f, 0x6f, 0x3, 0x63, 0x6f, 0x6d,
2370   0x0,                          /* null lbl */
2371   0x0, 0xff,                    /* type ALL */
2372   0x0, 0x1,                     /* class IN */
2373   0xc0, 0xc,                    /* pointer to yahoo.com name */
2374   0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x24, 0x23,
2375   0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x72, 0x65, 0x64, 0x69, 0x72,
2376   0x65, 0x63, 0x74, 0x3d, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x6d, 0x61, 0x69,
2377   0x6c, 0x2e, 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0xc0,
2378   0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73,
2379   0x35, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0,
2380   0x6, 0x3, 0x6e, 0x73, 0x34, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0,
2381   0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x31, 0xc0, 0xc, 0xc0, 0xc,
2382   0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x32,
2383   0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6,
2384   0x3, 0x6e, 0x73, 0x33, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0,
2385   0x6, 0x5c, 0x0, 0x19, 0x0, 0x1, 0x4, 0x6d, 0x74, 0x61, 0x36, 0x3, 0x61,
2386   0x6d, 0x30, 0x8, 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x64, 0x6e, 0x73, 0x3,
2387   0x6e,
2388   0x65, 0x74, 0x0, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0,
2389   0x9, 0x0, 0x1, 0x4, 0x6d, 0x74, 0x61, 0x37, 0xc0, 0xb8, 0xc0, 0xc, 0x0,
2390   0xf, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x9, 0x0, 0x1, 0x4, 0x6d, 0x74,
2391   0x61, 0x35, 0xc0, 0xb8, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
2392   0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0x44, 0x2, 0x4, 0x0, 0x0,
2393   0x0,
2394   0x0, 0x0, 0x0, 0x0, 0xa7, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
2395   0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0xc, 0xa, 0x6, 0x0, 0x0, 0x0,
2396   0x0, 0x0, 0x2, 0x40, 0x8, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
2397   0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0x58, 0xc, 0x2, 0x0, 0x0,
2398   0x0,
2399   0x0, 0x0, 0x0, 0x0, 0xa9, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x6,
2400   0x5c, 0x0, 0x4, 0x62, 0x8a, 0xfd, 0x6d, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1,
2401   0x0,
2402   0x0, 0x6, 0x5c, 0x0, 0x4, 0xce, 0xbe, 0x24, 0x2d, 0xc0, 0xc, 0x0, 0x1,
2403   0x0,
2404   0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x4, 0x62, 0x8b, 0xb4, 0x95, 0xc0, 0xc,
2405   0x0,
2406   0x6, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x2d, 0xc0, 0x7b, 0xa, 0x68,
2407   0x6f,
2408   0x73, 0x74, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x9, 0x79, 0x61, 0x68,
2409   0x6f, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0xc0, 0x12, 0x78, 0x3a, 0x85, 0x44,
2410   0x0, 0x0, 0xe, 0x10, 0x0, 0x0, 0x1, 0x2c, 0x0, 0x1b, 0xaf, 0x80, 0x0, 0x0,
2411   0x2, 0x58
2412 };
2413
2414 /* www.cisco.com, has no addresses in reply */
2415 static u8 dns_reply_data_initializer[] = {
2416   0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
2417   0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x05,
2418   0x63, 0x69, 0x73, 0x63, 0x6f, 0x03, 0x63, 0x6f, 0x6d,
2419
2420   0x00, 0x00, 0xff, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05,
2421   0x00, 0x01, 0x00, 0x00, 0x0b, 0xd3, 0x00, 0x1a, 0x03,
2422   0x77, 0x77, 0x77, 0x05, 0x63, 0x69, 0x73, 0x63, 0x6f,
2423   0x03, 0x63, 0x6f, 0x6d, 0x06, 0x61, 0x6b, 0x61, 0x64,
2424   0x6e, 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00,
2425 };
2426
2427 /* bind8 (linux widget, w/ nasty double pointer chasees */
2428 static u8 dns_reply_data_initializer[] = {
2429   /* 0 */
2430   0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x08,
2431   /* 8 */
2432   0x00, 0x06, 0x00, 0x06, 0x0a, 0x6f, 0x72, 0x69,
2433   /* 16 */
2434   0x67, 0x69, 0x6e, 0x2d, 0x77, 0x77, 0x77, 0x05,
2435   /* 24 */
2436   0x63, 0x69, 0x73, 0x63, 0x6f, 0x03, 0x63, 0x6f,
2437   /* 32 */
2438   0x6d, 0x00, 0x00, 0xff, 0x00, 0x01, 0x0a, 0x6f,
2439   /* 40 */
2440   0x72, 0x69, 0x67, 0x69, 0x6e, 0x2d, 0x77, 0x77,
2441   /* 48 */
2442   0x77, 0x05, 0x43, 0x49, 0x53, 0x43, 0x4f, 0xc0,
2443
2444   /* 56 */
2445   0x1d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05,
2446
2447   /* 64 */
2448   0x9a, 0x00, 0x18, 0x15, 0x72, 0x63, 0x64,
2449   0x6e, 0x39, 0x2d, 0x31, 0x34, 0x70, 0x2d, 0x64, 0x63,
2450   0x7a, 0x30, 0x35, 0x6e, 0x2d, 0x67, 0x73, 0x73, 0x31,
2451   0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02, 0x00, 0x01, 0x00,
2452   0x00, 0x05, 0x9a, 0x00, 0x1a, 0x17, 0x61, 0x6c, 0x6c,
2453   0x6e, 0x30, 0x31, 0x2d, 0x61, 0x67, 0x30, 0x39, 0x2d,
2454   0x64, 0x63, 0x7a, 0x30, 0x33, 0x6e, 0x2d, 0x67, 0x73,
2455   0x73, 0x31, 0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02, 0x00,
2456   0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x10, 0x0d, 0x72,
2457   0x74, 0x70, 0x35, 0x2d, 0x64, 0x6d, 0x7a, 0x2d, 0x67,
2458   0x73, 0x73, 0x31, 0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02,
2459   0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x18, 0x15,
2460   0x6d, 0x74, 0x76, 0x35, 0x2d, 0x61, 0x70, 0x31, 0x30,
2461   0x2d, 0x64, 0x63, 0x7a, 0x30, 0x36, 0x6e, 0x2d, 0x67,
2462   0x73, 0x73, 0x31, 0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02,
2463   0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x1b, 0x18,
2464   0x73, 0x6e, 0x67, 0x64, 0x63, 0x30, 0x31, 0x2d, 0x61,
2465   0x62, 0x30, 0x37, 0x2d, 0x64, 0x63, 0x7a, 0x30, 0x31,
2466   0x6e, 0x2d, 0x67, 0x73, 0x73, 0x31, 0xc0, 0x17, 0xc0,
2467   0x26, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05, 0x9a,
2468   0x00, 0x1a, 0x17, 0x61, 0x65, 0x72, 0x30, 0x31, 0x2d,
2469   0x72, 0x34, 0x63, 0x32, 0x35, 0x2d, 0x64, 0x63, 0x7a,
2470   0x30, 0x31, 0x6e, 0x2d, 0x67, 0x73, 0x73, 0x31, 0xc0,
2471   0x17, 0xc0, 0x26, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
2472   0x00, 0x81, 0x00, 0x04, 0x48, 0xa3, 0x04, 0xa1, 0xc0,
2473   0x26, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x82,
2474   0x00, 0x10, 0x20, 0x01, 0x04, 0x20, 0x12, 0x01, 0x00,
2475   0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a,
2476   0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05,
2477   0x9a, 0x00, 0x02, 0xc0, 0xf4, 0xc0, 0x0c, 0x00, 0x02,
2478   0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x02, 0xc0,
2479   0xcd, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2480   0x05, 0x9a, 0x00, 0x02, 0xc0, 0x8d, 0xc0, 0x0c, 0x00,
2481   0x02, 0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x02,
2482   0xc0, 0x43, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00,
2483   0x00, 0x05, 0x9a, 0x00, 0x02, 0xc0, 0xa9, 0xc0, 0x0c,
2484   0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00,
2485   0x02, 0xc0, 0x67, 0xc0, 0x8d, 0x00, 0x01, 0x00, 0x01,
2486   0x00, 0x00, 0x07, 0x08, 0x00, 0x04, 0x40, 0x66, 0xf6,
2487   0x05, 0xc0, 0xa9, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
2488   0x07, 0x08, 0x00, 0x04, 0xad, 0x24, 0xe0, 0x64, 0xc0,
2489   0x43, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x07, 0x08,
2490   0x00, 0x04, 0x48, 0xa3, 0x04, 0x1c, 0xc0, 0xf4, 0x00,
2491   0x01, 0x00, 0x01, 0x00, 0x00, 0x07, 0x08, 0x00, 0x04,
2492   0xad, 0x26, 0xd4, 0x6c, 0xc0, 0x67, 0x00, 0x01, 0x00,
2493   0x01, 0x00, 0x00, 0x07, 0x08, 0x00, 0x04, 0xad, 0x25,
2494   0x90, 0x64, 0xc0, 0xcd, 0x00, 0x01, 0x00, 0x01, 0x00,
2495   0x00, 0x07, 0x08, 0x00, 0x04, 0xad, 0x27, 0x70, 0x44,
2496 };
2497
2498 /* google.com */
2499 static u8 dns_reply_data_initializer[] =
2500   { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x6,
2501   0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x3, 0x63, 0x6f, 0x6d, 0x0, 0x0, 0xff,
2502   0x0, 0x1, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x2b, 0x0, 0x4,
2503   0xac, 0xd9, 0x3, 0x2e, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x1,
2504   0x2b,
2505   0x0, 0x10, 0x26, 0x7, 0xf8, 0xb0, 0x40, 0x4, 0x8, 0xf, 0x0, 0x0, 0x0, 0x0,
2506   0x0, 0x0, 0x20, 0xe, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x51, 0x7f,
2507   0x0, 0x6, 0x3, 0x6e, 0x73, 0x31, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x6, 0x0, 0x1,
2508   0x0, 0x0, 0x0, 0x3b, 0x0, 0x22, 0xc0, 0x54, 0x9, 0x64, 0x6e, 0x73, 0x2d,
2509   0x61, 0x64, 0x6d, 0x69, 0x6e, 0xc0, 0xc, 0xa, 0x3d, 0xc7, 0x30, 0x0, 0x0,
2510   0x3, 0x84, 0x0, 0x0, 0x3, 0x84, 0x0, 0x0, 0x7, 0x8, 0x0, 0x0, 0x0, 0x3c,
2511   0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x11, 0x0, 0x1e,
2512   0x4, 0x61, 0x6c, 0x74, 0x32, 0x5, 0x61, 0x73, 0x70, 0x6d, 0x78, 0x1, 0x6c,
2513   0xc0, 0xc, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x4,
2514   0x0, 0xa, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0xe, 0xf,
2515   0x0, 0x24, 0x23, 0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x69, 0x6e,
2516   0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x67,
2517   0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x7e, 0x61,
2518   0x6c, 0x6c, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x51, 0x7f, 0x0, 0x6,
2519   0x3, 0x6e, 0x73, 0x32, 0xc0, 0xc, 0xc0, 0xc, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1,
2520   0x51, 0x7f, 0x0, 0xf, 0x0, 0x5, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, 0x6b,
2521   0x69, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0,
2522   0x1, 0x51, 0x7f, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x34, 0xc0, 0xc, 0xc0, 0xc,
2523   0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x9, 0x0, 0x28, 0x4, 0x61,
2524   0x6c, 0x74, 0x33, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1,
2525   0x51, 0x7f, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x33, 0xc0, 0xc, 0xc0, 0xc, 0x0,
2526   0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x9, 0x0, 0x32, 0x4, 0x61, 0x6c,
2527   0x74, 0x34, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2,
2528   0x57,
2529   0x0, 0x9, 0x0, 0x14, 0x4, 0x61, 0x6c, 0x74, 0x31, 0xc0, 0x9b
2530 };
2531
2532 #else
2533 /* www.weatherlink.com */
2534 static u8 dns_reply_data_initializer[] = {
2535   0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
2536   0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x0b,
2537   0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x6c, 0x69,
2538   0x6e, 0x6b, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0xff,
2539   0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00,
2540   0x00, 0x0c, 0x9e, 0x00, 0x1f, 0x0e, 0x64, 0x33, 0x6b,
2541   0x72, 0x30, 0x67, 0x75, 0x62, 0x61, 0x31, 0x64, 0x76,
2542   0x77, 0x66, 0x0a, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x66,
2543   0x72, 0x6f, 0x6e, 0x74, 0x03, 0x6e, 0x65, 0x74, 0x00,
2544 };
2545
2546 #endif
2547
2548 static clib_error_t *
2549 test_dns_fmt_command_fn (vlib_main_t * vm,
2550                          unformat_input_t * input, vlib_cli_command_t * cmd)
2551 {
2552   u8 *dns_reply_data = 0;
2553   int verbose = 0;
2554   int rv;
2555   vl_api_dns_resolve_name_reply_t _rm, *rmp = &_rm;
2556
2557   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2558     {
2559       if (unformat (input, "verbose %d", &verbose))
2560         ;
2561       else if (unformat (input, "verbose"))
2562         verbose = 1;
2563       else
2564         return clib_error_return (0, "unknown input `%U'",
2565                                   format_unformat_error, input);
2566     }
2567
2568   vec_validate (dns_reply_data, ARRAY_LEN (dns_reply_data_initializer) - 1);
2569
2570   memcpy (dns_reply_data, dns_reply_data_initializer,
2571           ARRAY_LEN (dns_reply_data_initializer));
2572
2573   vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
2574
2575   memset (rmp, 0, sizeof (*rmp));
2576
2577   rv = vnet_dns_response_to_reply (dns_reply_data, rmp, 0 /* ttl-ptr */ );
2578
2579   switch (rv)
2580     {
2581     case VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES:
2582       vlib_cli_output (vm, "no addresses found...");
2583       break;
2584
2585     default:
2586       vlib_cli_output (vm, "response to reply returned %d", rv);
2587       break;
2588
2589     case 0:
2590       if (rmp->ip4_set)
2591         vlib_cli_output (vm, "ip4 address: %U", format_ip4_address,
2592                          (ip4_address_t *) rmp->ip4_address);
2593       if (rmp->ip6_set)
2594         vlib_cli_output (vm, "ip6 address: %U", format_ip6_address,
2595                          (ip6_address_t *) rmp->ip6_address);
2596       break;
2597     }
2598
2599   vec_free (dns_reply_data);
2600
2601   return 0;
2602 }
2603
2604
2605 /* *INDENT-OFF* */
2606 VLIB_CLI_COMMAND (test_dns_fmt_command) =
2607 {
2608   .path = "test dns format",
2609   .short_help = "test dns format",
2610   .function = test_dns_fmt_command_fn,
2611 };
2612 /* *INDENT-ON* */
2613
2614 static clib_error_t *
2615 test_dns_unfmt_command_fn (vlib_main_t * vm,
2616                            unformat_input_t * input, vlib_cli_command_t * cmd)
2617 {
2618   u8 *dns_reply_data = 0;
2619   int verbose = 0;
2620   int reply_set = 0;
2621
2622   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2623     {
2624       if (unformat (input, "verbose %d", &verbose))
2625         ;
2626       else if (unformat (input, "verbose"))
2627         verbose = 1;
2628       else if (unformat (input, "%U", unformat_dns_reply, &dns_reply_data))
2629         reply_set = 1;
2630       else
2631         return clib_error_return (0, "unknown input `%U'",
2632                                   format_unformat_error, input);
2633     }
2634
2635   if (reply_set == 0)
2636     return clib_error_return (0, "dns data not set...");
2637
2638   vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
2639
2640   vec_free (dns_reply_data);
2641
2642   return 0;
2643 }
2644
2645 /* *INDENT-OFF* */
2646 VLIB_CLI_COMMAND (test_dns_unfmt_command) =
2647 {
2648   .path = "test dns unformat",
2649   .short_help = "test dns unformat <name> [ip4][ip6]",
2650   .function = test_dns_unfmt_command_fn,
2651 };
2652 /* *INDENT-ON* */
2653
2654 static clib_error_t *
2655 test_dns_expire_command_fn (vlib_main_t * vm,
2656                             unformat_input_t * input,
2657                             vlib_cli_command_t * cmd)
2658 {
2659   dns_main_t *dm = &dns_main;
2660   u8 *name;
2661   uword *p;
2662   clib_error_t *e;
2663   dns_cache_entry_t *ep;
2664
2665   if (unformat (input, "%v", &name))
2666     {
2667       vec_add1 (name, 0);
2668       _vec_len (name) -= 1;
2669     }
2670
2671   dns_cache_lock (dm);
2672
2673   p = hash_get_mem (dm->cache_entry_by_name, name);
2674   if (!p)
2675     {
2676       dns_cache_unlock (dm);
2677       e = clib_error_return (0, "%s is not in the cache...", name);
2678       vec_free (name);
2679       return e;
2680     }
2681
2682   ep = pool_elt_at_index (dm->entries, p[0]);
2683
2684   ep->expiration_time = 0;
2685
2686   return 0;
2687 }
2688
2689 /* *INDENT-OFF* */
2690 VLIB_CLI_COMMAND (test_dns_expire_command) =
2691 {
2692   .path = "test dns expire",
2693   .short_help = "test dns expire <name>",
2694   .function = test_dns_expire_command_fn,
2695 };
2696 /* *INDENT-ON* */
2697 #endif
2698
2699 void
2700 vnet_send_dns6_reply (dns_main_t * dm, dns_pending_request_t * pr,
2701                       dns_cache_entry_t * ep, vlib_buffer_t * b0)
2702 {
2703   clib_warning ("Unimplemented...");
2704 }
2705
2706
2707 void
2708 vnet_send_dns4_reply (dns_main_t * dm, dns_pending_request_t * pr,
2709                       dns_cache_entry_t * ep, vlib_buffer_t * b0)
2710 {
2711   vlib_main_t *vm = dm->vlib_main;
2712   u32 bi = 0;
2713   fib_prefix_t prefix;
2714   fib_node_index_t fei;
2715   u32 sw_if_index, fib_index;
2716   ip4_main_t *im4 = &ip4_main;
2717   ip_lookup_main_t *lm4 = &im4->lookup_main;
2718   ip_interface_address_t *ia = 0;
2719   ip4_address_t *src_address;
2720   ip4_header_t *ip;
2721   udp_header_t *udp;
2722   dns_header_t *dh;
2723   vlib_frame_t *f;
2724   u32 *to_next;
2725   u8 *dns_response;
2726   u8 *reply;
2727   vl_api_dns_resolve_name_reply_t _rnr, *rnr = &_rnr;
2728   vl_api_dns_resolve_ip_reply_t _rir, *rir = &_rir;
2729   u32 ttl, tmp;
2730   u32 qp_offset;
2731   dns_query_t *qp;
2732   dns_rr_t *rr;
2733   u8 *rrptr;
2734   int is_fail = 0;
2735
2736   ASSERT (ep && ep->dns_response);
2737
2738   if (pr->request_type == DNS_PEER_PENDING_NAME_TO_IP)
2739     {
2740       /* Quick and dirty way to dig up the A-record address. $$ FIXME */
2741       memset (rnr, 0, sizeof (*rnr));
2742       if (vnet_dns_response_to_reply (ep->dns_response, rnr, &ttl))
2743         {
2744           /* clib_warning ("response_to_reply failed..."); */
2745           is_fail = 1;
2746         }
2747       if (rnr->ip4_set == 0)
2748         {
2749           /* clib_warning ("No A-record..."); */
2750           is_fail = 1;
2751         }
2752     }
2753   else if (pr->request_type == DNS_PEER_PENDING_IP_TO_NAME)
2754     {
2755       memset (rir, 0, sizeof (*rir));
2756       if (vnet_dns_response_to_name (ep->dns_response, rir, &ttl))
2757         {
2758           /* clib_warning ("response_to_name failed..."); */
2759           is_fail = 1;
2760         }
2761     }
2762   else
2763     {
2764       clib_warning ("Unknown request type %d", pr->request_type);
2765       return;
2766     }
2767
2768   /* Initialize a buffer */
2769   if (b0 == 0)
2770     {
2771       if (vlib_buffer_alloc (vm, &bi, 1) != 1)
2772         return;
2773       b0 = vlib_get_buffer (vm, bi);
2774     }
2775
2776   if (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
2777     vlib_buffer_free_one (vm, b0->next_buffer);
2778
2779   /*
2780    * Reset the buffer. We recycle the DNS request packet in the cache
2781    * hit case, and reply immediately from the request node.
2782    *
2783    * In the resolution-required / deferred case, resetting a freshly-allocated
2784    * buffer won't hurt. We hope.
2785    */
2786   b0->flags &= VLIB_BUFFER_FREE_LIST_INDEX_MASK;
2787   b0->flags |= (VNET_BUFFER_F_LOCALLY_ORIGINATED
2788                 | VLIB_BUFFER_TOTAL_LENGTH_VALID);
2789   b0->current_data = 0;
2790   b0->current_length = 0;
2791   b0->total_length_not_including_first_buffer = 0;
2792   vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;   /* "local0" */
2793   vnet_buffer (b0)->sw_if_index[VLIB_TX] = 0;   /* default VRF for now */
2794
2795   /* Find a FIB path to the peer we're trying to answer */
2796   clib_memcpy (&prefix.fp_addr.ip4, pr->dst_address, sizeof (ip4_address_t));
2797   prefix.fp_proto = FIB_PROTOCOL_IP4;
2798   prefix.fp_len = 32;
2799
2800   fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ );
2801   if (fib_index == (u32) ~ 0)
2802     {
2803       clib_warning ("no fib table");
2804       return;
2805     }
2806
2807   fei = fib_table_lookup (fib_index, &prefix);
2808
2809   /* Couldn't find route to destination. Bail out. */
2810   if (fei == FIB_NODE_INDEX_INVALID)
2811     {
2812       clib_warning ("no route to DNS server");
2813       return;
2814     }
2815
2816   sw_if_index = fib_entry_get_resolving_interface (fei);
2817
2818   if (sw_if_index == ~0)
2819     {
2820       clib_warning
2821         ("route to %U exists, fei %d, get_resolving_interface returned"
2822          " ~0", fei, format_ip4_address, &prefix.fp_addr);
2823       return;
2824     }
2825
2826   /* *INDENT-OFF* */
2827   foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnummbered */,
2828   ({
2829     src_address = ip_interface_address_get_address (lm4, ia);
2830     goto found_src_address;
2831   }));
2832   /* *INDENT-ON* */
2833
2834   clib_warning ("FIB BUG");
2835   return;
2836
2837 found_src_address:
2838
2839   ip = vlib_buffer_get_current (b0);
2840   udp = (udp_header_t *) (ip + 1);
2841   dns_response = (u8 *) (udp + 1);
2842   memset (ip, 0, sizeof (*ip) + sizeof (*udp));
2843
2844   /*
2845    * Start with the variadic portion of the exercise.
2846    * Turn the name into a set of DNS "labels". Max length
2847    * per label is 63, enforce that.
2848    */
2849   reply = name_to_labels (pr->name);
2850   vec_free (pr->name);
2851
2852   qp_offset = vec_len (reply);
2853
2854   /* Add space for the query header */
2855   vec_validate (reply, qp_offset + sizeof (dns_query_t) - 1);
2856
2857   qp = (dns_query_t *) (reply + qp_offset);
2858
2859   if (pr->request_type == DNS_PEER_PENDING_NAME_TO_IP)
2860     qp->type = clib_host_to_net_u16 (DNS_TYPE_A);
2861   else
2862     qp->type = clib_host_to_net_u16 (DNS_TYPE_PTR);
2863
2864   qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
2865
2866   /* Punch in space for the dns_header_t */
2867   vec_insert (reply, sizeof (dns_header_t), 0);
2868
2869   dh = (dns_header_t *) reply;
2870
2871   /* Transaction ID = pool index */
2872   dh->id = pr->id;
2873
2874   /* Announce that we did a recursive lookup */
2875   tmp = DNS_AA | DNS_RA | DNS_RD | DNS_OPCODE_QUERY | DNS_QR;
2876   if (is_fail)
2877     tmp |= DNS_RCODE_NAME_ERROR;
2878   dh->flags = clib_host_to_net_u16 (tmp);
2879   dh->qdcount = clib_host_to_net_u16 (1);
2880   dh->anscount = (is_fail == 0) ? clib_host_to_net_u16 (1) : 0;
2881   dh->nscount = 0;
2882   dh->arcount = 0;
2883
2884   /* If the name resolution worked, cough up an appropriate RR */
2885   if (is_fail == 0)
2886     {
2887       /* Add the answer. First, a name pointer (0xC00C) */
2888       vec_add1 (reply, 0xC0);
2889       vec_add1 (reply, 0x0C);
2890
2891       /* Now, add single A-rec RR */
2892       if (pr->request_type == DNS_PEER_PENDING_NAME_TO_IP)
2893         {
2894           vec_add2 (reply, rrptr, sizeof (dns_rr_t) + sizeof (ip4_address_t));
2895           rr = (dns_rr_t *) rrptr;
2896
2897           rr->type = clib_host_to_net_u16 (DNS_TYPE_A);
2898           rr->class = clib_host_to_net_u16 (1 /* internet */ );
2899           rr->ttl = clib_host_to_net_u32 (ttl);
2900           rr->rdlength = clib_host_to_net_u16 (sizeof (ip4_address_t));
2901           clib_memcpy (rr->rdata, rnr->ip4_address, sizeof (ip4_address_t));
2902         }
2903       else
2904         {
2905           /* Or a single PTR RR */
2906           u8 *vecname = format (0, "%s", rir->name);
2907           u8 *label_vec = name_to_labels (vecname);
2908           vec_free (vecname);
2909
2910           vec_add2 (reply, rrptr, sizeof (dns_rr_t) + vec_len (label_vec));
2911           rr = (dns_rr_t *) rrptr;
2912           rr->type = clib_host_to_net_u16 (DNS_TYPE_PTR);
2913           rr->class = clib_host_to_net_u16 (1 /* internet */ );
2914           rr->ttl = clib_host_to_net_u32 (ttl);
2915           rr->rdlength = clib_host_to_net_u16 (vec_len (label_vec));
2916           clib_memcpy (rr->rdata, label_vec, vec_len (label_vec));
2917           vec_free (label_vec);
2918         }
2919     }
2920   clib_memcpy (dns_response, reply, vec_len (reply));
2921
2922   /* Set the packet length */
2923   b0->current_length = sizeof (*ip) + sizeof (*udp) + vec_len (reply);
2924
2925   /* IP header */
2926   ip->ip_version_and_header_length = 0x45;
2927   ip->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
2928   ip->ttl = 255;
2929   ip->protocol = IP_PROTOCOL_UDP;
2930   ip->src_address.as_u32 = src_address->as_u32;
2931   clib_memcpy (ip->dst_address.as_u8, pr->dst_address,
2932                sizeof (ip4_address_t));
2933   ip->checksum = ip4_header_checksum (ip);
2934
2935   /* UDP header */
2936   udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
2937   udp->dst_port = pr->dst_port;
2938   udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
2939                                       vec_len (reply));
2940   udp->checksum = 0;
2941   vec_free (reply);
2942
2943   /* Ship it to ip4_lookup */
2944   f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
2945   to_next = vlib_frame_vector_args (f);
2946   to_next[0] = bi;
2947   f->n_vectors = 1;
2948   vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
2949 }
2950
2951 /*
2952  * fd.io coding-style-patch-verification: ON
2953  *
2954  * Local Variables:
2955  * eval: (c-set-style "gnu")
2956  * End:
2957  */