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