VPP-1027: DNS name resolver
[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->api_clients_to_notify);
59     vec_free (ep->api_client_contexts);
60     vec_free (ep->ip4_peers_to_notify);
61     vec_free (ep->ip6_peers_to_notify);
62   }));
63   /* *INDENT-ON* */
64
65   pool_free (dm->entries);
66   hash_free (dm->cache_entry_by_name);
67   dm->cache_entry_by_name = hash_create_string (0, sizeof (uword));
68   vec_free (dm->unresolved_entries);
69   dns_cache_unlock (dm);
70   return 0;
71 }
72
73 static int
74 dns_enable_disable (dns_main_t * dm, int is_enable)
75 {
76   vlib_thread_main_t *tm = &vlib_thread_main;
77   u32 n_vlib_mains = tm->n_vlib_mains;
78
79   if (is_enable)
80     {
81       if (vec_len (dm->ip4_name_servers) == 0
82           && (vec_len (dm->ip6_name_servers) == 0))
83         return VNET_API_ERROR_NO_NAME_SERVERS;
84
85       if (dm->cache_entry_by_name == 0)
86         {
87           if (n_vlib_mains > 1)
88             dm->cache_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
89                                                      CLIB_CACHE_LINE_BYTES);
90
91           dm->cache_entry_by_name = hash_create_string (0, sizeof (uword));
92         }
93
94       dm->is_enabled = 1;
95     }
96   else
97     {
98       dns_cache_clear (dm);
99       dm->is_enabled = 0;
100     }
101   return 0;
102 }
103
104 static void vl_api_dns_enable_disable_t_handler
105   (vl_api_dns_enable_disable_t * mp)
106 {
107   vl_api_dns_enable_disable_reply_t *rmp;
108   dns_main_t *dm = &dns_main;
109   int rv;
110
111   rv = dns_enable_disable (dm, mp->enable);
112
113   REPLY_MACRO (VL_API_DNS_ENABLE_DISABLE_REPLY);
114 }
115
116 static int
117 dns6_name_server_add_del (dns_main_t * dm,
118                           u8 * server_address_as_u8, int is_add)
119 {
120   int i;
121   ip6_address_t *ap;
122
123   if (is_add)
124     {
125       /* Already there? done... */
126       for (i = 0; i < vec_len (dm->ip6_name_servers); i++)
127         {
128           if (!memcmp (dm->ip6_name_servers + i, server_address_as_u8,
129                        sizeof (ip6_address_t)))
130             return 0;
131         }
132
133       vec_add2 (dm->ip6_name_servers, ap, 1);
134       clib_memcpy (ap, server_address_as_u8, sizeof (*ap));
135     }
136   else
137     {
138       for (i = 0; i < vec_len (dm->ip6_name_servers); i++)
139         {
140           if (!memcmp (dm->ip6_name_servers + i, server_address_as_u8,
141                        sizeof (ip6_address_t)))
142             {
143               vec_delete (dm->ip6_name_servers, 1, i);
144               return 0;
145             }
146         }
147       return VNET_API_ERROR_NAME_SERVER_NOT_FOUND;
148     }
149   return 0;
150 }
151
152 static int
153 dns4_name_server_add_del (dns_main_t * dm,
154                           u8 * server_address_as_u8, int is_add)
155 {
156   int i;
157   ip4_address_t *ap;
158
159   if (is_add)
160     {
161       /* Already there? done... */
162       for (i = 0; i < vec_len (dm->ip4_name_servers); i++)
163         {
164           if (!memcmp (dm->ip4_name_servers + i, server_address_as_u8,
165                        sizeof (ip4_address_t)))
166             return 0;
167         }
168
169       vec_add2 (dm->ip4_name_servers, ap, 1);
170       clib_memcpy (ap, server_address_as_u8, sizeof (*ap));
171     }
172   else
173     {
174       for (i = 0; i < vec_len (dm->ip4_name_servers); i++)
175         {
176           if (!memcmp (dm->ip4_name_servers + i, server_address_as_u8,
177                        sizeof (ip4_address_t)))
178             {
179               vec_delete (dm->ip4_name_servers, 1, i);
180               return 0;
181             }
182         }
183       return VNET_API_ERROR_NAME_SERVER_NOT_FOUND;
184     }
185   return 0;
186 }
187
188 static void vl_api_dns_name_server_add_del_t_handler
189   (vl_api_dns_name_server_add_del_t * mp)
190 {
191   dns_main_t *dm = &dns_main;
192   vl_api_dns_name_server_add_del_reply_t *rmp;
193   int rv;
194
195   if (mp->is_ip6)
196     rv = dns6_name_server_add_del (dm, mp->server_address, mp->is_add);
197   else
198     rv = dns4_name_server_add_del (dm, mp->server_address, mp->is_add);
199
200   REPLY_MACRO (VL_API_DNS_NAME_SERVER_ADD_DEL_REPLY);
201 }
202
203 static void
204 send_dns4_request (dns_main_t * dm,
205                    dns_cache_entry_t * ep, ip4_address_t * server)
206 {
207   vlib_main_t *vm = dm->vlib_main;
208   f64 now = vlib_time_now (vm);
209   u32 bi;
210   vlib_buffer_t *b;
211   ip4_header_t *ip;
212   fib_prefix_t prefix;
213   fib_node_index_t fei;
214   u32 sw_if_index, fib_index;
215   udp_header_t *udp;
216   ip4_main_t *im4 = &ip4_main;
217   ip_lookup_main_t *lm4 = &im4->lookup_main;
218   ip_interface_address_t *ia = 0;
219   ip4_address_t *src_address;
220   u8 *dns_request;
221   vlib_frame_t *f;
222   u32 *to_next;
223
224   ASSERT (ep->dns_request);
225
226   /* Find a FIB path to the server */
227   clib_memcpy (&prefix.fp_addr.ip4, server, sizeof (*server));
228   prefix.fp_proto = FIB_PROTOCOL_IP4;
229   prefix.fp_len = 32;
230
231   fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ );
232   if (fib_index == (u32) ~ 0)
233     {
234       clib_warning ("no fib table");
235       return;
236     }
237
238   fei = fib_table_lookup (fib_index, &prefix);
239
240   /* Couldn't find route to destination. Bail out. */
241   if (fei == FIB_NODE_INDEX_INVALID)
242     {
243       clib_warning ("no route to DNS server");
244       return;
245     }
246
247   sw_if_index = fib_entry_get_resolving_interface (fei);
248
249   if (sw_if_index == ~0)
250     {
251       clib_warning
252         ("route to %U exists, fei %d, get_resolving_interface returned"
253          " ~0", fei, format_ip4_address, &prefix.fp_addr);
254       return;
255     }
256
257   /* *INDENT-OFF* */
258   foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnummbered */,
259   ({
260     src_address = ip_interface_address_get_address (lm4, ia);
261     goto found_src_address;
262   }));
263   /* *INDENT-ON* */
264
265   clib_warning ("FIB BUG");
266   return;
267
268 found_src_address:
269
270   /* Go get a buffer */
271   if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1)
272     return;
273
274   b = vlib_get_buffer (vm, bi);
275   b->current_length = sizeof (ip4_header_t) + sizeof (udp_header_t) +
276     vec_len (ep->dns_request);
277   b->total_length_not_including_first_buffer = 0;
278   b->flags =
279     VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED;
280   vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;    /* "local0" */
281   vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;    /* default VRF for now */
282
283   ip = vlib_buffer_get_current (b);
284   memset (ip, 0, sizeof (*ip));
285   udp = (udp_header_t *) (ip + 1);
286   memset (udp, 0, sizeof (*udp));
287
288   dns_request = (u8 *) (udp + 1);
289
290   /* IP header */
291   ip->ip_version_and_header_length = 0x45;
292   ip->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b));
293   ip->ttl = 255;
294   ip->protocol = IP_PROTOCOL_UDP;
295   ip->src_address.as_u32 = src_address->as_u32;
296   ip->dst_address.as_u32 = server->as_u32;
297   ip->checksum = ip4_header_checksum (ip);
298
299   /* UDP header */
300   udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns_reply);
301   udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
302   udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
303                                       vec_len (ep->dns_request));
304   udp->checksum = 0;
305
306   /* The actual DNS request */
307   clib_memcpy (dns_request, ep->dns_request, vec_len (ep->dns_request));
308
309   /* Ship it to ip4_lookup */
310   f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
311   to_next = vlib_frame_vector_args (f);
312   to_next[0] = bi;
313   f->n_vectors = 1;
314   vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
315
316   ep->retry_timer = now + 2.0;
317 }
318
319 static void
320 send_dns6_request (dns_main_t * dm,
321                    dns_cache_entry_t * ep, ip6_address_t * server)
322 {
323   vlib_main_t *vm = dm->vlib_main;
324   f64 now = vlib_time_now (vm);
325   u32 bi;
326   vlib_buffer_t *b;
327   ip6_header_t *ip;
328   fib_prefix_t prefix;
329   fib_node_index_t fei;
330   u32 sw_if_index, fib_index;
331   udp_header_t *udp;
332   ip6_main_t *im6 = &ip6_main;
333   ip_lookup_main_t *lm6 = &im6->lookup_main;
334   ip_interface_address_t *ia = 0;
335   ip6_address_t *src_address;
336   u8 *dns_request;
337   vlib_frame_t *f;
338   u32 *to_next;
339   int junk __attribute__ ((unused));
340
341   ASSERT (ep->dns_request);
342
343   /* Find a FIB path to the server */
344   clib_memcpy (&prefix.fp_addr, server, sizeof (*server));
345   prefix.fp_proto = FIB_PROTOCOL_IP6;
346   prefix.fp_len = 32;
347
348   fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ );
349   if (fib_index == (u32) ~ 0)
350     {
351       clib_warning ("no fib table");
352       return;
353     }
354
355   fei = fib_table_lookup (fib_index, &prefix);
356
357   /* Couldn't find route to destination. Bail out. */
358   if (fei == FIB_NODE_INDEX_INVALID)
359     {
360       clib_warning ("no route to DNS server");
361     }
362
363   sw_if_index = fib_entry_get_resolving_interface (fei);
364
365   /* *INDENT-OFF* */
366   foreach_ip_interface_address(lm6, ia, sw_if_index, 1 /* honor unnummbered */,
367   ({
368     src_address = ip_interface_address_get_address (lm6, ia);
369     goto found_src_address;
370   }));
371   /* *INDENT-ON* */
372
373   clib_warning ("FIB BUG");
374   return;
375
376 found_src_address:
377
378   /* Go get a buffer */
379   if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1)
380     return;
381
382   b = vlib_get_buffer (vm, bi);
383   b->current_length = sizeof (ip6_header_t) + sizeof (udp_header_t) +
384     vec_len (ep->dns_request);
385   b->total_length_not_including_first_buffer = 0;
386   b->flags =
387     VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED;
388
389   ip = vlib_buffer_get_current (b);
390   memset (ip, 0, sizeof (*ip));
391   udp = (udp_header_t *) (ip + 1);
392   memset (udp, 0, sizeof (*udp));
393
394   dns_request = (u8 *) (udp + 1);
395
396   /* IP header */
397   ip->ip_version_traffic_class_and_flow_label =
398     clib_host_to_net_u32 (0x6 << 28);
399
400   ip->payload_length =
401     clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b)
402                           - sizeof (ip6_header_t));
403   ip->hop_limit = 255;
404   ip->protocol = IP_PROTOCOL_UDP;
405   clib_memcpy (&ip->src_address, src_address, sizeof (ip6_address_t));
406   clib_memcpy (&ip->dst_address, server, sizeof (ip6_address_t));
407
408   /* UDP header */
409   udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns_reply);
410   udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
411   udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
412                                       vec_len (ep->dns_request));
413   udp->checksum = 0;
414   udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip, &junk);
415
416   /* The actual DNS request */
417   clib_memcpy (dns_request, ep->dns_request, vec_len (ep->dns_request));
418
419   /* Ship it to ip6_lookup */
420   f = vlib_get_frame_to_node (vm, ip6_lookup_node.index);
421   to_next = vlib_frame_vector_args (f);
422   to_next[0] = bi;
423   f->n_vectors = 1;
424
425   ep->retry_timer = now + 2.0;
426 }
427
428 /**
429  * Translate "foo.com" into "0x3 f o o 0x3 c o m 0x0"
430  * A historical / hysterical micro-TLV scheme. DGMS.
431  */
432 u8 *
433 name_to_labels (u8 * name)
434 {
435   int i;
436   int last_label_index;
437   u8 *rv;
438
439   rv = vec_dup (name);
440
441   /* punch in space for the first length */
442   vec_insert (rv, 1, 0);
443   last_label_index = 0;
444   i = 1;
445
446   while (i < vec_len (rv))
447     {
448       if (rv[i] == '.')
449         {
450           rv[last_label_index] = (i - last_label_index) - 1;
451           if ((i - last_label_index) > 63)
452             clib_warning ("stupid name, label length %d",
453                           i - last_label_index);
454           last_label_index = i;
455           rv[i] = 0;
456         }
457       i++;
458     }
459   /* Set the last real label length */
460   rv[last_label_index] = (i - last_label_index) - 1;
461
462   /*
463    * Add a [sic] NULL root label. Otherwise, the name parser can't figure out
464    * where to stop.
465    */
466   vec_add1 (rv, 0);
467   return rv;
468 }
469
470 /**
471  * arc-function for the above.
472  * Translate "0x3 f o o 0x3 c o m 0x0" into "foo.com"
473  * Produces a non-NULL-terminated u8 *vector. %v format is your friend.
474  */
475 u8 *
476 labels_to_name (u8 * label, u8 * full_text, u8 ** parse_from_here)
477 {
478   u8 *reply = 0;
479   u16 offset;
480   u8 len;
481   int i;
482
483   *parse_from_here = 0;
484
485   /* chase initial pointer? */
486   if ((label[0] & 0xC0) == 0xC0)
487     {
488       *parse_from_here = label + 2;
489       offset = ((label[0] & 0x3f) << 8) + label[1];
490       label = full_text + offset;
491     }
492
493   len = *label++;
494
495   while (len)
496     {
497       for (i = 0; i < len; i++)
498         vec_add1 (reply, *label++);
499
500       /* chase pointer? */
501       if ((label[0] & 0xC0) == 0xC0)
502         {
503           *parse_from_here = label + 2;
504           offset = ((label[0] & 0x3f) << 8) + label[1];
505           label = full_text + offset;
506         }
507
508       len = *label++;
509       if (len)
510         vec_add1 (reply, '.');
511     }
512   if (*parse_from_here == 0)
513     *parse_from_here = label;
514   return reply;
515 }
516
517 void
518 vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
519 {
520   dns_header_t *h;
521   dns_query_t *qp;
522   u16 tmp;
523   u8 *request;
524   u32 qp_offset;
525
526   /* Construct the dns request, if we haven't been here already */
527   if (vec_len (ep->dns_request) == 0)
528     {
529       /*
530        * Start with the variadic portion of the exercise.
531        * Turn the name into a set of DNS "labels". Max length
532        * per label is 63, enforce that.
533        */
534       request = name_to_labels (ep->name);
535       qp_offset = vec_len (request);
536
537       /* Add space for the query header */
538       vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
539
540       qp = (dns_query_t *) (request + qp_offset);
541
542       qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
543       qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
544
545       /* Punch in space for the dns_header_t */
546       vec_insert (request, sizeof (dns_header_t), 0);
547
548       h = (dns_header_t *) request;
549
550       /* Transaction ID = pool index */
551       h->id = clib_host_to_net_u16 (ep - dm->entries);
552
553       /* Ask for a recursive lookup */
554       tmp = DNS_RD | DNS_OPCODE_QUERY;
555       h->flags = clib_host_to_net_u16 (tmp);
556       h->qdcount = clib_host_to_net_u16 (1);
557       h->nscount = 0;
558       h->arcount = 0;
559
560       ep->dns_request = request;
561     }
562
563   /* Work out which server / address family we're going to use */
564
565   /* Retry using current server */
566   if (ep->retry_count++ < DNS_RETRIES_PER_SERVER)
567     {
568       if (ep->server_af == 1 /* ip6 */ )
569         {
570           if (vec_len (dm->ip6_name_servers))
571             {
572               send_dns6_request (dm, ep,
573                                  dm->ip6_name_servers + ep->server_rotor);
574               goto out;
575             }
576           else
577             ep->server_af = 0;
578         }
579       if (vec_len (dm->ip4_name_servers))
580         {
581           send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
582           goto out;
583         }
584     }
585   else                          /* switch to a new server */
586     {
587       ep->retry_count = 1;
588       ep->server_rotor++;
589       if (ep->server_af == 1 /* ip6 */ )
590         {
591           if (ep->server_rotor >= vec_len (dm->ip6_name_servers))
592             {
593               ep->server_rotor = 0;
594               ep->server_af = vec_len (dm->ip4_name_servers) > 0 ? 0 : 1;
595             }
596         }
597       else
598         {
599           if (ep->server_rotor >= vec_len (dm->ip4_name_servers))
600             {
601               ep->server_rotor = 0;
602               ep->server_af = vec_len (dm->ip6_name_servers) > 0 ? 1 : 0;
603             }
604         }
605     }
606
607   if (ep->server_af == 1 /* ip6 */ )
608     send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor);
609   else
610     send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
611
612 out:
613
614   vlib_process_signal_event_mt (dm->vlib_main, dns_resolver_node.index,
615                                 DNS_RESOLVER_EVENT_PENDING, 0);
616 }
617
618 int
619 vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index)
620 {
621   dns_cache_entry_t *ep;
622   int i;
623
624   if (dm->is_enabled == 0)
625     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
626
627   if (pool_is_free_index (dm->entries, index))
628     return VNET_API_ERROR_NO_SUCH_ENTRY;
629
630   ep = pool_elt_at_index (dm->entries, index);
631
632   if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_VALID))
633     {
634       for (i = 0; i < vec_len (dm->unresolved_entries); i++)
635         if (index == dm->unresolved_entries[i])
636           {
637             vec_delete (dm->unresolved_entries, 1, i);
638             goto found;
639           }
640       clib_warning ("pool elt %d supposedly pending, but not found...",
641                     index);
642     }
643
644 found:
645   hash_unset_mem (dm->cache_entry_by_name, ep->name);
646   vec_free (ep->name);
647   vec_free (ep->api_clients_to_notify);
648   vec_free (ep->api_client_contexts);
649   vec_free (ep->ip4_peers_to_notify);
650   vec_free (ep->ip6_peers_to_notify);
651   pool_put (dm->entries, ep);
652
653   return 0;
654 }
655
656 static int
657 dns_delete_by_name (dns_main_t * dm, u8 * name)
658 {
659   int rv;
660   uword *p;
661
662   if (dm->is_enabled == 0)
663     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
664
665   dns_cache_lock (dm);
666   p = hash_get_mem (dm->cache_entry_by_name, name);
667   if (!p)
668     {
669       dns_cache_unlock (dm);
670       return VNET_API_ERROR_NO_SUCH_ENTRY;
671     }
672   rv = vnet_dns_delete_entry_by_index_nolock (dm, p[0]);
673
674   dns_cache_unlock (dm);
675
676   return rv;
677 }
678
679 static int
680 delete_random_entry (dns_main_t * dm)
681 {
682   int rv;
683   u32 victim_index, start_index, i;
684   u32 limit;
685   dns_cache_entry_t *ep;
686
687   if (dm->is_enabled == 0)
688     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
689
690   dns_cache_lock (dm);
691   limit = pool_elts (dm->entries);
692   start_index = random_u32 (&dm->random_seed) % limit;
693
694   for (i = 0; i < limit; i++)
695     {
696       victim_index = (start_index + i) % limit;
697
698       if (!pool_is_free_index (dm->entries, victim_index))
699         {
700           ep = pool_elt_at_index (dm->entries, victim_index);
701           /* Delete only valid, non-static entries */
702           if ((ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
703               && ((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0))
704             {
705               rv = vnet_dns_delete_entry_by_index_nolock (dm, victim_index);
706               dns_cache_unlock (dm);
707               return rv;
708             }
709         }
710     }
711   dns_cache_unlock (dm);
712
713   clib_warning ("Couldn't find an entry to delete?");
714   return VNET_API_ERROR_UNSPECIFIED;
715 }
716
717 static int
718 dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data)
719 {
720   dns_cache_entry_t *ep;
721   uword *p;
722   int rv;
723
724   if (dm->is_enabled == 0)
725     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
726
727   dns_cache_lock (dm);
728   p = hash_get_mem (dm->cache_entry_by_name, name);
729   if (p)
730     {
731       dns_cache_unlock (dm);
732       return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
733     }
734
735   if (pool_elts (dm->entries) == dm->name_cache_size)
736     {
737       /* Will only fail if the cache is totally filled w/ static entries... */
738       rv = delete_random_entry (dm);
739       if (rv)
740         {
741           dns_cache_unlock (dm);
742           return rv;
743         }
744     }
745
746   pool_get (dm->entries, ep);
747   memset (ep, 0, sizeof (*ep));
748
749   /* Note: consumes the name vector */
750   ep->name = name;
751   hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
752   ep->flags = DNS_CACHE_ENTRY_FLAG_VALID | DNS_CACHE_ENTRY_FLAG_STATIC;
753   ep->dns_response = dns_reply_data;
754
755   dns_cache_unlock (dm);
756   return 0;
757 }
758
759 static int
760 dns_resolve_name (dns_main_t * dm,
761                   u8 * name, u32 client_index, u32 client_context,
762                   dns_cache_entry_t ** retp)
763 {
764   dns_cache_entry_t *ep;
765   int rv;
766   f64 now;
767   uword *p;
768
769   now = vlib_time_now (dm->vlib_main);
770
771   /* In case we can't actually answer the question right now... */
772   *retp = 0;
773
774   dns_cache_lock (dm);
775   p = hash_get_mem (dm->cache_entry_by_name, name);
776   if (p)
777     {
778       ep = pool_elt_at_index (dm->entries, p[0]);
779       if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
780         {
781           /* Has the entry expired? */
782           if (((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0)
783               && (now > ep->expiration_time))
784             {
785               clib_warning ("Re-resolve %s", name);
786               /* Yes, kill it... */
787               vnet_dns_delete_entry_by_index_nolock (dm, p[0]);
788               goto re_resolve;
789             }
790
791           /* Note: caller must drop the lock! */
792           *retp = ep;
793           return (0);
794         }
795     }
796
797   if (pool_elts (dm->entries) == dm->name_cache_size)
798     {
799       /* Will only fail if the cache is totally filled w/ static entries... */
800       rv = delete_random_entry (dm);
801       if (rv)
802         {
803           dns_cache_unlock (dm);
804           return rv;
805         }
806     }
807
808 re_resolve:
809   /* add new hash table entry */
810   pool_get (dm->entries, ep);
811   memset (ep, 0, sizeof (*ep));
812
813   ep->name = format (0, "%s%c", name, 0);
814   _vec_len (ep->name) = vec_len (ep->name) - 1;
815
816   hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
817
818   vec_add1 (dm->unresolved_entries, ep - dm->entries);
819   vec_add1 (ep->api_clients_to_notify, client_index);
820   vec_add1 (ep->api_client_contexts, client_context);
821   vnet_send_dns_request (dm, ep);
822   dns_cache_unlock (dm);
823
824   return 0;
825 }
826
827 /**
828  * Handle cname indirection. JFC. Called with the cache locked.
829  * returns 0 if the reply is not a CNAME.
830  */
831
832 int
833 vnet_dns_cname_indirection_nolock (dns_main_t * dm, dns_cache_entry_t * ep,
834                                    u8 * reply)
835 {
836   dns_header_t *h;
837   dns_query_t *qp;
838   dns_rr_t *rr;
839   u8 *curpos;
840   u8 *pos, *pos2;
841   int len, i;
842   u8 *cname = 0;
843   u8 *request = 0;
844   u32 qp_offset;
845   u16 flags;
846   u16 rcode;
847
848   h = (dns_header_t *) reply;
849   flags = clib_net_to_host_u16 (h->flags);
850   rcode = flags & DNS_RCODE_MASK;
851
852   /* See if the response is OK */
853   switch (rcode)
854     {
855     case DNS_RCODE_NO_ERROR:
856       break;
857
858     case DNS_RCODE_NAME_ERROR:
859     case DNS_RCODE_FORMAT_ERROR:
860     case DNS_RCODE_SERVER_FAILURE:
861     case DNS_RCODE_NOT_IMPLEMENTED:
862     case DNS_RCODE_REFUSED:
863       return 0;
864     }
865
866   curpos = (u8 *) (h + 1);
867   pos = curpos;
868   len = *pos++;
869
870   /* Skip the questions */
871   for (i = 0; i < clib_net_to_host_u16 (h->qdcount); i++)
872     {
873       while (len)
874         {
875           pos += len;
876           len = *pos++;
877         }
878       qp = (dns_query_t *) pos;
879       pos += sizeof (*qp);
880     }
881   pos2 = pos;
882   /* expect a pointer chase here for a CNAME record */
883   if ((pos2[0] & 0xC0) == 0xC0)
884     pos += 2;
885   else
886     return 0;
887
888   rr = (dns_rr_t *) pos;
889
890   /* This is a real record, not a CNAME record */
891   if (clib_net_to_host_u16 (rr->type) != DNS_TYPE_CNAME)
892     return 0;
893
894   /* Crap. Chase the CNAME name chain. */
895
896   cname = labels_to_name (rr->rdata, reply, &pos2);
897   request = name_to_labels (cname);
898   vec_free (cname);
899
900   qp_offset = vec_len (request);
901
902   /* Add space for the query header */
903   vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
904
905   qp = (dns_query_t *) (request + qp_offset);
906
907   qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
908   qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
909
910   /* Punch in space for the dns_header_t */
911   vec_insert (request, sizeof (dns_header_t), 0);
912
913   h = (dns_header_t *) request;
914
915   /* Transaction ID = pool index */
916   h->id = clib_host_to_net_u16 (ep - dm->entries);
917
918   /* Ask for a recursive lookup */
919   h->flags = clib_host_to_net_u16 (DNS_RD | DNS_OPCODE_QUERY);
920   h->qdcount = clib_host_to_net_u16 (1);
921   h->nscount = 0;
922   h->arcount = 0;
923
924   vec_free (ep->dns_request);
925   ep->dns_request = request;
926   ep->retry_timer = vlib_time_now (dm->vlib_main) + 2.0;
927   ep->retry_count = 0;
928
929   /*
930    * Enable this to watch recursive resolution happen...
931    * fformat (stdout, "%U", format_dns_reply, request, 2);
932    */
933
934   if (ep->server_af == 1 /* ip6 */ )
935     send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor);
936   else
937     send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
938
939   vec_free (reply);
940   return (1);
941 }
942
943 int
944 vnet_dns_response_to_reply (u8 * response,
945                             vl_api_dns_resolve_name_reply_t * rmp,
946                             u32 * min_ttlp)
947 {
948   dns_header_t *h;
949   dns_query_t *qp;
950   dns_rr_t *rr;
951   int i, limit;
952   u8 len;
953   u8 *curpos, *pos;
954   u16 flags;
955   u16 rcode;
956   u32 ttl;
957
958   h = (dns_header_t *) response;
959   flags = clib_net_to_host_u16 (h->flags);
960   rcode = flags & DNS_RCODE_MASK;
961
962   /* See if the response is OK, etc. */
963   switch (rcode)
964     {
965     default:
966     case DNS_RCODE_NO_ERROR:
967       break;
968
969     case DNS_RCODE_NAME_ERROR:
970     case DNS_RCODE_FORMAT_ERROR:
971       return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME;
972
973     case DNS_RCODE_SERVER_FAILURE:
974     case DNS_RCODE_NOT_IMPLEMENTED:
975     case DNS_RCODE_REFUSED:
976       return VNET_API_ERROR_NAME_SERVER_NEXT_SERVER;
977     }
978
979   /* No answers? Loser... */
980   if (clib_net_to_host_u16 (h->anscount) < 1)
981     return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
982
983   curpos = (u8 *) (h + 1);
984
985   /* Skip the name we asked about */
986   pos = curpos;
987   len = *pos++;
988   /* Should never happen, but stil... */
989   if ((len & 0xC0) == 0xC0)
990     curpos += 2;
991   else
992     {
993       /* skip the name / label-set */
994       while (len)
995         {
996           pos += len;
997           len = *pos++;
998         }
999       curpos = pos;
1000     }
1001   /* Skip queries */
1002   limit = clib_net_to_host_u16 (h->qdcount);
1003   qp = (dns_query_t *) curpos;
1004   qp += limit;
1005   curpos = (u8 *) qp;
1006
1007   /* Parse answers */
1008   limit = clib_net_to_host_u16 (h->anscount);
1009
1010   for (i = 0; i < limit; i++)
1011     {
1012       pos = curpos;
1013
1014       /* Expect pointer chases in the answer section... */
1015       if ((pos[0] & 0xC0) == 0xC0)
1016         curpos += 2;
1017       else
1018         {
1019           len = *pos++;
1020           while (len)
1021             {
1022               if ((pos[0] & 0xC0) == 0xC0)
1023                 {
1024                   curpos = pos + 2;
1025                   break;
1026                 }
1027               pos += len;
1028               len = *pos++;
1029             }
1030           curpos = pos;
1031         }
1032
1033       rr = (dns_rr_t *) curpos;
1034
1035       switch (clib_net_to_host_u16 (rr->type))
1036         {
1037         case DNS_TYPE_A:
1038           /* Collect an ip4 address. Do not pass go. Do not collect $200 */
1039           memcpy (rmp->ip4_address, rr->rdata, sizeof (ip4_address_t));
1040           rmp->ip4_set = 1;
1041           ttl = clib_net_to_host_u32 (rr->ttl);
1042           if (min_ttlp && *min_ttlp > ttl)
1043             *min_ttlp = ttl;
1044           break;
1045         case DNS_TYPE_AAAA:
1046           /* Collect an ip6 address. Do not pass go. Do not collect $200 */
1047           memcpy (rmp->ip6_address, rr->rdata, sizeof (ip6_address_t));
1048           ttl = clib_net_to_host_u32 (rr->ttl);
1049           if (min_ttlp && *min_ttlp > ttl)
1050             *min_ttlp = ttl;
1051           rmp->ip6_set = 1;
1052           break;
1053         default:
1054           break;
1055         }
1056       /* Might as well stop ASAP */
1057       if (rmp->ip4_set && rmp->ip6_set)
1058         break;
1059       curpos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1060     }
1061
1062   if ((rmp->ip4_set + rmp->ip6_set) == 0)
1063     return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
1064   return 0;
1065 }
1066
1067 static void
1068 vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp)
1069 {
1070   dns_main_t *dm = &dns_main;
1071   vl_api_dns_resolve_name_reply_t *rmp;
1072   dns_cache_entry_t *ep;
1073   int rv;
1074
1075   /* Sanitize the name slightly */
1076   mp->name[ARRAY_LEN (mp->name) - 1] = 0;
1077
1078   rv = dns_resolve_name (dm, mp->name, mp->client_index, mp->context, &ep);
1079
1080   /* Error, e.g. not enabled? Tell the user */
1081   if (rv < 0)
1082     {
1083       REPLY_MACRO (VL_API_DNS_RESOLVE_NAME_REPLY);
1084       return;
1085     }
1086
1087   /* Resolution pending? Don't reply... */
1088   if (ep == 0)
1089     return;
1090
1091   /* *INDENT-OFF* */
1092   REPLY_MACRO2(VL_API_DNS_RESOLVE_NAME_REPLY,
1093   ({
1094     rv = vnet_dns_response_to_reply (ep->dns_response, rmp, 0 /* ttl-ptr */);
1095     rmp->retval = clib_host_to_net_u32 (rv);
1096   }));
1097   /* *INDENT-ON* */
1098
1099   /*
1100    * dns_resolve_name leaves the cache locked when it returns
1101    * a cached result, so unlock it here.
1102    */
1103   dns_cache_unlock (dm);
1104 }
1105
1106 #define vl_msg_name_crc_list
1107 #include <vpp/api/vpe_all_api_h.h>
1108 #undef vl_msg_name_crc_list
1109
1110 static void
1111 setup_message_id_table (api_main_t * am)
1112 {
1113 #define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
1114   foreach_vl_msg_name_crc_dns;
1115 #undef _
1116 }
1117
1118 #define foreach_dns_api_msg                             \
1119 _(DNS_ENABLE_DISABLE, dns_enable_disable)               \
1120 _(DNS_NAME_SERVER_ADD_DEL, dns_name_server_add_del)     \
1121 _(DNS_RESOLVE_NAME, dns_resolve_name)
1122
1123 static clib_error_t *
1124 dns_api_hookup (vlib_main_t * vm)
1125 {
1126 #define _(N,n)                                                  \
1127     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1128                            vl_api_##n##_t_handler,              \
1129                            vl_noop_handler,                     \
1130                            vl_api_##n##_t_endian,               \
1131                            vl_api_##n##_t_print,                \
1132                            sizeof(vl_api_##n##_t), 1);
1133   foreach_dns_api_msg;
1134 #undef _
1135
1136   setup_message_id_table (&api_main);
1137   return 0;
1138 }
1139
1140 VLIB_API_INIT_FUNCTION (dns_api_hookup);
1141
1142
1143 static clib_error_t *
1144 dns_config_fn (vlib_main_t * vm, unformat_input_t * input)
1145 {
1146   dns_main_t *dm = &dns_main;
1147
1148   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1149     {
1150       if (unformat (input, "max-cache-size %u", &dm->name_cache_size))
1151         ;
1152       else if (unformat (input, "max-ttl %u", &dm->max_ttl_in_seconds))
1153         ;
1154       else
1155         return clib_error_return (0, "unknown input `%U'",
1156                                   format_unformat_error, input);
1157     }
1158   return 0;
1159 }
1160
1161 VLIB_CONFIG_FUNCTION (dns_config_fn, "dns");
1162
1163 static clib_error_t *
1164 dns_init (vlib_main_t * vm)
1165 {
1166   dns_main_t *dm = &dns_main;
1167
1168   dm->vlib_main = vm;
1169   dm->vnet_main = vnet_get_main ();
1170   dm->name_cache_size = 65535;
1171   dm->max_ttl_in_seconds = 86400;
1172   dm->random_seed = 0xDEADDABE;
1173
1174   udp_register_dst_port (vm, UDP_DST_PORT_dns_reply, dns46_reply_node.index,
1175                          1 /* is_ip4 */ );
1176
1177   udp_register_dst_port (vm, UDP_DST_PORT_dns_reply6, dns46_reply_node.index,
1178                          0 /* is_ip4 */ );
1179
1180 #if 0
1181   udp_register_dst_port (vm, UDP_DST_PORT_dns, dns4_request_node.index,
1182                          1 /* is_ip4 */ );
1183   udp_register_dst_port (vm, UDP_DST_PORT_dns6, dns6_request_node.index,
1184                          0 /* is_ip4 */ );
1185 #endif
1186
1187   return 0;
1188 }
1189
1190 VLIB_INIT_FUNCTION (dns_init);
1191
1192 uword
1193 unformat_dns_reply (unformat_input_t * input, va_list * args)
1194 {
1195   u8 **result = va_arg (*args, u8 **);
1196   u8 **namep = va_arg (*args, u8 **);
1197   ip4_address_t a4;
1198   ip6_address_t a6;
1199   int a4_set = 0;
1200   int a6_set = 0;
1201   u8 *name;
1202   int name_set = 0;
1203   u8 *ce;
1204   u32 qp_offset;
1205   dns_header_t *h;
1206   dns_query_t *qp;
1207   dns_rr_t *rr;
1208   u8 *rru8;
1209
1210   if (unformat (input, "%v", &name))
1211     name_set = 1;
1212
1213   if (unformat (input, "%U", unformat_ip4_address, &a4))
1214     {
1215       a4_set = 1;
1216       if (unformat (input, "%U", unformat_ip6_address, &a6))
1217         a6_set = 1;
1218     }
1219
1220   if (unformat (input, "%U", unformat_ip6_address, &a6))
1221     {
1222       a6_set = 1;
1223       if (unformat (input, "%U", unformat_ip4_address, &a6))
1224         a4_set = 1;
1225     }
1226
1227   /* Must have a name */
1228   if (!name_set)
1229     return 0;
1230
1231   /* Must have at least one address */
1232   if (!(a4_set + a6_set))
1233     return 0;
1234
1235   /* Build a fake DNS cache entry string, one hemorrhoid at a time */
1236   ce = name_to_labels (name);
1237   qp_offset = vec_len (ce);
1238
1239   /* Add space for the query header */
1240   vec_validate (ce, qp_offset + sizeof (dns_query_t) - 1);
1241   qp = (dns_query_t *) (ce + qp_offset);
1242
1243   qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
1244   qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1245
1246   /* Punch in space for the dns_header_t */
1247   vec_insert (ce, sizeof (dns_header_t), 0);
1248
1249   h = (dns_header_t *) ce;
1250
1251   /* Fake Transaction ID */
1252   h->id = 0xFFFF;
1253
1254   h->flags = clib_host_to_net_u16 (DNS_RD | DNS_RA);
1255   h->qdcount = clib_host_to_net_u16 (1);
1256   h->anscount = clib_host_to_net_u16 (a4_set + a6_set);
1257   h->nscount = 0;
1258   h->arcount = 0;
1259
1260   /* Now append one or two A/AAAA RR's... */
1261   if (a4_set)
1262     {
1263       /* Pointer to the name (DGMS) */
1264       vec_add1 (ce, 0xC0);
1265       vec_add1 (ce, 0x0C);
1266       vec_add2 (ce, rru8, sizeof (*rr) + 4);
1267       rr = (void *) rru8;
1268       rr->type = clib_host_to_net_u16 (DNS_TYPE_A);
1269       rr->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1270       rr->ttl = clib_host_to_net_u32 (86400);
1271       rr->rdlength = clib_host_to_net_u16 (4);
1272       memcpy (rr->rdata, &a4, sizeof (a4));
1273     }
1274   if (a6_set)
1275     {
1276       /* Pointer to the name (DGMS) */
1277       vec_add1 (ce, 0xC0);
1278       vec_add1 (ce, 0x0C);
1279       vec_add2 (ce, rru8, sizeof (*rr) + 16);
1280       rr = (void *) rru8;
1281       rr->type = clib_host_to_net_u16 (DNS_TYPE_AAAA);
1282       rr->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1283       rr->ttl = clib_host_to_net_u32 (86400);
1284       rr->rdlength = clib_host_to_net_u16 (16);
1285       memcpy (rr->rdata, &a6, sizeof (a6));
1286     }
1287   *result = ce;
1288   if (namep)
1289     *namep = name;
1290   else
1291     vec_free (name);
1292
1293   return 1;
1294 }
1295
1296 u8 *
1297 format_dns_query (u8 * s, va_list * args)
1298 {
1299   u8 **curpos = va_arg (*args, u8 **);
1300   int verbose = va_arg (*args, int);
1301   u8 *pos;
1302   dns_query_t *qp;
1303   int len, i;
1304   if (verbose > 1)
1305     s = format (s, "    Name: ");
1306
1307   /* Unwind execrated counted-label sheit */
1308   pos = *curpos;
1309   len = *pos++;
1310
1311   while (len)
1312     {
1313       for (i = 0; i < len; i++)
1314         vec_add1 (s, *pos++);
1315
1316       len = *pos++;
1317       if (len)
1318         vec_add1 (s, '.');
1319       else
1320         {
1321           vec_add1 (s, ':');
1322           vec_add1 (s, ' ');
1323         }
1324     }
1325
1326   qp = (dns_query_t *) pos;
1327   if (verbose > 1)
1328     {
1329       switch (clib_net_to_host_u16 (qp->type))
1330         {
1331         case DNS_TYPE_A:
1332           s = format (s, "type A\n");
1333           break;
1334         case DNS_TYPE_AAAA:
1335           s = format (s, "type AAAA\n");
1336           break;
1337         case DNS_TYPE_ALL:
1338           s = format (s, "type ALL\n");
1339           break;
1340
1341         default:
1342           s = format (s, "type %d\n", clib_net_to_host_u16 (qp->type));
1343           break;
1344         }
1345     }
1346
1347   pos += sizeof (*qp);
1348
1349   *curpos = pos;
1350   return s;
1351 }
1352
1353 /**
1354  * format dns reply data
1355  * verbose > 1, dump everything
1356  * verbose == 1, dump all A and AAAA records
1357  * verbose == 0, dump one A record, and one AAAA record
1358  */
1359
1360 u8 *
1361 format_dns_reply_data (u8 * s, va_list * args)
1362 {
1363   u8 *reply = va_arg (*args, u8 *);
1364   u8 **curpos = va_arg (*args, u8 **);
1365   int verbose = va_arg (*args, int);
1366   int *print_ip4 = va_arg (*args, int *);
1367   int *print_ip6 = va_arg (*args, int *);
1368   int len;
1369   u8 *pos, *pos2;
1370   dns_rr_t *rr;
1371   int i;
1372   int initial_pointer_chase = 0;
1373   u16 *tp;
1374
1375   pos = pos2 = *curpos;
1376
1377   if (verbose > 1)
1378     s = format (s, "    ");
1379
1380   /* chase pointer? almost always yes here... */
1381   if (pos2[0] == 0xc0)
1382     {
1383       pos2 = reply + pos2[1];
1384       pos += 2;
1385       initial_pointer_chase = 1;
1386     }
1387
1388   len = *pos2++;
1389
1390   while (len)
1391     {
1392       for (i = 0; i < len; i++)
1393         {
1394           if (verbose > 1)
1395             vec_add1 (s, *pos2);
1396           pos2++;
1397         }
1398       len = *pos2++;
1399       if (len)
1400         {
1401           if (verbose > 1)
1402             vec_add1 (s, '.');
1403         }
1404       else
1405         {
1406           if (verbose > 1)
1407             vec_add1 (s, ' ');
1408         }
1409     }
1410
1411   if (initial_pointer_chase == 0)
1412     pos = pos2;
1413
1414   rr = (dns_rr_t *) pos;
1415
1416   switch (clib_net_to_host_u16 (rr->type))
1417     {
1418     case DNS_TYPE_A:
1419       if (verbose > 1)
1420         {
1421           s = format (s, "A: ttl %d %U\n", clib_net_to_host_u32 (rr->ttl),
1422                       format_ip4_address, rr->rdata);
1423         }
1424       else
1425         {
1426           if (*print_ip4)
1427             s = format (s, "%U [%u] ", format_ip4_address, rr->rdata,
1428                         clib_net_to_host_u32 (rr->ttl));
1429           if (verbose == 0)
1430             *print_ip4 = 0;
1431
1432         }
1433       pos += sizeof (*rr) + 4;
1434       break;
1435
1436     case DNS_TYPE_AAAA:
1437       if (verbose > 1)
1438         {
1439           s = format (s, "AAAA: ttl %d %U\n", clib_net_to_host_u32 (rr->ttl),
1440                       format_ip6_address, rr->rdata);
1441         }
1442       else
1443         {
1444           if (*print_ip6)
1445             s = format (s, "%U [%u] ", format_ip6_address, rr->rdata,
1446                         clib_net_to_host_u32 (rr->ttl));
1447           if (verbose == 0)
1448             *print_ip6 = 0;
1449         }
1450       pos += sizeof (*rr) + 16;
1451       break;
1452
1453     case DNS_TYPE_TEXT:
1454       if (verbose > 1)
1455         {
1456           s = format (s, "TEXT: ");
1457           for (i = 0; i < clib_net_to_host_u16 (rr->rdlength); i++)
1458             vec_add1 (s, rr->rdata[i]);
1459           vec_add1 (s, '\n');
1460         }
1461       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1462       break;
1463
1464     case DNS_TYPE_NAMESERVER:
1465       if (verbose > 1)
1466         {
1467           s = format (s, "Nameserver: ");
1468           pos2 = rr->rdata;
1469
1470           /* chase pointer? */
1471           if (pos2[0] == 0xc0)
1472             pos2 = reply + pos2[1];
1473
1474           len = *pos2++;
1475
1476           while (len)
1477             {
1478               for (i = 0; i < len; i++)
1479                 vec_add1 (s, *pos2++);
1480
1481               /* chase pointer, typically to offset 12... */
1482               if (pos2[0] == 0xC0)
1483                 pos2 = reply + pos2[1];
1484
1485               len = *pos2++;
1486               if (len)
1487                 vec_add1 (s, '.');
1488               else
1489                 vec_add1 (s, '\n');
1490             }
1491         }
1492       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1493       break;
1494
1495     case DNS_TYPE_MAIL_EXCHANGE:
1496       if (verbose > 1)
1497         {
1498           tp = (u16 *) rr->rdata;
1499
1500           s = format (s, "Mail Exchange: Preference %d ", (u32)
1501                       clib_net_to_host_u16 (*tp));
1502
1503           pos2 = rr->rdata + 2;
1504
1505           /* chase pointer? */
1506           if (pos2[0] == 0xc0)
1507             pos2 = reply + pos2[1];
1508
1509           len = *pos2++;
1510
1511           while (len)
1512             {
1513               for (i = 0; i < len; i++)
1514                 vec_add1 (s, *pos2++);
1515
1516               /* chase pointer */
1517               if (pos2[0] == 0xC0)
1518                 pos2 = reply + pos2[1];
1519
1520               len = *pos2++;
1521               if (len)
1522                 vec_add1 (s, '.');
1523               else
1524                 vec_add1 (s, '\n');
1525             }
1526         }
1527
1528       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1529       break;
1530
1531     case DNS_TYPE_CNAME:
1532       if (verbose > 1)
1533         {
1534           tp = (u16 *) rr->rdata;
1535
1536           s = format (s, "CNAME: ");
1537
1538           pos2 = rr->rdata;
1539
1540           /* chase pointer? */
1541           if (pos2[0] == 0xc0)
1542             pos2 = reply + pos2[1];
1543
1544           len = *pos2++;
1545
1546           while (len)
1547             {
1548               for (i = 0; i < len; i++)
1549                 vec_add1 (s, *pos2++);
1550
1551               /* chase pointer */
1552               if (pos2[0] == 0xC0)
1553                 pos2 = reply + pos2[1];
1554
1555               len = *pos2++;
1556               if (len)
1557                 vec_add1 (s, '.');
1558               else
1559                 vec_add1 (s, '\n');
1560             }
1561         }
1562       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1563       break;
1564
1565     default:
1566       if (verbose > 1)
1567         s = format (s, "type %d: len %d\n",
1568                     (int) clib_net_to_host_u16 (rr->type),
1569                     sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength));
1570       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1571       break;
1572     }
1573
1574   *curpos = pos;
1575
1576   return s;
1577 }
1578
1579 u8 *
1580 format_dns_reply (u8 * s, va_list * args)
1581 {
1582   u8 *reply_as_u8 = va_arg (*args, u8 *);
1583   int verbose = va_arg (*args, int);
1584   dns_header_t *h;
1585   u16 id, flags;
1586   u8 *curpos;
1587   int i;
1588   int print_ip4 = 1;
1589   int print_ip6 = 1;
1590
1591   h = (dns_header_t *) reply_as_u8;
1592   id = clib_net_to_host_u16 (h->id);
1593   flags = clib_net_to_host_u16 (h->flags);
1594
1595   if (verbose > 1)
1596     {
1597       s = format (s, "DNS %s: id %d\n", (flags & DNS_QR) ? "reply" : "query",
1598                   id);
1599       s = format (s, "  %s %s %s %s\n",
1600                   (flags & DNS_RA) ? "recur" : "no-recur",
1601                   (flags & DNS_RD) ? "recur-des" : "no-recur-des",
1602                   (flags & DNS_TC) ? "trunc" : "no-trunc",
1603                   (flags & DNS_AA) ? "auth" : "non-auth");
1604       s = format (s, "  %d queries, %d answers, %d name-servers,"
1605                   " %d add'l recs\n",
1606                   clib_net_to_host_u16 (h->qdcount),
1607                   clib_net_to_host_u16 (h->anscount),
1608                   clib_net_to_host_u16 (h->nscount),
1609                   clib_net_to_host_u16 (h->arcount));
1610     }
1611
1612   curpos = (u8 *) (h + 1);
1613
1614   if (h->qdcount)
1615     {
1616       if (verbose > 1)
1617         s = format (s, "  Queries:\n");
1618       for (i = 0; i < clib_net_to_host_u16 (h->qdcount); i++)
1619         {
1620           /* The query is variable-length, so curpos is a value-result parm */
1621           s = format (s, "%U", format_dns_query, &curpos, verbose);
1622         }
1623     }
1624   if (h->anscount)
1625     {
1626       if (verbose > 1)
1627         s = format (s, "  Replies:\n");
1628
1629       for (i = 0; i < clib_net_to_host_u16 (h->anscount); i++)
1630         {
1631           /* curpos is a value-result parm */
1632           s = format (s, "%U", format_dns_reply_data, reply_as_u8, &curpos,
1633                       verbose, &print_ip4, &print_ip6);
1634         }
1635     }
1636   return s;
1637 }
1638
1639 u8 *
1640 format_dns_cache (u8 * s, va_list * args)
1641 {
1642   dns_main_t *dm = va_arg (*args, dns_main_t *);
1643   f64 now = va_arg (*args, f64);
1644   int verbose = va_arg (*args, int);
1645   u8 *name = va_arg (*args, u8 *);
1646   dns_cache_entry_t *ep;
1647   char *ss;
1648   uword *p;
1649
1650   if (dm->is_enabled == 0)
1651     {
1652       s = format (s, "The DNS cache is disabled...");
1653       return s;
1654     }
1655
1656   if (pool_elts (dm->entries) == 0)
1657     {
1658       s = format (s, "The DNS cache is empty...");
1659       return s;
1660     }
1661
1662   dns_cache_lock (dm);
1663
1664   if (name)
1665     {
1666       p = hash_get_mem (dm->cache_entry_by_name, name);
1667       if (!p)
1668         {
1669           s = format (s, "%s is not in the cache...", name);
1670           dns_cache_unlock (dm);
1671           return (s);
1672         }
1673
1674       ep = pool_elt_at_index (dm->entries, p[0]);
1675       /* Magic to spit out a C-initializer to research hemorrhoids... */
1676       if (verbose == 3)
1677         {
1678           int i, j;
1679           s = format (s, "static u8 dns_reply_data_initializer[] =\n");
1680           s = format (s, "{\n");
1681           j = 0;
1682           for (i = 0; i < vec_len (ep->dns_response); i++)
1683             {
1684               if (j++ == 8)
1685                 {
1686                   j = 0;
1687                   vec_add1 (s, '\n');
1688                 }
1689               s = format (s, "0x%02x, ", ep->dns_response[i]);
1690             }
1691           s = format (s, "};\n");
1692         }
1693       else
1694         {
1695           if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
1696             {
1697               ASSERT (ep->dns_response);
1698               if (ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC)
1699                 ss = "[S] ";
1700               else
1701                 ss = "    ";
1702
1703               s = format (s, "%s%s -> %U", ss, ep->name,
1704                           format_dns_reply, ep->dns_response, verbose);
1705               if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
1706                 {
1707                   f64 time_left = ep->expiration_time - now;
1708                   if (time_left > 0.0)
1709                     s = format (s, "  TTL left %.1f", time_left);
1710                   else
1711                     s = format (s, "  EXPIRED");
1712                 }
1713             }
1714           else
1715             {
1716               ASSERT (ep->dns_request);
1717               s = format (s, "[P] %U", format_dns_reply, ep->dns_request,
1718                           verbose);
1719             }
1720           vec_add1 (s, '\n');
1721         }
1722       return s;
1723     }
1724
1725   /* *INDENT-OFF* */
1726   pool_foreach (ep, dm->entries,
1727   ({
1728     if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
1729       {
1730         ASSERT (ep->dns_response);
1731         if (ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC)
1732           ss = "[S] ";
1733         else
1734           ss = "    ";
1735
1736         s = format (s, "%s%s -> %U", ss, ep->name,
1737                     format_dns_reply,
1738                     ep->dns_response,
1739                     verbose);
1740         if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
1741           {
1742             f64 time_left = ep->expiration_time - now;
1743             if (time_left > 0.0)
1744               s = format (s, "  TTL left %.1f", time_left);
1745             else
1746               s = format (s, "  EXPIRED");
1747           }
1748       }
1749     else
1750       {
1751         ASSERT (ep->dns_request);
1752         s = format (s, "[P] %U", format_dns_reply, ep->dns_request,
1753                     verbose);
1754       }
1755     vec_add1 (s, '\n');
1756   }));
1757   /* *INDENT-ON* */
1758
1759   dns_cache_unlock (dm);
1760
1761   return s;
1762 }
1763
1764 static clib_error_t *
1765 show_dns_cache_command_fn (vlib_main_t * vm,
1766                            unformat_input_t * input, vlib_cli_command_t * cmd)
1767 {
1768   dns_main_t *dm = &dns_main;
1769   int verbose = 0;
1770   u8 *name = 0;
1771   f64 now = vlib_time_now (vm);
1772
1773   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1774     {
1775       if (unformat (input, "verbose %d", &verbose))
1776         ;
1777       else if (unformat (input, "verbose"))
1778         verbose = 1;
1779       else if (unformat (input, "name %s", &name))
1780         ;
1781       else
1782         return clib_error_return (0, "unknown input `%U'",
1783                                   format_unformat_error, input);
1784     }
1785
1786   vlib_cli_output (vm, "%U", format_dns_cache, dm, now, verbose, name);
1787
1788   return 0;
1789 }
1790
1791 /* *INDENT-OFF* */
1792 VLIB_CLI_COMMAND (show_dns_cache_command) =
1793 {
1794   .path = "show dns cache",
1795   .short_help = "show dns cache [verbose [nn]]",
1796   .function = show_dns_cache_command_fn,
1797 };
1798 /* *INDENT-ON* */
1799
1800 static clib_error_t *
1801 dns_cache_add_del_command_fn (vlib_main_t * vm,
1802                               unformat_input_t * input,
1803                               vlib_cli_command_t * cmd)
1804 {
1805   dns_main_t *dm = &dns_main;
1806   u8 *dns_reply_data;
1807   u8 *name;
1808   int is_add = -1;
1809   int is_clear = -1;
1810   int rv;
1811   clib_error_t *error;
1812
1813   if (unformat (input, "add"))
1814     is_add = 1;
1815   if (unformat (input, "del"))
1816     is_add = 0;
1817   if (unformat (input, "clear"))
1818     is_clear = 1;
1819
1820   if (is_add == -1 && is_clear == -1)
1821     return clib_error_return (0, "add / del / clear required...");
1822
1823   if (is_clear == 1)
1824     {
1825       rv = dns_cache_clear (dm);
1826       switch (rv)
1827         {
1828         case 0:
1829           return 0;
1830
1831         case VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED:
1832           error = clib_error_return (0, "Name resolution not enabled");
1833           return error;
1834         }
1835     }
1836
1837   /* Delete (by name)? */
1838   if (is_add == 0)
1839     {
1840       if (unformat (input, "%v", &name))
1841         {
1842           rv = dns_delete_by_name (dm, name);
1843           switch (rv)
1844             {
1845             case VNET_API_ERROR_NO_SUCH_ENTRY:
1846               error = clib_error_return (0, "%v not in the cache...", name);
1847               vec_free (name);
1848               return error;
1849
1850             case VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED:
1851               error = clib_error_return (0, "Name resolution not enabled");
1852               vec_free (name);
1853               return error;
1854
1855             case 0:
1856               vec_free (name);
1857               return 0;
1858
1859             default:
1860               error = clib_error_return (0, "dns_delete_by_name returned %d",
1861                                          rv);
1862               vec_free (name);
1863               return error;
1864             }
1865         }
1866       return clib_error_return (0, "unknown input `%U'",
1867                                 format_unformat_error, input);
1868     }
1869
1870   /* Note: dns_add_static_entry consumes the name vector if OK... */
1871   if (unformat (input, "%U", unformat_dns_reply, &dns_reply_data, &name))
1872     {
1873       rv = dns_add_static_entry (dm, name, dns_reply_data);
1874       switch (rv)
1875         {
1876         case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
1877           vec_free (name);
1878           vec_free (dns_reply_data);
1879           return clib_error_return (0, "%v already in the cache...", name);
1880         case 0:
1881           return 0;
1882
1883         default:
1884           return clib_error_return (0, "dns_add_static_entry returned %d",
1885                                     rv);
1886         }
1887     }
1888
1889   return 0;
1890 }
1891
1892 /* *INDENT-OFF* */
1893 VLIB_CLI_COMMAND (dns_cache_add_del_command) =
1894 {
1895   .path = "dns cache",
1896   .short_help = "dns cache [add|del|clear] <name> [ip4][ip6]",
1897   .function = dns_cache_add_del_command_fn,
1898 };
1899 /* *INDENT-ON* */
1900
1901 #define DNS_FORMAT_TEST 1
1902
1903 #if DNS_FORMAT_TEST > 0
1904 #if 0
1905 /* yahoo.com */
1906 static u8 dns_reply_data_initializer[] =
1907   { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x5,
1908   0x79, 0x61, 0x68, 0x6f, 0x6f, 0x3, 0x63, 0x6f, 0x6d,
1909   0x0,                          /* null lbl */
1910   0x0, 0xff,                    /* type ALL */
1911   0x0, 0x1,                     /* class IN */
1912   0xc0, 0xc,                    /* pointer to yahoo.com name */
1913   0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x24, 0x23,
1914   0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x72, 0x65, 0x64, 0x69, 0x72,
1915   0x65, 0x63, 0x74, 0x3d, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x6d, 0x61, 0x69,
1916   0x6c, 0x2e, 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0xc0,
1917   0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73,
1918   0x35, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0,
1919   0x6, 0x3, 0x6e, 0x73, 0x34, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0,
1920   0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x31, 0xc0, 0xc, 0xc0, 0xc,
1921   0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x32,
1922   0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6,
1923   0x3, 0x6e, 0x73, 0x33, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0,
1924   0x6, 0x5c, 0x0, 0x19, 0x0, 0x1, 0x4, 0x6d, 0x74, 0x61, 0x36, 0x3, 0x61,
1925   0x6d, 0x30, 0x8, 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x64, 0x6e, 0x73, 0x3,
1926   0x6e,
1927   0x65, 0x74, 0x0, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0,
1928   0x9, 0x0, 0x1, 0x4, 0x6d, 0x74, 0x61, 0x37, 0xc0, 0xb8, 0xc0, 0xc, 0x0,
1929   0xf, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x9, 0x0, 0x1, 0x4, 0x6d, 0x74,
1930   0x61, 0x35, 0xc0, 0xb8, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
1931   0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0x44, 0x2, 0x4, 0x0, 0x0,
1932   0x0,
1933   0x0, 0x0, 0x0, 0x0, 0xa7, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
1934   0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0xc, 0xa, 0x6, 0x0, 0x0, 0x0,
1935   0x0, 0x0, 0x2, 0x40, 0x8, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
1936   0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0x58, 0xc, 0x2, 0x0, 0x0,
1937   0x0,
1938   0x0, 0x0, 0x0, 0x0, 0xa9, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x6,
1939   0x5c, 0x0, 0x4, 0x62, 0x8a, 0xfd, 0x6d, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1,
1940   0x0,
1941   0x0, 0x6, 0x5c, 0x0, 0x4, 0xce, 0xbe, 0x24, 0x2d, 0xc0, 0xc, 0x0, 0x1,
1942   0x0,
1943   0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x4, 0x62, 0x8b, 0xb4, 0x95, 0xc0, 0xc,
1944   0x0,
1945   0x6, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x2d, 0xc0, 0x7b, 0xa, 0x68,
1946   0x6f,
1947   0x73, 0x74, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x9, 0x79, 0x61, 0x68,
1948   0x6f, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0xc0, 0x12, 0x78, 0x3a, 0x85, 0x44,
1949   0x0, 0x0, 0xe, 0x10, 0x0, 0x0, 0x1, 0x2c, 0x0, 0x1b, 0xaf, 0x80, 0x0, 0x0,
1950   0x2, 0x58
1951 };
1952
1953 /* www.cisco.com, has no addresses in reply */
1954 static u8 dns_reply_data_initializer[] = {
1955   0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
1956   0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x05,
1957   0x63, 0x69, 0x73, 0x63, 0x6f, 0x03, 0x63, 0x6f, 0x6d,
1958
1959   0x00, 0x00, 0xff, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05,
1960   0x00, 0x01, 0x00, 0x00, 0x0b, 0xd3, 0x00, 0x1a, 0x03,
1961   0x77, 0x77, 0x77, 0x05, 0x63, 0x69, 0x73, 0x63, 0x6f,
1962   0x03, 0x63, 0x6f, 0x6d, 0x06, 0x61, 0x6b, 0x61, 0x64,
1963   0x6e, 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00,
1964 };
1965 #else
1966 /* google.com */
1967 static u8 dns_reply_data_initializer[] =
1968   { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x6,
1969   0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x3, 0x63, 0x6f, 0x6d, 0x0, 0x0, 0xff,
1970   0x0, 0x1, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x2b, 0x0, 0x4,
1971   0xac, 0xd9, 0x3, 0x2e, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x1,
1972   0x2b,
1973   0x0, 0x10, 0x26, 0x7, 0xf8, 0xb0, 0x40, 0x4, 0x8, 0xf, 0x0, 0x0, 0x0, 0x0,
1974   0x0, 0x0, 0x20, 0xe, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x51, 0x7f,
1975   0x0, 0x6, 0x3, 0x6e, 0x73, 0x31, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x6, 0x0, 0x1,
1976   0x0, 0x0, 0x0, 0x3b, 0x0, 0x22, 0xc0, 0x54, 0x9, 0x64, 0x6e, 0x73, 0x2d,
1977   0x61, 0x64, 0x6d, 0x69, 0x6e, 0xc0, 0xc, 0xa, 0x3d, 0xc7, 0x30, 0x0, 0x0,
1978   0x3, 0x84, 0x0, 0x0, 0x3, 0x84, 0x0, 0x0, 0x7, 0x8, 0x0, 0x0, 0x0, 0x3c,
1979   0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x11, 0x0, 0x1e,
1980   0x4, 0x61, 0x6c, 0x74, 0x32, 0x5, 0x61, 0x73, 0x70, 0x6d, 0x78, 0x1, 0x6c,
1981   0xc0, 0xc, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x4,
1982   0x0, 0xa, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0xe, 0xf,
1983   0x0, 0x24, 0x23, 0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x69, 0x6e,
1984   0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x67,
1985   0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x7e, 0x61,
1986   0x6c, 0x6c, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x51, 0x7f, 0x0, 0x6,
1987   0x3, 0x6e, 0x73, 0x32, 0xc0, 0xc, 0xc0, 0xc, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1,
1988   0x51, 0x7f, 0x0, 0xf, 0x0, 0x5, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, 0x6b,
1989   0x69, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0,
1990   0x1, 0x51, 0x7f, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x34, 0xc0, 0xc, 0xc0, 0xc,
1991   0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x9, 0x0, 0x28, 0x4, 0x61,
1992   0x6c, 0x74, 0x33, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1,
1993   0x51, 0x7f, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x33, 0xc0, 0xc, 0xc0, 0xc, 0x0,
1994   0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x9, 0x0, 0x32, 0x4, 0x61, 0x6c,
1995   0x74, 0x34, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2,
1996   0x57,
1997   0x0, 0x9, 0x0, 0x14, 0x4, 0x61, 0x6c, 0x74, 0x31, 0xc0, 0x9b
1998 };
1999 #endif
2000
2001 static clib_error_t *
2002 test_dns_fmt_command_fn (vlib_main_t * vm,
2003                          unformat_input_t * input, vlib_cli_command_t * cmd)
2004 {
2005   u8 *dns_reply_data = 0;
2006   int verbose = 0;
2007   int rv;
2008   vl_api_dns_resolve_name_reply_t _rm, *rmp = &_rm;
2009
2010   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2011     {
2012       if (unformat (input, "verbose %d", &verbose))
2013         ;
2014       else if (unformat (input, "verbose"))
2015         verbose = 1;
2016       else
2017         return clib_error_return (0, "unknown input `%U'",
2018                                   format_unformat_error, input);
2019     }
2020
2021   vec_validate (dns_reply_data, ARRAY_LEN (dns_reply_data_initializer) - 1);
2022
2023   memcpy (dns_reply_data, dns_reply_data_initializer,
2024           ARRAY_LEN (dns_reply_data_initializer));
2025
2026   vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
2027
2028   memset (rmp, 0, sizeof (*rmp));
2029
2030   rv = vnet_dns_response_to_reply (dns_reply_data, rmp, 0 /* ttl-ptr */ );
2031
2032   switch (rv)
2033     {
2034     case VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES:
2035       vlib_cli_output (vm, "no addresses found...");
2036       break;
2037
2038     default:
2039       vlib_cli_output (vm, "response to reply returned %d", rv);
2040       break;
2041
2042     case 0:
2043       if (rmp->ip4_set)
2044         vlib_cli_output (vm, "ip4 address: %U", format_ip4_address,
2045                          (ip4_address_t *) rmp->ip4_address);
2046       if (rmp->ip6_set)
2047         vlib_cli_output (vm, "ip6 address: %U", format_ip6_address,
2048                          (ip6_address_t *) rmp->ip6_address);
2049       break;
2050     }
2051
2052   vec_free (dns_reply_data);
2053
2054   return 0;
2055 }
2056
2057
2058 /* *INDENT-OFF* */
2059 VLIB_CLI_COMMAND (test_dns_fmt_command) =
2060 {
2061   .path = "test dns format",
2062   .short_help = "test dns format",
2063   .function = test_dns_fmt_command_fn,
2064 };
2065 /* *INDENT-ON* */
2066
2067 static clib_error_t *
2068 test_dns_unfmt_command_fn (vlib_main_t * vm,
2069                            unformat_input_t * input, vlib_cli_command_t * cmd)
2070 {
2071   u8 *dns_reply_data = 0;
2072   int verbose = 0;
2073   int reply_set = 0;
2074
2075   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2076     {
2077       if (unformat (input, "verbose %d", &verbose))
2078         ;
2079       else if (unformat (input, "verbose"))
2080         verbose = 1;
2081       else if (unformat (input, "%U", unformat_dns_reply, &dns_reply_data))
2082         reply_set = 1;
2083       else
2084         return clib_error_return (0, "unknown input `%U'",
2085                                   format_unformat_error, input);
2086     }
2087
2088   if (reply_set == 0)
2089     return clib_error_return (0, "dns data not set...");
2090
2091   vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
2092
2093   vec_free (dns_reply_data);
2094
2095   return 0;
2096 }
2097
2098 /* *INDENT-OFF* */
2099 VLIB_CLI_COMMAND (test_dns_unfmt_command) =
2100 {
2101   .path = "test dns unformat",
2102   .short_help = "test dns unformat <name> [ip4][ip6]",
2103   .function = test_dns_unfmt_command_fn,
2104 };
2105 /* *INDENT-ON* */
2106 #endif
2107
2108 /*
2109  * fd.io coding-style-patch-verification: ON
2110  *
2111  * Local Variables:
2112  * eval: (c-set-style "gnu")
2113  * End:
2114  */