DNS name resolver improvements
[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   /* This can easily happen if sitting in GDB, etc. */
527   if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
528     return;
529
530   /* Construct the dns request, if we haven't been here already */
531   if (vec_len (ep->dns_request) == 0)
532     {
533       /*
534        * Start with the variadic portion of the exercise.
535        * Turn the name into a set of DNS "labels". Max length
536        * per label is 63, enforce that.
537        */
538       request = name_to_labels (ep->name);
539       qp_offset = vec_len (request);
540
541       /* Add space for the query header */
542       vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
543
544       qp = (dns_query_t *) (request + qp_offset);
545
546       qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
547       qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
548
549       /* Punch in space for the dns_header_t */
550       vec_insert (request, sizeof (dns_header_t), 0);
551
552       h = (dns_header_t *) request;
553
554       /* Transaction ID = pool index */
555       h->id = clib_host_to_net_u16 (ep - dm->entries);
556
557       /* Ask for a recursive lookup */
558       tmp = DNS_RD | DNS_OPCODE_QUERY;
559       h->flags = clib_host_to_net_u16 (tmp);
560       h->qdcount = clib_host_to_net_u16 (1);
561       h->nscount = 0;
562       h->arcount = 0;
563
564       ep->dns_request = request;
565     }
566
567   /* Work out which server / address family we're going to use */
568
569   /* Retry using current server */
570   if (ep->retry_count++ < DNS_RETRIES_PER_SERVER)
571     {
572       if (ep->server_af == 1 /* ip6 */ )
573         {
574           if (vec_len (dm->ip6_name_servers))
575             {
576               send_dns6_request (dm, ep,
577                                  dm->ip6_name_servers + ep->server_rotor);
578               goto out;
579             }
580           else
581             ep->server_af = 0;
582         }
583       if (vec_len (dm->ip4_name_servers))
584         {
585           send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
586           goto out;
587         }
588     }
589   else                          /* switch to a new server */
590     {
591       ep->retry_count = 1;
592       ep->server_rotor++;
593       if (ep->server_af == 1 /* ip6 */ )
594         {
595           if (ep->server_rotor >= vec_len (dm->ip6_name_servers))
596             {
597               ep->server_rotor = 0;
598               ep->server_af = vec_len (dm->ip4_name_servers) > 0 ? 0 : 1;
599             }
600         }
601       else
602         {
603           if (ep->server_rotor >= vec_len (dm->ip4_name_servers))
604             {
605               ep->server_rotor = 0;
606               ep->server_af = vec_len (dm->ip6_name_servers) > 0 ? 1 : 0;
607             }
608         }
609     }
610
611   if (ep->server_af == 1 /* ip6 */ )
612     send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor);
613   else
614     send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
615
616 out:
617
618   vlib_process_signal_event_mt (dm->vlib_main, dns_resolver_node.index,
619                                 DNS_RESOLVER_EVENT_PENDING, 0);
620 }
621
622 int
623 vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index)
624 {
625   dns_cache_entry_t *ep;
626   int i;
627
628   if (dm->is_enabled == 0)
629     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
630
631   if (pool_is_free_index (dm->entries, index))
632     return VNET_API_ERROR_NO_SUCH_ENTRY;
633
634   ep = pool_elt_at_index (dm->entries, index);
635   if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_VALID))
636     {
637       for (i = 0; i < vec_len (dm->unresolved_entries); i++)
638         if (index == dm->unresolved_entries[i])
639           {
640             vec_delete (dm->unresolved_entries, 1, i);
641             goto found;
642           }
643       clib_warning ("pool elt %d supposedly pending, but not found...",
644                     index);
645     }
646
647 found:
648   hash_unset_mem (dm->cache_entry_by_name, ep->name);
649   vec_free (ep->name);
650   vec_free (ep->api_clients_to_notify);
651   vec_free (ep->api_client_contexts);
652   vec_free (ep->ip4_peers_to_notify);
653   vec_free (ep->ip6_peers_to_notify);
654   pool_put (dm->entries, ep);
655
656   return 0;
657 }
658
659 static int
660 dns_delete_by_name (dns_main_t * dm, u8 * name)
661 {
662   int rv;
663   uword *p;
664
665   if (dm->is_enabled == 0)
666     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
667
668   dns_cache_lock (dm);
669   p = hash_get_mem (dm->cache_entry_by_name, name);
670   if (!p)
671     {
672       dns_cache_unlock (dm);
673       return VNET_API_ERROR_NO_SUCH_ENTRY;
674     }
675   rv = vnet_dns_delete_entry_by_index_nolock (dm, p[0]);
676
677   dns_cache_unlock (dm);
678
679   return rv;
680 }
681
682 static int
683 delete_random_entry (dns_main_t * dm)
684 {
685   int rv;
686   u32 victim_index, start_index, i;
687   u32 limit;
688   dns_cache_entry_t *ep;
689
690   if (dm->is_enabled == 0)
691     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
692
693   dns_cache_lock (dm);
694   limit = pool_elts (dm->entries);
695   start_index = random_u32 (&dm->random_seed) % limit;
696
697   for (i = 0; i < limit; i++)
698     {
699       victim_index = (start_index + i) % limit;
700
701       if (!pool_is_free_index (dm->entries, victim_index))
702         {
703           ep = pool_elt_at_index (dm->entries, victim_index);
704           /* Delete only valid, non-static entries */
705           if ((ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
706               && ((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0))
707             {
708               rv = vnet_dns_delete_entry_by_index_nolock (dm, victim_index);
709               dns_cache_unlock (dm);
710               return rv;
711             }
712         }
713     }
714   dns_cache_unlock (dm);
715
716   clib_warning ("Couldn't find an entry to delete?");
717   return VNET_API_ERROR_UNSPECIFIED;
718 }
719
720 static int
721 dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data)
722 {
723   dns_cache_entry_t *ep;
724   uword *p;
725   int rv;
726
727   if (dm->is_enabled == 0)
728     return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
729
730   dns_cache_lock (dm);
731   p = hash_get_mem (dm->cache_entry_by_name, name);
732   if (p)
733     {
734       dns_cache_unlock (dm);
735       return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
736     }
737
738   if (pool_elts (dm->entries) == dm->name_cache_size)
739     {
740       /* Will only fail if the cache is totally filled w/ static entries... */
741       rv = delete_random_entry (dm);
742       if (rv)
743         {
744           dns_cache_unlock (dm);
745           return rv;
746         }
747     }
748
749   pool_get (dm->entries, ep);
750   memset (ep, 0, sizeof (*ep));
751
752   /* Note: consumes the name vector */
753   ep->name = name;
754   hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
755   ep->flags = DNS_CACHE_ENTRY_FLAG_VALID | DNS_CACHE_ENTRY_FLAG_STATIC;
756   ep->dns_response = dns_reply_data;
757
758   dns_cache_unlock (dm);
759   return 0;
760 }
761
762 static int
763 dns_resolve_name (dns_main_t * dm,
764                   u8 * name, u32 client_index, u32 client_context,
765                   dns_cache_entry_t ** retp)
766 {
767   dns_cache_entry_t *ep;
768   int rv;
769   f64 now;
770   uword *p;
771
772   now = vlib_time_now (dm->vlib_main);
773
774   /* In case we can't actually answer the question right now... */
775   *retp = 0;
776
777   dns_cache_lock (dm);
778 search_again:
779   p = hash_get_mem (dm->cache_entry_by_name, name);
780   if (p)
781     {
782       ep = pool_elt_at_index (dm->entries, p[0]);
783       if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
784         {
785           /* Has the entry expired? */
786           if (((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0)
787               && (now > ep->expiration_time))
788             {
789               int i;
790               u32 *indices_to_delete = 0;
791
792               /*
793                * Take out the rest of the resolution chain
794                * This isn't optimal, but it won't happen very often.
795                */
796               while (ep)
797                 {
798                   if ((ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME))
799                     {
800                       vec_add1 (indices_to_delete, ep - dm->entries);
801
802                       p = hash_get_mem (dm->cache_entry_by_name, ep->cname);
803                       if (!p)
804                         break;
805                       ep = pool_elt_at_index (dm->entries, p[0]);
806                     }
807                   else
808                     {
809                       vec_add1 (indices_to_delete, ep - dm->entries);
810                       break;
811                     }
812                 }
813               for (i = 0; i < vec_len (indices_to_delete); i++)
814                 {
815                   /* Reenable to watch re-resolutions */
816                   if (0)
817                     {
818                       ep = pool_elt_at_index (dm->entries,
819                                               indices_to_delete[i]);
820                       clib_warning ("Re-resolve %s", ep->name);
821                     }
822
823                   vnet_dns_delete_entry_by_index_nolock
824                     (dm, indices_to_delete[i]);
825                 }
826               vec_free (indices_to_delete);
827               /* Yes, kill it... */
828               goto re_resolve;
829             }
830
831           if (ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
832             {
833               name = ep->cname;
834               goto search_again;
835             }
836
837           /* Note: caller must drop the lock! */
838           *retp = ep;
839           return (0);
840         }
841       else
842         {
843           /*
844            * Resolution pending. Add request to the pending vector(s) */
845           vec_add1 (ep->api_clients_to_notify, client_index);
846           vec_add1 (ep->api_client_contexts, client_context);
847           dns_cache_unlock (dm);
848           return (0);
849         }
850     }
851
852 re_resolve:
853   if (pool_elts (dm->entries) == dm->name_cache_size)
854     {
855       /* Will only fail if the cache is totally filled w/ static entries... */
856       rv = delete_random_entry (dm);
857       if (rv)
858         {
859           dns_cache_unlock (dm);
860           return rv;
861         }
862     }
863
864   /* add new hash table entry */
865   pool_get (dm->entries, ep);
866   memset (ep, 0, sizeof (*ep));
867
868   ep->name = format (0, "%s%c", name, 0);
869   _vec_len (ep->name) = vec_len (ep->name) - 1;
870
871   hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
872
873   vec_add1 (dm->unresolved_entries, ep - dm->entries);
874   vec_add1 (ep->api_clients_to_notify, client_index);
875   vec_add1 (ep->api_client_contexts, client_context);
876   vnet_send_dns_request (dm, ep);
877   dns_cache_unlock (dm);
878   return 0;
879 }
880
881 #define foreach_notification_to_move            \
882 _(api_clients_to_notify)                        \
883 _(api_client_contexts)                          \
884 _(ip4_peers_to_notify)                          \
885 _(ip6_peers_to_notify)
886
887 /**
888  * Handle cname indirection. JFC. Called with the cache locked.
889  * returns 0 if the reply is not a CNAME.
890  */
891
892 int
893 vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply)
894 {
895   dns_header_t *h;
896   dns_query_t *qp;
897   dns_rr_t *rr;
898   u8 *curpos;
899   u8 *pos, *pos2;
900   int len, i;
901   u8 *cname = 0;
902   u8 *request = 0;
903   u32 qp_offset;
904   u16 flags;
905   u16 rcode;
906   dns_cache_entry_t *ep, *next_ep;
907   f64 now;
908
909   h = (dns_header_t *) reply;
910   flags = clib_net_to_host_u16 (h->flags);
911   rcode = flags & DNS_RCODE_MASK;
912
913   /* See if the response is OK */
914   switch (rcode)
915     {
916     case DNS_RCODE_NO_ERROR:
917       break;
918
919     case DNS_RCODE_NAME_ERROR:
920     case DNS_RCODE_FORMAT_ERROR:
921     case DNS_RCODE_SERVER_FAILURE:
922     case DNS_RCODE_NOT_IMPLEMENTED:
923     case DNS_RCODE_REFUSED:
924       return 0;
925     }
926
927   curpos = (u8 *) (h + 1);
928   pos = curpos;
929   len = *pos++;
930
931   /* Skip the questions */
932   for (i = 0; i < clib_net_to_host_u16 (h->qdcount); i++)
933     {
934       while (len)
935         {
936           pos += len;
937           len = *pos++;
938         }
939       qp = (dns_query_t *) pos;
940       pos += sizeof (*qp);
941     }
942   pos2 = pos;
943   /* expect a pointer chase here for a CNAME record */
944   if ((pos2[0] & 0xC0) == 0xC0)
945     pos += 2;
946   else
947     return 0;
948
949   rr = (dns_rr_t *) pos;
950
951   /* This is a real record, not a CNAME record */
952   if (clib_net_to_host_u16 (rr->type) != DNS_TYPE_CNAME)
953     return 0;
954
955   /* This is a CNAME record, chase the name chain. */
956
957   /* The last request is no longer pending.. */
958   for (i = 0; i < vec_len (dm->unresolved_entries); i++)
959     if (ep_index == dm->unresolved_entries[i])
960       {
961         vec_delete (dm->unresolved_entries, 1, i);
962         goto found_last_request;
963       }
964   clib_warning ("pool elt %d supposedly pending, but not found...", ep_index);
965
966 found_last_request:
967
968   now = vlib_time_now (dm->vlib_main);
969   cname = labels_to_name (rr->rdata, reply, &pos2);
970   /* Save the cname */
971   vec_add1 (cname, 0);
972   _vec_len (cname) -= 1;
973   ep = pool_elt_at_index (dm->entries, ep_index);
974   ep->cname = cname;
975   ep->flags |= (DNS_CACHE_ENTRY_FLAG_CNAME | DNS_CACHE_ENTRY_FLAG_VALID);
976   /* Save the response */
977   ep->dns_response = reply;
978   /* Set up expiration time */
979   ep->expiration_time = now + clib_net_to_host_u32 (rr->ttl);
980
981   pool_get (dm->entries, next_ep);
982
983   /* Need to recompute ep post pool-get */
984   ep = pool_elt_at_index (dm->entries, ep_index);
985
986   memset (next_ep, 0, sizeof (*next_ep));
987   next_ep->name = vec_dup (cname);
988   vec_add1 (next_ep->name, 0);
989   _vec_len (next_ep->name) -= 1;
990
991   hash_set_mem (dm->cache_entry_by_name, next_ep->name,
992                 next_ep - dm->entries);
993
994   /* Use the same server */
995   next_ep->server_rotor = ep->server_rotor;
996   next_ep->server_af = ep->server_af;
997
998   /* Move notification data to the next name in the chain */
999 #define _(a) next_ep->a = ep->a; ep->a = 0;
1000   foreach_notification_to_move;
1001 #undef _
1002
1003   request = name_to_labels (cname);
1004
1005   qp_offset = vec_len (request);
1006
1007   /* Add space for the query header */
1008   vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
1009
1010   qp = (dns_query_t *) (request + qp_offset);
1011
1012   qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
1013   qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1014
1015   /* Punch in space for the dns_header_t */
1016   vec_insert (request, sizeof (dns_header_t), 0);
1017
1018   h = (dns_header_t *) request;
1019
1020   /* Transaction ID = pool index */
1021   h->id = clib_host_to_net_u16 (next_ep - dm->entries);
1022
1023   /* Ask for a recursive lookup */
1024   h->flags = clib_host_to_net_u16 (DNS_RD | DNS_OPCODE_QUERY);
1025   h->qdcount = clib_host_to_net_u16 (1);
1026   h->nscount = 0;
1027   h->arcount = 0;
1028
1029   next_ep->dns_request = request;
1030   next_ep->retry_timer = now + 2.0;
1031   next_ep->retry_count = 0;
1032
1033   /*
1034    * Enable this to watch recursive resolution happen...
1035    * fformat (stdout, "%U", format_dns_reply, request, 2);
1036    */
1037
1038   vec_add1 (dm->unresolved_entries, next_ep - dm->entries);
1039   vnet_send_dns_request (dm, next_ep);
1040   return (1);
1041 }
1042
1043 int
1044 vnet_dns_response_to_reply (u8 * response,
1045                             vl_api_dns_resolve_name_reply_t * rmp,
1046                             u32 * min_ttlp)
1047 {
1048   dns_header_t *h;
1049   dns_query_t *qp;
1050   dns_rr_t *rr;
1051   int i, limit;
1052   u8 len;
1053   u8 *curpos, *pos;
1054   u16 flags;
1055   u16 rcode;
1056   u32 ttl;
1057
1058   h = (dns_header_t *) response;
1059   flags = clib_net_to_host_u16 (h->flags);
1060   rcode = flags & DNS_RCODE_MASK;
1061
1062   /* See if the response is OK, etc. */
1063   switch (rcode)
1064     {
1065     default:
1066     case DNS_RCODE_NO_ERROR:
1067       break;
1068
1069     case DNS_RCODE_NAME_ERROR:
1070     case DNS_RCODE_FORMAT_ERROR:
1071       return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME;
1072
1073     case DNS_RCODE_SERVER_FAILURE:
1074     case DNS_RCODE_NOT_IMPLEMENTED:
1075     case DNS_RCODE_REFUSED:
1076       return VNET_API_ERROR_NAME_SERVER_NEXT_SERVER;
1077     }
1078
1079   /* No answers? Loser... */
1080   if (clib_net_to_host_u16 (h->anscount) < 1)
1081     return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
1082
1083   curpos = (u8 *) (h + 1);
1084
1085   /* Skip the name we asked about */
1086   pos = curpos;
1087   len = *pos++;
1088   /* Should never happen, but stil... */
1089   if ((len & 0xC0) == 0xC0)
1090     curpos += 2;
1091   else
1092     {
1093       /* skip the name / label-set */
1094       while (len)
1095         {
1096           pos += len;
1097           len = *pos++;
1098         }
1099       curpos = pos;
1100     }
1101   /* Skip queries */
1102   limit = clib_net_to_host_u16 (h->qdcount);
1103   qp = (dns_query_t *) curpos;
1104   qp += limit;
1105   curpos = (u8 *) qp;
1106
1107   /* Parse answers */
1108   limit = clib_net_to_host_u16 (h->anscount);
1109
1110   for (i = 0; i < limit; i++)
1111     {
1112       pos = curpos;
1113
1114       /* Expect pointer chases in the answer section... */
1115       if ((pos[0] & 0xC0) == 0xC0)
1116         curpos += 2;
1117       else
1118         {
1119           len = *pos++;
1120           while (len)
1121             {
1122               if ((pos[0] & 0xC0) == 0xC0)
1123                 {
1124                   curpos = pos + 2;
1125                   break;
1126                 }
1127               pos += len;
1128               len = *pos++;
1129             }
1130           curpos = pos;
1131         }
1132
1133       rr = (dns_rr_t *) curpos;
1134
1135       switch (clib_net_to_host_u16 (rr->type))
1136         {
1137         case DNS_TYPE_A:
1138           /* Collect an ip4 address. Do not pass go. Do not collect $200 */
1139           memcpy (rmp->ip4_address, rr->rdata, sizeof (ip4_address_t));
1140           rmp->ip4_set = 1;
1141           ttl = clib_net_to_host_u32 (rr->ttl);
1142           if (min_ttlp && *min_ttlp > ttl)
1143             *min_ttlp = ttl;
1144           break;
1145         case DNS_TYPE_AAAA:
1146           /* Collect an ip6 address. Do not pass go. Do not collect $200 */
1147           memcpy (rmp->ip6_address, rr->rdata, sizeof (ip6_address_t));
1148           ttl = clib_net_to_host_u32 (rr->ttl);
1149           if (min_ttlp && *min_ttlp > ttl)
1150             *min_ttlp = ttl;
1151           rmp->ip6_set = 1;
1152           break;
1153         default:
1154           break;
1155         }
1156       /* Might as well stop ASAP */
1157       if (rmp->ip4_set && rmp->ip6_set)
1158         break;
1159       curpos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1160     }
1161
1162   if ((rmp->ip4_set + rmp->ip6_set) == 0)
1163     return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
1164   return 0;
1165 }
1166
1167 static void
1168 vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp)
1169 {
1170   dns_main_t *dm = &dns_main;
1171   vl_api_dns_resolve_name_reply_t *rmp;
1172   dns_cache_entry_t *ep;
1173   int rv;
1174
1175   /* Sanitize the name slightly */
1176   mp->name[ARRAY_LEN (mp->name) - 1] = 0;
1177
1178   rv = dns_resolve_name (dm, mp->name, mp->client_index, mp->context, &ep);
1179
1180   /* Error, e.g. not enabled? Tell the user */
1181   if (rv < 0)
1182     {
1183       REPLY_MACRO (VL_API_DNS_RESOLVE_NAME_REPLY);
1184       return;
1185     }
1186
1187   /* Resolution pending? Don't reply... */
1188   if (ep == 0)
1189     return;
1190
1191   /* *INDENT-OFF* */
1192   REPLY_MACRO2(VL_API_DNS_RESOLVE_NAME_REPLY,
1193   ({
1194     rv = vnet_dns_response_to_reply (ep->dns_response, rmp, 0 /* ttl-ptr */);
1195     rmp->retval = clib_host_to_net_u32 (rv);
1196   }));
1197   /* *INDENT-ON* */
1198
1199   /*
1200    * dns_resolve_name leaves the cache locked when it returns
1201    * a cached result, so unlock it here.
1202    */
1203   dns_cache_unlock (dm);
1204 }
1205
1206 #define vl_msg_name_crc_list
1207 #include <vpp/api/vpe_all_api_h.h>
1208 #undef vl_msg_name_crc_list
1209
1210 static void
1211 setup_message_id_table (api_main_t * am)
1212 {
1213 #define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
1214   foreach_vl_msg_name_crc_dns;
1215 #undef _
1216 }
1217
1218 #define foreach_dns_api_msg                             \
1219 _(DNS_ENABLE_DISABLE, dns_enable_disable)               \
1220 _(DNS_NAME_SERVER_ADD_DEL, dns_name_server_add_del)     \
1221 _(DNS_RESOLVE_NAME, dns_resolve_name)
1222
1223 static clib_error_t *
1224 dns_api_hookup (vlib_main_t * vm)
1225 {
1226 #define _(N,n)                                                  \
1227     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1228                            vl_api_##n##_t_handler,              \
1229                            vl_noop_handler,                     \
1230                            vl_api_##n##_t_endian,               \
1231                            vl_api_##n##_t_print,                \
1232                            sizeof(vl_api_##n##_t), 1);
1233   foreach_dns_api_msg;
1234 #undef _
1235
1236   setup_message_id_table (&api_main);
1237   return 0;
1238 }
1239
1240 VLIB_API_INIT_FUNCTION (dns_api_hookup);
1241
1242
1243 static clib_error_t *
1244 dns_config_fn (vlib_main_t * vm, unformat_input_t * input)
1245 {
1246   dns_main_t *dm = &dns_main;
1247
1248   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1249     {
1250       if (unformat (input, "max-cache-size %u", &dm->name_cache_size))
1251         ;
1252       else if (unformat (input, "max-ttl %u", &dm->max_ttl_in_seconds))
1253         ;
1254       else
1255         return clib_error_return (0, "unknown input `%U'",
1256                                   format_unformat_error, input);
1257     }
1258   return 0;
1259 }
1260
1261 VLIB_CONFIG_FUNCTION (dns_config_fn, "dns");
1262
1263 static clib_error_t *
1264 dns_init (vlib_main_t * vm)
1265 {
1266   dns_main_t *dm = &dns_main;
1267
1268   dm->vlib_main = vm;
1269   dm->vnet_main = vnet_get_main ();
1270   dm->name_cache_size = 65535;
1271   dm->max_ttl_in_seconds = 86400;
1272   dm->random_seed = 0xDEADDABE;
1273
1274   udp_register_dst_port (vm, UDP_DST_PORT_dns_reply, dns46_reply_node.index,
1275                          1 /* is_ip4 */ );
1276
1277   udp_register_dst_port (vm, UDP_DST_PORT_dns_reply6, dns46_reply_node.index,
1278                          0 /* is_ip4 */ );
1279
1280 #if 0
1281   udp_register_dst_port (vm, UDP_DST_PORT_dns, dns4_request_node.index,
1282                          1 /* is_ip4 */ );
1283   udp_register_dst_port (vm, UDP_DST_PORT_dns6, dns6_request_node.index,
1284                          0 /* is_ip4 */ );
1285 #endif
1286
1287   return 0;
1288 }
1289
1290 VLIB_INIT_FUNCTION (dns_init);
1291
1292 uword
1293 unformat_dns_reply (unformat_input_t * input, va_list * args)
1294 {
1295   u8 **result = va_arg (*args, u8 **);
1296   u8 **namep = va_arg (*args, u8 **);
1297   ip4_address_t a4;
1298   ip6_address_t a6;
1299   int a4_set = 0;
1300   int a6_set = 0;
1301   u8 *name;
1302   int name_set = 0;
1303   u8 *ce;
1304   u32 qp_offset;
1305   dns_header_t *h;
1306   dns_query_t *qp;
1307   dns_rr_t *rr;
1308   u8 *rru8;
1309
1310   if (unformat (input, "%v", &name))
1311     name_set = 1;
1312
1313   if (unformat (input, "%U", unformat_ip4_address, &a4))
1314     {
1315       a4_set = 1;
1316       if (unformat (input, "%U", unformat_ip6_address, &a6))
1317         a6_set = 1;
1318     }
1319
1320   if (unformat (input, "%U", unformat_ip6_address, &a6))
1321     {
1322       a6_set = 1;
1323       if (unformat (input, "%U", unformat_ip4_address, &a6))
1324         a4_set = 1;
1325     }
1326
1327   /* Must have a name */
1328   if (!name_set)
1329     return 0;
1330
1331   /* Must have at least one address */
1332   if (!(a4_set + a6_set))
1333     return 0;
1334
1335   /* Build a fake DNS cache entry string, one hemorrhoid at a time */
1336   ce = name_to_labels (name);
1337   qp_offset = vec_len (ce);
1338
1339   /* Add space for the query header */
1340   vec_validate (ce, qp_offset + sizeof (dns_query_t) - 1);
1341   qp = (dns_query_t *) (ce + qp_offset);
1342
1343   qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
1344   qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1345
1346   /* Punch in space for the dns_header_t */
1347   vec_insert (ce, sizeof (dns_header_t), 0);
1348
1349   h = (dns_header_t *) ce;
1350
1351   /* Fake Transaction ID */
1352   h->id = 0xFFFF;
1353
1354   h->flags = clib_host_to_net_u16 (DNS_RD | DNS_RA);
1355   h->qdcount = clib_host_to_net_u16 (1);
1356   h->anscount = clib_host_to_net_u16 (a4_set + a6_set);
1357   h->nscount = 0;
1358   h->arcount = 0;
1359
1360   /* Now append one or two A/AAAA RR's... */
1361   if (a4_set)
1362     {
1363       /* Pointer to the name (DGMS) */
1364       vec_add1 (ce, 0xC0);
1365       vec_add1 (ce, 0x0C);
1366       vec_add2 (ce, rru8, sizeof (*rr) + 4);
1367       rr = (void *) rru8;
1368       rr->type = clib_host_to_net_u16 (DNS_TYPE_A);
1369       rr->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1370       rr->ttl = clib_host_to_net_u32 (86400);
1371       rr->rdlength = clib_host_to_net_u16 (4);
1372       memcpy (rr->rdata, &a4, sizeof (a4));
1373     }
1374   if (a6_set)
1375     {
1376       /* Pointer to the name (DGMS) */
1377       vec_add1 (ce, 0xC0);
1378       vec_add1 (ce, 0x0C);
1379       vec_add2 (ce, rru8, sizeof (*rr) + 16);
1380       rr = (void *) rru8;
1381       rr->type = clib_host_to_net_u16 (DNS_TYPE_AAAA);
1382       rr->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1383       rr->ttl = clib_host_to_net_u32 (86400);
1384       rr->rdlength = clib_host_to_net_u16 (16);
1385       memcpy (rr->rdata, &a6, sizeof (a6));
1386     }
1387   *result = ce;
1388   if (namep)
1389     *namep = name;
1390   else
1391     vec_free (name);
1392
1393   return 1;
1394 }
1395
1396 u8 *
1397 format_dns_query (u8 * s, va_list * args)
1398 {
1399   u8 **curpos = va_arg (*args, u8 **);
1400   int verbose = va_arg (*args, int);
1401   u8 *pos;
1402   dns_query_t *qp;
1403   int len, i;
1404   if (verbose > 1)
1405     s = format (s, "    Name: ");
1406
1407   /* Unwind execrated counted-label sheit */
1408   pos = *curpos;
1409   len = *pos++;
1410
1411   while (len)
1412     {
1413       for (i = 0; i < len; i++)
1414         vec_add1 (s, *pos++);
1415
1416       len = *pos++;
1417       if (len)
1418         vec_add1 (s, '.');
1419       else
1420         {
1421           vec_add1 (s, ':');
1422           vec_add1 (s, ' ');
1423         }
1424     }
1425
1426   qp = (dns_query_t *) pos;
1427   if (verbose > 1)
1428     {
1429       switch (clib_net_to_host_u16 (qp->type))
1430         {
1431         case DNS_TYPE_A:
1432           s = format (s, "type A\n");
1433           break;
1434         case DNS_TYPE_AAAA:
1435           s = format (s, "type AAAA\n");
1436           break;
1437         case DNS_TYPE_ALL:
1438           s = format (s, "type ALL\n");
1439           break;
1440
1441         default:
1442           s = format (s, "type %d\n", clib_net_to_host_u16 (qp->type));
1443           break;
1444         }
1445     }
1446
1447   pos += sizeof (*qp);
1448
1449   *curpos = pos;
1450   return s;
1451 }
1452
1453 /**
1454  * format dns reply data
1455  * verbose > 1, dump everything
1456  * verbose == 1, dump all A and AAAA records
1457  * verbose == 0, dump one A record, and one AAAA record
1458  */
1459
1460 u8 *
1461 format_dns_reply_data (u8 * s, va_list * args)
1462 {
1463   u8 *reply = va_arg (*args, u8 *);
1464   u8 **curpos = va_arg (*args, u8 **);
1465   int verbose = va_arg (*args, int);
1466   int *print_ip4 = va_arg (*args, int *);
1467   int *print_ip6 = va_arg (*args, int *);
1468   int len;
1469   u8 *pos, *pos2;
1470   dns_rr_t *rr;
1471   int i;
1472   int initial_pointer_chase = 0;
1473   u16 *tp;
1474
1475   pos = pos2 = *curpos;
1476
1477   if (verbose > 1)
1478     s = format (s, "    ");
1479
1480   /* chase pointer? almost always yes here... */
1481   if (pos2[0] == 0xc0)
1482     {
1483       pos2 = reply + pos2[1];
1484       pos += 2;
1485       initial_pointer_chase = 1;
1486     }
1487
1488   len = *pos2++;
1489
1490   while (len)
1491     {
1492       for (i = 0; i < len; i++)
1493         {
1494           if (verbose > 1)
1495             vec_add1 (s, *pos2);
1496           pos2++;
1497         }
1498       len = *pos2++;
1499       if (len)
1500         {
1501           if (verbose > 1)
1502             vec_add1 (s, '.');
1503         }
1504       else
1505         {
1506           if (verbose > 1)
1507             vec_add1 (s, ' ');
1508         }
1509     }
1510
1511   if (initial_pointer_chase == 0)
1512     pos = pos2;
1513
1514   rr = (dns_rr_t *) pos;
1515
1516   switch (clib_net_to_host_u16 (rr->type))
1517     {
1518     case DNS_TYPE_A:
1519       if (verbose > 1)
1520         {
1521           s = format (s, "A: ttl %d %U\n", clib_net_to_host_u32 (rr->ttl),
1522                       format_ip4_address, rr->rdata);
1523         }
1524       else
1525         {
1526           if (*print_ip4)
1527             s = format (s, "%U [%u] ", format_ip4_address, rr->rdata,
1528                         clib_net_to_host_u32 (rr->ttl));
1529           if (verbose == 0)
1530             *print_ip4 = 0;
1531
1532         }
1533       pos += sizeof (*rr) + 4;
1534       break;
1535
1536     case DNS_TYPE_AAAA:
1537       if (verbose > 1)
1538         {
1539           s = format (s, "AAAA: ttl %d %U\n", clib_net_to_host_u32 (rr->ttl),
1540                       format_ip6_address, rr->rdata);
1541         }
1542       else
1543         {
1544           if (*print_ip6)
1545             s = format (s, "%U [%u] ", format_ip6_address, rr->rdata,
1546                         clib_net_to_host_u32 (rr->ttl));
1547           if (verbose == 0)
1548             *print_ip6 = 0;
1549         }
1550       pos += sizeof (*rr) + 16;
1551       break;
1552
1553     case DNS_TYPE_TEXT:
1554       if (verbose > 1)
1555         {
1556           s = format (s, "TEXT: ");
1557           for (i = 0; i < clib_net_to_host_u16 (rr->rdlength); i++)
1558             vec_add1 (s, rr->rdata[i]);
1559           vec_add1 (s, '\n');
1560         }
1561       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1562       break;
1563
1564     case DNS_TYPE_HINFO:
1565       {
1566         /* Two counted strings. DGMS */
1567         u8 *len;
1568         u8 *curpos;
1569         int i;
1570         if (verbose > 1)
1571           {
1572             s = format (s, "HINFO: ");
1573             len = rr->rdata;
1574             curpos = len + 1;
1575             for (i = 0; i < *len; i++)
1576               vec_add1 (s, *curpos++);
1577
1578             vec_add1 (s, ' ');
1579             len = curpos++;
1580             for (i = 0; i < *len; i++)
1581               vec_add1 (s, *curpos++);
1582
1583             vec_add1 (s, '\n');
1584           }
1585       }
1586       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1587       break;
1588
1589     case DNS_TYPE_NAMESERVER:
1590       if (verbose > 1)
1591         {
1592           s = format (s, "Nameserver: ");
1593           pos2 = rr->rdata;
1594
1595           /* chase pointer? */
1596           if (pos2[0] == 0xc0)
1597             pos2 = reply + pos2[1];
1598
1599           len = *pos2++;
1600
1601           while (len)
1602             {
1603               for (i = 0; i < len; i++)
1604                 vec_add1 (s, *pos2++);
1605
1606               /* chase pointer, typically to offset 12... */
1607               if (pos2[0] == 0xC0)
1608                 pos2 = reply + pos2[1];
1609
1610               len = *pos2++;
1611               if (len)
1612                 vec_add1 (s, '.');
1613               else
1614                 vec_add1 (s, '\n');
1615             }
1616         }
1617       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1618       break;
1619
1620     case DNS_TYPE_MAIL_EXCHANGE:
1621       if (verbose > 1)
1622         {
1623           tp = (u16 *) rr->rdata;
1624
1625           s = format (s, "Mail Exchange: Preference %d ", (u32)
1626                       clib_net_to_host_u16 (*tp));
1627
1628           pos2 = rr->rdata + 2;
1629
1630           /* chase pointer? */
1631           if (pos2[0] == 0xc0)
1632             pos2 = reply + pos2[1];
1633
1634           len = *pos2++;
1635
1636           while (len)
1637             {
1638               for (i = 0; i < len; i++)
1639                 vec_add1 (s, *pos2++);
1640
1641               /* chase pointer */
1642               if (pos2[0] == 0xC0)
1643                 pos2 = reply + pos2[1];
1644
1645               len = *pos2++;
1646               if (len)
1647                 vec_add1 (s, '.');
1648               else
1649                 vec_add1 (s, '\n');
1650             }
1651         }
1652
1653       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1654       break;
1655
1656     case DNS_TYPE_CNAME:
1657       if (verbose > 1)
1658         {
1659           tp = (u16 *) rr->rdata;
1660
1661           s = format (s, "CNAME: ");
1662
1663           pos2 = rr->rdata;
1664
1665           /* chase pointer? */
1666           if (pos2[0] == 0xc0)
1667             pos2 = reply + pos2[1];
1668
1669           len = *pos2++;
1670
1671           while (len)
1672             {
1673               for (i = 0; i < len; i++)
1674                 vec_add1 (s, *pos2++);
1675
1676               /* chase pointer */
1677               if (pos2[0] == 0xC0)
1678                 pos2 = reply + pos2[1];
1679
1680               len = *pos2++;
1681               if (len)
1682                 vec_add1 (s, '.');
1683               else
1684                 vec_add1 (s, '\n');
1685             }
1686         }
1687       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1688       break;
1689
1690     default:
1691       if (verbose > 1)
1692         s = format (s, "type %d: len %d\n",
1693                     (int) clib_net_to_host_u16 (rr->type),
1694                     sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength));
1695       pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1696       break;
1697     }
1698
1699   *curpos = pos;
1700
1701   return s;
1702 }
1703
1704 u8 *
1705 format_dns_reply (u8 * s, va_list * args)
1706 {
1707   u8 *reply_as_u8 = va_arg (*args, u8 *);
1708   int verbose = va_arg (*args, int);
1709   dns_header_t *h;
1710   u16 id, flags;
1711   u8 *curpos;
1712   int i;
1713   int print_ip4 = 1;
1714   int print_ip6 = 1;
1715
1716   h = (dns_header_t *) reply_as_u8;
1717   id = clib_net_to_host_u16 (h->id);
1718   flags = clib_net_to_host_u16 (h->flags);
1719
1720   if (verbose > 1)
1721     {
1722       s = format (s, "DNS %s: id %d\n", (flags & DNS_QR) ? "reply" : "query",
1723                   id);
1724       s = format (s, "  %s %s %s %s\n",
1725                   (flags & DNS_RA) ? "recur" : "no-recur",
1726                   (flags & DNS_RD) ? "recur-des" : "no-recur-des",
1727                   (flags & DNS_TC) ? "trunc" : "no-trunc",
1728                   (flags & DNS_AA) ? "auth" : "non-auth");
1729       s = format (s, "  %d queries, %d answers, %d name-servers,"
1730                   " %d add'l recs\n",
1731                   clib_net_to_host_u16 (h->qdcount),
1732                   clib_net_to_host_u16 (h->anscount),
1733                   clib_net_to_host_u16 (h->nscount),
1734                   clib_net_to_host_u16 (h->arcount));
1735     }
1736
1737   curpos = (u8 *) (h + 1);
1738
1739   if (h->qdcount)
1740     {
1741       if (verbose > 1)
1742         s = format (s, "  Queries:\n");
1743       for (i = 0; i < clib_net_to_host_u16 (h->qdcount); i++)
1744         {
1745           /* The query is variable-length, so curpos is a value-result parm */
1746           s = format (s, "%U", format_dns_query, &curpos, verbose);
1747         }
1748     }
1749   if (h->anscount)
1750     {
1751       if (verbose > 1)
1752         s = format (s, "  Replies:\n");
1753
1754       for (i = 0; i < clib_net_to_host_u16 (h->anscount); i++)
1755         {
1756           /* curpos is a value-result parm */
1757           s = format (s, "%U", format_dns_reply_data, reply_as_u8, &curpos,
1758                       verbose, &print_ip4, &print_ip6);
1759         }
1760     }
1761   return s;
1762 }
1763
1764 u8 *
1765 format_dns_cache (u8 * s, va_list * args)
1766 {
1767   dns_main_t *dm = va_arg (*args, dns_main_t *);
1768   f64 now = va_arg (*args, f64);
1769   int verbose = va_arg (*args, int);
1770   u8 *name = va_arg (*args, u8 *);
1771   dns_cache_entry_t *ep;
1772   char *ss;
1773   uword *p;
1774
1775   if (dm->is_enabled == 0)
1776     {
1777       s = format (s, "The DNS cache is disabled...");
1778       return s;
1779     }
1780
1781   if (pool_elts (dm->entries) == 0)
1782     {
1783       s = format (s, "The DNS cache is empty...");
1784       return s;
1785     }
1786
1787   dns_cache_lock (dm);
1788
1789   if (name)
1790     {
1791       p = hash_get_mem (dm->cache_entry_by_name, name);
1792       if (!p)
1793         {
1794           s = format (s, "%s is not in the cache...", name);
1795           dns_cache_unlock (dm);
1796           return (s);
1797         }
1798
1799       ep = pool_elt_at_index (dm->entries, p[0]);
1800       /* Magic to spit out a C-initializer to research hemorrhoids... */
1801       if (verbose == 3)
1802         {
1803           int i, j;
1804           s = format (s, "static u8 dns_reply_data_initializer[] =\n");
1805           s = format (s, "{\n");
1806           j = 0;
1807           for (i = 0; i < vec_len (ep->dns_response); i++)
1808             {
1809               if (j++ == 8)
1810                 {
1811                   j = 0;
1812                   vec_add1 (s, '\n');
1813                 }
1814               s = format (s, "0x%02x, ", ep->dns_response[i]);
1815             }
1816           s = format (s, "};\n");
1817         }
1818       else
1819         {
1820           if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
1821             {
1822               ASSERT (ep->dns_response);
1823               if (ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC)
1824                 ss = "[S] ";
1825               else
1826                 ss = "    ";
1827
1828               if (verbose < 2 && ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
1829                 s = format (s, "%s%s -> %s", ss, ep->name, ep->cname);
1830               else
1831                 s = format (s, "%s%s -> %U", ss, ep->name,
1832                             format_dns_reply, ep->dns_response, verbose);
1833               if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
1834                 {
1835                   f64 time_left = ep->expiration_time - now;
1836                   if (time_left > 0.0)
1837                     s = format (s, "  TTL left %.1f", time_left);
1838                   else
1839                     s = format (s, "  EXPIRED");
1840                 }
1841             }
1842           else
1843             {
1844               ASSERT (ep->dns_request);
1845               s = format (s, "[P] %U", format_dns_reply, ep->dns_request,
1846                           verbose);
1847             }
1848           vec_add1 (s, '\n');
1849         }
1850       return s;
1851     }
1852
1853   /* *INDENT-OFF* */
1854   pool_foreach (ep, dm->entries,
1855   ({
1856     if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
1857       {
1858         ASSERT (ep->dns_response);
1859         if (ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC)
1860           ss = "[S] ";
1861         else
1862           ss = "    ";
1863
1864         if (verbose < 2 && ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
1865           s = format (s, "%s%s -> %s", ss, ep->name, ep->cname);
1866         else
1867           s = format (s, "%s%s -> %U", ss, ep->name,
1868                       format_dns_reply,
1869                       ep->dns_response,
1870                       verbose);
1871         if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
1872           {
1873             f64 time_left = ep->expiration_time - now;
1874             if (time_left > 0.0)
1875               s = format (s, "  TTL left %.1f", time_left);
1876             else
1877               s = format (s, "  EXPIRED");
1878
1879             if (verbose > 2)
1880               s = format (s, "    %d client notifications pending\n",
1881                           vec_len(ep->api_clients_to_notify));
1882           }
1883       }
1884     else
1885       {
1886         ASSERT (ep->dns_request);
1887         s = format (s, "[P] %U", format_dns_reply, ep->dns_request,
1888                     verbose);
1889       }
1890     vec_add1 (s, '\n');
1891   }));
1892   /* *INDENT-ON* */
1893
1894   dns_cache_unlock (dm);
1895
1896   return s;
1897 }
1898
1899 static clib_error_t *
1900 show_dns_cache_command_fn (vlib_main_t * vm,
1901                            unformat_input_t * input, vlib_cli_command_t * cmd)
1902 {
1903   dns_main_t *dm = &dns_main;
1904   int verbose = 0;
1905   u8 *name = 0;
1906   f64 now = vlib_time_now (vm);
1907
1908   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1909     {
1910       if (unformat (input, "verbose %d", &verbose))
1911         ;
1912       else if (unformat (input, "verbose"))
1913         verbose = 1;
1914       else if (unformat (input, "name %s", &name))
1915         ;
1916       else
1917         return clib_error_return (0, "unknown input `%U'",
1918                                   format_unformat_error, input);
1919     }
1920
1921   vlib_cli_output (vm, "%U", format_dns_cache, dm, now, verbose, name);
1922
1923   return 0;
1924 }
1925
1926 /* *INDENT-OFF* */
1927 VLIB_CLI_COMMAND (show_dns_cache_command) =
1928 {
1929   .path = "show dns cache",
1930   .short_help = "show dns cache [verbose [nn]]",
1931   .function = show_dns_cache_command_fn,
1932 };
1933 /* *INDENT-ON* */
1934
1935 static clib_error_t *
1936 dns_cache_add_del_command_fn (vlib_main_t * vm,
1937                               unformat_input_t * input,
1938                               vlib_cli_command_t * cmd)
1939 {
1940   dns_main_t *dm = &dns_main;
1941   u8 *dns_reply_data;
1942   u8 *name;
1943   int is_add = -1;
1944   int is_clear = -1;
1945   int rv;
1946   clib_error_t *error;
1947
1948   if (unformat (input, "add"))
1949     is_add = 1;
1950   if (unformat (input, "del"))
1951     is_add = 0;
1952   if (unformat (input, "clear"))
1953     is_clear = 1;
1954
1955   if (is_add == -1 && is_clear == -1)
1956     return clib_error_return (0, "add / del / clear required...");
1957
1958   if (is_clear == 1)
1959     {
1960       rv = dns_cache_clear (dm);
1961       switch (rv)
1962         {
1963         case 0:
1964           return 0;
1965
1966         case VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED:
1967           error = clib_error_return (0, "Name resolution not enabled");
1968           return error;
1969         }
1970     }
1971
1972   /* Delete (by name)? */
1973   if (is_add == 0)
1974     {
1975       if (unformat (input, "%v", &name))
1976         {
1977           rv = dns_delete_by_name (dm, name);
1978           switch (rv)
1979             {
1980             case VNET_API_ERROR_NO_SUCH_ENTRY:
1981               error = clib_error_return (0, "%v not in the cache...", name);
1982               vec_free (name);
1983               return error;
1984
1985             case VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED:
1986               error = clib_error_return (0, "Name resolution not enabled");
1987               vec_free (name);
1988               return error;
1989
1990             case 0:
1991               vec_free (name);
1992               return 0;
1993
1994             default:
1995               error = clib_error_return (0, "dns_delete_by_name returned %d",
1996                                          rv);
1997               vec_free (name);
1998               return error;
1999             }
2000         }
2001       return clib_error_return (0, "unknown input `%U'",
2002                                 format_unformat_error, input);
2003     }
2004
2005   /* Note: dns_add_static_entry consumes the name vector if OK... */
2006   if (unformat (input, "%U", unformat_dns_reply, &dns_reply_data, &name))
2007     {
2008       rv = dns_add_static_entry (dm, name, dns_reply_data);
2009       switch (rv)
2010         {
2011         case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
2012           vec_free (name);
2013           vec_free (dns_reply_data);
2014           return clib_error_return (0, "%v already in the cache...", name);
2015         case 0:
2016           return 0;
2017
2018         default:
2019           return clib_error_return (0, "dns_add_static_entry returned %d",
2020                                     rv);
2021         }
2022     }
2023
2024   return 0;
2025 }
2026
2027 /* *INDENT-OFF* */
2028 VLIB_CLI_COMMAND (dns_cache_add_del_command) =
2029 {
2030   .path = "dns cache",
2031   .short_help = "dns cache [add|del|clear] <name> [ip4][ip6]",
2032   .function = dns_cache_add_del_command_fn,
2033 };
2034 /* *INDENT-ON* */
2035
2036 #define DNS_FORMAT_TEST 1
2037
2038 #if DNS_FORMAT_TEST > 0
2039 #if 0
2040 /* yahoo.com */
2041 static u8 dns_reply_data_initializer[] =
2042   { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x5,
2043   0x79, 0x61, 0x68, 0x6f, 0x6f, 0x3, 0x63, 0x6f, 0x6d,
2044   0x0,                          /* null lbl */
2045   0x0, 0xff,                    /* type ALL */
2046   0x0, 0x1,                     /* class IN */
2047   0xc0, 0xc,                    /* pointer to yahoo.com name */
2048   0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x24, 0x23,
2049   0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x72, 0x65, 0x64, 0x69, 0x72,
2050   0x65, 0x63, 0x74, 0x3d, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x6d, 0x61, 0x69,
2051   0x6c, 0x2e, 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0xc0,
2052   0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73,
2053   0x35, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0,
2054   0x6, 0x3, 0x6e, 0x73, 0x34, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0,
2055   0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x31, 0xc0, 0xc, 0xc0, 0xc,
2056   0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x32,
2057   0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6,
2058   0x3, 0x6e, 0x73, 0x33, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0,
2059   0x6, 0x5c, 0x0, 0x19, 0x0, 0x1, 0x4, 0x6d, 0x74, 0x61, 0x36, 0x3, 0x61,
2060   0x6d, 0x30, 0x8, 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x64, 0x6e, 0x73, 0x3,
2061   0x6e,
2062   0x65, 0x74, 0x0, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0,
2063   0x9, 0x0, 0x1, 0x4, 0x6d, 0x74, 0x61, 0x37, 0xc0, 0xb8, 0xc0, 0xc, 0x0,
2064   0xf, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x9, 0x0, 0x1, 0x4, 0x6d, 0x74,
2065   0x61, 0x35, 0xc0, 0xb8, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
2066   0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0x44, 0x2, 0x4, 0x0, 0x0,
2067   0x0,
2068   0x0, 0x0, 0x0, 0x0, 0xa7, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
2069   0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0xc, 0xa, 0x6, 0x0, 0x0, 0x0,
2070   0x0, 0x0, 0x2, 0x40, 0x8, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
2071   0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0x58, 0xc, 0x2, 0x0, 0x0,
2072   0x0,
2073   0x0, 0x0, 0x0, 0x0, 0xa9, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x6,
2074   0x5c, 0x0, 0x4, 0x62, 0x8a, 0xfd, 0x6d, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1,
2075   0x0,
2076   0x0, 0x6, 0x5c, 0x0, 0x4, 0xce, 0xbe, 0x24, 0x2d, 0xc0, 0xc, 0x0, 0x1,
2077   0x0,
2078   0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x4, 0x62, 0x8b, 0xb4, 0x95, 0xc0, 0xc,
2079   0x0,
2080   0x6, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x2d, 0xc0, 0x7b, 0xa, 0x68,
2081   0x6f,
2082   0x73, 0x74, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x9, 0x79, 0x61, 0x68,
2083   0x6f, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0xc0, 0x12, 0x78, 0x3a, 0x85, 0x44,
2084   0x0, 0x0, 0xe, 0x10, 0x0, 0x0, 0x1, 0x2c, 0x0, 0x1b, 0xaf, 0x80, 0x0, 0x0,
2085   0x2, 0x58
2086 };
2087
2088 /* www.cisco.com, has no addresses in reply */
2089 static u8 dns_reply_data_initializer[] = {
2090   0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
2091   0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x05,
2092   0x63, 0x69, 0x73, 0x63, 0x6f, 0x03, 0x63, 0x6f, 0x6d,
2093
2094   0x00, 0x00, 0xff, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05,
2095   0x00, 0x01, 0x00, 0x00, 0x0b, 0xd3, 0x00, 0x1a, 0x03,
2096   0x77, 0x77, 0x77, 0x05, 0x63, 0x69, 0x73, 0x63, 0x6f,
2097   0x03, 0x63, 0x6f, 0x6d, 0x06, 0x61, 0x6b, 0x61, 0x64,
2098   0x6e, 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00,
2099 };
2100 #else
2101 /* google.com */
2102 static u8 dns_reply_data_initializer[] =
2103   { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x6,
2104   0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x3, 0x63, 0x6f, 0x6d, 0x0, 0x0, 0xff,
2105   0x0, 0x1, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x2b, 0x0, 0x4,
2106   0xac, 0xd9, 0x3, 0x2e, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x1,
2107   0x2b,
2108   0x0, 0x10, 0x26, 0x7, 0xf8, 0xb0, 0x40, 0x4, 0x8, 0xf, 0x0, 0x0, 0x0, 0x0,
2109   0x0, 0x0, 0x20, 0xe, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x51, 0x7f,
2110   0x0, 0x6, 0x3, 0x6e, 0x73, 0x31, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x6, 0x0, 0x1,
2111   0x0, 0x0, 0x0, 0x3b, 0x0, 0x22, 0xc0, 0x54, 0x9, 0x64, 0x6e, 0x73, 0x2d,
2112   0x61, 0x64, 0x6d, 0x69, 0x6e, 0xc0, 0xc, 0xa, 0x3d, 0xc7, 0x30, 0x0, 0x0,
2113   0x3, 0x84, 0x0, 0x0, 0x3, 0x84, 0x0, 0x0, 0x7, 0x8, 0x0, 0x0, 0x0, 0x3c,
2114   0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x11, 0x0, 0x1e,
2115   0x4, 0x61, 0x6c, 0x74, 0x32, 0x5, 0x61, 0x73, 0x70, 0x6d, 0x78, 0x1, 0x6c,
2116   0xc0, 0xc, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x4,
2117   0x0, 0xa, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0xe, 0xf,
2118   0x0, 0x24, 0x23, 0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x69, 0x6e,
2119   0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x67,
2120   0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x7e, 0x61,
2121   0x6c, 0x6c, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x51, 0x7f, 0x0, 0x6,
2122   0x3, 0x6e, 0x73, 0x32, 0xc0, 0xc, 0xc0, 0xc, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1,
2123   0x51, 0x7f, 0x0, 0xf, 0x0, 0x5, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, 0x6b,
2124   0x69, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0,
2125   0x1, 0x51, 0x7f, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x34, 0xc0, 0xc, 0xc0, 0xc,
2126   0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x9, 0x0, 0x28, 0x4, 0x61,
2127   0x6c, 0x74, 0x33, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1,
2128   0x51, 0x7f, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x33, 0xc0, 0xc, 0xc0, 0xc, 0x0,
2129   0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x9, 0x0, 0x32, 0x4, 0x61, 0x6c,
2130   0x74, 0x34, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2,
2131   0x57,
2132   0x0, 0x9, 0x0, 0x14, 0x4, 0x61, 0x6c, 0x74, 0x31, 0xc0, 0x9b
2133 };
2134 #endif
2135
2136 static clib_error_t *
2137 test_dns_fmt_command_fn (vlib_main_t * vm,
2138                          unformat_input_t * input, vlib_cli_command_t * cmd)
2139 {
2140   u8 *dns_reply_data = 0;
2141   int verbose = 0;
2142   int rv;
2143   vl_api_dns_resolve_name_reply_t _rm, *rmp = &_rm;
2144
2145   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2146     {
2147       if (unformat (input, "verbose %d", &verbose))
2148         ;
2149       else if (unformat (input, "verbose"))
2150         verbose = 1;
2151       else
2152         return clib_error_return (0, "unknown input `%U'",
2153                                   format_unformat_error, input);
2154     }
2155
2156   vec_validate (dns_reply_data, ARRAY_LEN (dns_reply_data_initializer) - 1);
2157
2158   memcpy (dns_reply_data, dns_reply_data_initializer,
2159           ARRAY_LEN (dns_reply_data_initializer));
2160
2161   vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
2162
2163   memset (rmp, 0, sizeof (*rmp));
2164
2165   rv = vnet_dns_response_to_reply (dns_reply_data, rmp, 0 /* ttl-ptr */ );
2166
2167   switch (rv)
2168     {
2169     case VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES:
2170       vlib_cli_output (vm, "no addresses found...");
2171       break;
2172
2173     default:
2174       vlib_cli_output (vm, "response to reply returned %d", rv);
2175       break;
2176
2177     case 0:
2178       if (rmp->ip4_set)
2179         vlib_cli_output (vm, "ip4 address: %U", format_ip4_address,
2180                          (ip4_address_t *) rmp->ip4_address);
2181       if (rmp->ip6_set)
2182         vlib_cli_output (vm, "ip6 address: %U", format_ip6_address,
2183                          (ip6_address_t *) rmp->ip6_address);
2184       break;
2185     }
2186
2187   vec_free (dns_reply_data);
2188
2189   return 0;
2190 }
2191
2192
2193 /* *INDENT-OFF* */
2194 VLIB_CLI_COMMAND (test_dns_fmt_command) =
2195 {
2196   .path = "test dns format",
2197   .short_help = "test dns format",
2198   .function = test_dns_fmt_command_fn,
2199 };
2200 /* *INDENT-ON* */
2201
2202 static clib_error_t *
2203 test_dns_unfmt_command_fn (vlib_main_t * vm,
2204                            unformat_input_t * input, vlib_cli_command_t * cmd)
2205 {
2206   u8 *dns_reply_data = 0;
2207   int verbose = 0;
2208   int reply_set = 0;
2209
2210   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2211     {
2212       if (unformat (input, "verbose %d", &verbose))
2213         ;
2214       else if (unformat (input, "verbose"))
2215         verbose = 1;
2216       else if (unformat (input, "%U", unformat_dns_reply, &dns_reply_data))
2217         reply_set = 1;
2218       else
2219         return clib_error_return (0, "unknown input `%U'",
2220                                   format_unformat_error, input);
2221     }
2222
2223   if (reply_set == 0)
2224     return clib_error_return (0, "dns data not set...");
2225
2226   vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
2227
2228   vec_free (dns_reply_data);
2229
2230   return 0;
2231 }
2232
2233 /* *INDENT-OFF* */
2234 VLIB_CLI_COMMAND (test_dns_unfmt_command) =
2235 {
2236   .path = "test dns unformat",
2237   .short_help = "test dns unformat <name> [ip4][ip6]",
2238   .function = test_dns_unfmt_command_fn,
2239 };
2240 /* *INDENT-ON* */
2241
2242 static clib_error_t *
2243 test_dns_expire_command_fn (vlib_main_t * vm,
2244                             unformat_input_t * input,
2245                             vlib_cli_command_t * cmd)
2246 {
2247   dns_main_t *dm = &dns_main;
2248   u8 *name;
2249   uword *p;
2250   clib_error_t *e;
2251   dns_cache_entry_t *ep;
2252
2253   if (unformat (input, "%v", &name))
2254     {
2255       vec_add1 (name, 0);
2256       _vec_len (name) -= 1;
2257     }
2258
2259   dns_cache_lock (dm);
2260
2261   p = hash_get_mem (dm->cache_entry_by_name, name);
2262   if (!p)
2263     {
2264       dns_cache_unlock (dm);
2265       e = clib_error_return (0, "%s is not in the cache...", name);
2266       vec_free (name);
2267       return e;
2268     }
2269
2270   ep = pool_elt_at_index (dm->entries, p[0]);
2271
2272   ep->expiration_time = 0;
2273
2274   return 0;
2275 }
2276
2277 /* *INDENT-OFF* */
2278 VLIB_CLI_COMMAND (test_dns_expire_command) =
2279 {
2280   .path = "test dns expire",
2281   .short_help = "test dns expire <name>",
2282   .function = test_dns_expire_command_fn,
2283 };
2284 /* *INDENT-ON* */
2285 #endif
2286
2287 /*
2288  * fd.io coding-style-patch-verification: ON
2289  *
2290  * Local Variables:
2291  * eval: (c-set-style "gnu")
2292  * End:
2293  */