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