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