1 #define DNS_C
2 /*
3  *  Copyright (C) 2004, 2005  James Antill
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *  email: james@and.org
20  */
21 /* dns resolving, meant for async calls ... TCP only atm. */
22 
23 #define VSTR_COMPILE_INCLUDE 1
24 #include <vstr.h>
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <getopt.h>
34 #include <string.h>
35 #include <netinet/in.h>
36 #include <netinet/tcp.h>
37 #include <arpa/inet.h>
38 #include <sys/poll.h>
39 #include <netdb.h>
40 #include <sys/time.h>
41 #include <time.h>
42 #include <signal.h>
43 
44 /* #define NDEBUG 1 -- not done via. configure atm. */
45 #include <assert.h>
46 
47 #include <err.h>
48 
49 
50 #include <socket_poll.h>
51 #include <timer_q.h>
52 
53 #include "dns.h"
54 
55 #include "vlg.h"
56 
57 #define TRUE 1
58 #define FALSE 0
59 
60 #include "app.h"
61 
62 #define MAP_MAKE_ENTRY(x) [ DNS_CLASS_ ## x ] = #x
63 static const char *dns_map_class2name[DNS_CLASS_ALL + 1] = {
64  MAP_MAKE_ENTRY(IN),
65  MAP_MAKE_ENTRY(CH),
66  MAP_MAKE_ENTRY(HS),
67  [DNS_CLASS_ALL] = "*"
68 };
69 #undef MAP_MAKE_ENTRY
70 
71 #define MAP_MAKE_ENTRY(x) [ DNS_TYPE_IN_ ## x ] = #x
72 static const char *dns_map_type_in2name[DNS_TYPE_ALL + 1] = {
73  MAP_MAKE_ENTRY(A),
74  MAP_MAKE_ENTRY(NS),
75  MAP_MAKE_ENTRY(CNAME),
76  MAP_MAKE_ENTRY(SOA),
77  MAP_MAKE_ENTRY(PTR),
78  MAP_MAKE_ENTRY(MX),
79  MAP_MAKE_ENTRY(TXT),
80  MAP_MAKE_ENTRY(AAAA),
81  MAP_MAKE_ENTRY(SRV),
82  MAP_MAKE_ENTRY(AXFR),
83  MAP_MAKE_ENTRY(IXFR),
84  [DNS_TYPE_ALL] = "*"
85 };
86 #undef MAP_MAKE_ENTRY
87 
88 #define MAP_MAKE_ENTRY(x) [ DNS_TYPE_CH_ ## x ] = #x
89 static const char *dns_map_type_ch2name[DNS_TYPE_ALL + 1] = {
90  MAP_MAKE_ENTRY(A),
91  MAP_MAKE_ENTRY(TXT),
92  [DNS_TYPE_ALL] = "*"
93 };
94 #undef MAP_MAKE_ENTRY
95 
96 #define MAP_MAKE_ENTRY(x) [ DNS_HDR_R_ ## x ] = #x
97 static const char *dns_map_hdr_r2name[DNS_HDR_RSZ] = {
98  MAP_MAKE_ENTRY(NONE),
99  MAP_MAKE_ENTRY(BFMT),
100  MAP_MAKE_ENTRY(SERV),
101  MAP_MAKE_ENTRY(NAME),
102  MAP_MAKE_ENTRY(NSUP),
103  MAP_MAKE_ENTRY(REFU),
104 };
105 #undef MAP_MAKE_ENTRY
106 
dns_get_msg_len(Vstr_base * s1,size_t pos)107 unsigned int dns_get_msg_len(Vstr_base *s1, size_t pos)
108 {
109   if (s1->len < pos + 1)
110     return (0);
111 
112   return (2 + get_b_uint16(s1, pos));
113 }
114 
dns_name_type_ch(unsigned int num)115 const char *dns_name_type_ch(unsigned int num)
116 {
117   if (num > DNS_TYPE_ALL)
118     return ("");
119 
120   if (!dns_map_type_ch2name[num])
121     return ("");
122 
123   return (dns_map_type_ch2name[num]);
124 }
125 
dns_name_type_in(unsigned int num)126 const char *dns_name_type_in(unsigned int num)
127 {
128   if (num > DNS_TYPE_ALL)
129     return ("");
130 
131   if (!dns_map_type_in2name[num])
132     return ("");
133 
134   return (dns_map_type_in2name[num]);
135 }
136 
dns_name_class(unsigned int num)137 const char *dns_name_class(unsigned int num)
138 {
139   if (num > DNS_CLASS_ALL)
140     return ("");
141 
142   if (!dns_map_class2name[num])
143     return ("");
144 
145   return (dns_map_class2name[num]);
146 }
147 
dns_name_hdr_r(unsigned int num)148 const char *dns_name_hdr_r(unsigned int num)
149 {
150   if (num > DNS_HDR_RSZ)
151     return ("");
152 
153   if (!dns_map_hdr_r2name[num])
154     return ("");
155 
156   return (dns_map_hdr_r2name[num]);
157 }
158 
dns_app_class_type(Vstr_base * out,Vstr_base * pkt,size_t pos,size_t msg_len,unsigned int * dns_class,unsigned int * dns_type)159 static size_t dns_app_class_type(Vstr_base *out, Vstr_base *pkt,
160                                  size_t pos, size_t msg_len,
161                                  unsigned int *dns_class,
162                                  unsigned int *dns_type)
163 {
164   unsigned int cnum = 0;
165   unsigned int tnum = 0;
166 
167   if (4 > vstr_sc_posdiff(pos, msg_len))
168     return (msg_len);
169 
170   tnum = get_b_uint16(pkt, pos); pos += 2;
171   cnum = get_b_uint16(pkt, pos); pos += 2;
172 
173   if (dns_class) *dns_class = cnum;
174   if (dns_type)  *dns_type  = tnum;
175 
176   if (out)
177   {
178     if (dns_name_class(cnum))
179       app_cstr_buf(out, dns_name_class(cnum));
180     else
181       app_fmt(out, "%u", cnum);
182     app_cstr_buf(out, ".");
183   }
184 
185   if (out) switch (cnum)
186   {
187     case DNS_CLASS_IN:
188       app_cstr_buf(out, dns_name_type_in(tnum));
189       break;
190     case DNS_CLASS_CH:
191       app_cstr_buf(out, dns_name_type_ch(tnum));
192       break;
193     case DNS_CLASS_ALL:
194       if (tnum == DNS_TYPE_ALL)
195       {
196         app_cstr_buf(out, "*");
197         break;
198       }
199       /* FALL THROUGH */
200     default:
201       app_fmt(out, "%u", tnum);
202       break;
203   }
204 
205   return (pos);
206 }
207 
dns_app_txt(Vstr_base * out,Vstr_base * pkt,size_t pos,size_t msg_len)208 static size_t dns_app_txt(Vstr_base *out,
209                           Vstr_base *pkt, size_t pos, size_t msg_len)
210 {
211   unsigned char tmp = 0;
212 
213   while ((pos < msg_len) && (tmp = vstr_export_chr(pkt, pos)))
214   {
215     ++pos;
216     if (tmp > vstr_sc_posdiff(pos, msg_len))
217       tmp = vstr_sc_posdiff(pos, msg_len);
218     if (out) app_fmt(out, "${vstr:%p%zu%zu%u}",
219                      pkt, pos, tmp, VSTR_TYPE_ADD_ALL_BUF);
220     pos += tmp;
221   }
222   if (pos <= msg_len)
223     ++pos;
224 
225   return (pos);
226 }
227 
228 #define OUT_EQ_VLOG() (out == base->io_dbg->out_vstr)
229 
dns_app_label(Dns_base * base,Vstr_base * out,Vstr_base * pkt,size_t pos,size_t msg_len)230 static size_t dns_app_label(Dns_base *base, Vstr_base *out,
231                             Vstr_base *pkt, size_t pos, size_t msg_len)
232 {
233   unsigned char tmp = 0;
234 
235   while ((pos < msg_len) && (tmp = vstr_export_chr(pkt, pos)))
236   {
237     if (DNS_LABEL_IS_PTR(tmp))
238     { /* ptr */
239       if (OUT_EQ_VLOG()) app_cstr_buf(out, " <BAD END>");
240       return (msg_len);
241     }
242 
243     /* label */
244     ++pos;
245     if (tmp > vstr_sc_posdiff(pos, msg_len))
246       tmp = vstr_sc_posdiff(pos, msg_len);
247     if (out) app_fmt(out, "${vstr:%p%zu%zu%u}.",
248                      pkt, pos, tmp, VSTR_TYPE_ADD_ALL_BUF);
249     pos += tmp;
250   }
251   if (pos <= msg_len)
252     ++pos;
253 
254   return (pos);
255 }
256 
dns_app_name(Dns_base * base,Vstr_base * out,Vstr_base * pkt,size_t pos,size_t msg_len)257 static size_t dns_app_name(Dns_base *base, Vstr_base *out,
258                            Vstr_base *pkt, size_t pos, size_t msg_len)
259 {
260   size_t orig_pos = pos;
261   unsigned char tmp = 0;
262 
263   while ((pos < msg_len) && (tmp = vstr_export_chr(pkt, pos)))
264   {
265     if (DNS_LABEL_IS_PTR(tmp))
266     {
267       unsigned int off = get_b_uint16(pkt, pos);
268 
269       off &= ~0xC000; /* remove top bits that specifiy it's a ptr */
270       ++off; /* offset is 0 indexed, position is 1 indexed */
271 
272       if ((off != orig_pos) && (off < pos))
273         dns_app_name(base, out, pkt, off, msg_len);
274       else if (OUT_EQ_VLOG()) app_cstr_buf(out, " <BAD END>");
275 
276       return (pos + 2);
277     }
278 
279     ++pos;
280     if (tmp > vstr_sc_posdiff(pos, msg_len))
281       tmp = vstr_sc_posdiff(pos, msg_len);
282     if (out) app_fmt(out, "${vstr:%p%zu%zu%u}.",
283                      pkt, pos, tmp, VSTR_TYPE_ADD_ALL_BUF);
284     pos += tmp;
285   }
286   if (pos <= msg_len)
287     ++pos;
288 
289   return (pos);
290 }
291 
dns_app_ttl(Vstr_base * out,Vstr_base * pkt,size_t pos,size_t msg_len)292 static size_t dns_app_ttl(Vstr_base *out,
293                           Vstr_base *pkt, size_t pos, size_t msg_len)
294 {
295   unsigned int num = 0;
296 
297   if (4 > vstr_sc_posdiff(pos, msg_len))
298     return (msg_len);
299 
300   num = get_b_uint32(pkt, pos);
301   if (out) app_fmt(out, " for %ud %02u:%02u:%02u",
302                    (num / (1 * 60 * 60 * 24)),
303                    (num / (1 * 60 * 60)) % 24,
304                    (num / (1 * 60)) % 60,
305                    (num / (1)) % 60);
306 
307   return (pos + 4);
308 }
309 
dns_app_rr_unknown_data(Dns_base * base,Vstr_base * pkt,size_t pos,size_t msg_len,unsigned int len)310 static size_t dns_app_rr_unknown_data(Dns_base *base,
311                                       Vstr_base *pkt, size_t pos,
312                                       size_t msg_len, unsigned int len)
313 {
314   (void)pkt;
315 
316   vlg_dbg2(base->io_dbg, "  RD: %u %zu\n", len, vstr_sc_posdiff(pos, msg_len));
317 
318   if (len <= vstr_sc_posdiff(pos, msg_len))
319     return (pos + len);
320 
321   return (msg_len);
322 }
323 
dns_app_rr_data(Dns_base * base,Vstr_base * out,Vstr_base * pkt,size_t pos,size_t msg_len,unsigned int dns_class,unsigned int dns_type)324 static size_t dns_app_rr_data(Dns_base *base, Vstr_base *out,
325                               Vstr_base *pkt, size_t pos, size_t msg_len,
326                               unsigned int dns_class,
327                               unsigned int dns_type)
328 {
329   unsigned int len = 0;
330 
331   if (2 > vstr_sc_posdiff(pos, msg_len))
332     return (msg_len);
333 
334   len = get_b_uint16(pkt, pos); pos += 2;
335 
336   if (!len)
337     return (dns_app_rr_unknown_data(base, pkt, pos, msg_len, len));
338 
339   if (len > (vstr_sc_posdiff(pos, msg_len)))
340     return (dns_app_rr_unknown_data(base, pkt, pos, msg_len, len));
341 
342   if ((dns_class != DNS_CLASS_IN) && (dns_class != DNS_CLASS_CH))
343     return (dns_app_rr_unknown_data(base, pkt, pos, msg_len, len));
344 
345   msg_len = vstr_sc_poslast(pos, len);
346   if (dns_class == DNS_CLASS_CH)
347   {
348     if (0) { }
349     else if (dns_type == DNS_TYPE_CH_A)
350     {
351       unsigned int num  = 0;
352 
353       if (OUT_EQ_VLOG()) app_cstr_buf(out, "  NAME: ");
354       pos = dns_app_name(base, out, pkt, pos, msg_len);
355 
356       if (2 > vstr_sc_posdiff(pos, msg_len))
357         return (dns_app_rr_unknown_data(base, pkt, pos, msg_len, len));
358 
359       num  = get_b_uint16(pkt, pos); pos += 2;
360       if (OUT_EQ_VLOG()) app_cstr_buf(out, " A: ");
361       if (out) app_fmt(out, " %u", num);
362     }
363     else if (dns_type == DNS_TYPE_CH_TXT)
364     {
365       if (OUT_EQ_VLOG()) app_cstr_buf(out, "  TXT: ");
366       pos = dns_app_txt(out, pkt, pos, msg_len);
367     }
368     else
369       return (dns_app_rr_unknown_data(base, pkt, pos, msg_len, len));
370   }
371 
372   if (dns_class == DNS_CLASS_IN)
373   {
374     if (0) { }
375     else if (dns_type == DNS_TYPE_IN_A)
376     {
377       unsigned char buf[4];
378 
379       if (len != 4)
380         return (dns_app_rr_unknown_data(base, pkt, pos, msg_len, len));
381 
382       vstr_export_buf(pkt, pos, 4, buf, sizeof(buf)); pos += 4;
383       if (OUT_EQ_VLOG()) app_cstr_buf(out, "  A: ");
384       if (out) app_fmt(out, "%u.%u.%u.%u", buf[0], buf[1], buf[2], buf[3]);
385     }
386     else if (dns_type == DNS_TYPE_IN_NS)
387     {
388       if (OUT_EQ_VLOG()) app_cstr_buf(out, "  NS: ");
389       pos = dns_app_name(base, out, pkt, pos, msg_len);
390     }
391     else if (dns_type == DNS_TYPE_IN_CNAME)
392     {
393       if (OUT_EQ_VLOG()) app_cstr_buf(out, "  CNAME: ");
394       pos = dns_app_name(base, out, pkt, pos, msg_len);
395     }
396     else if (dns_type == DNS_TYPE_IN_SOA)
397     {
398       unsigned int num_serial  = 0;
399       unsigned int num_refresh = 0;
400       unsigned int num_retry   = 0;
401       unsigned int num_expire  = 0;
402       unsigned int num_min     = 0;
403 
404       if (OUT_EQ_VLOG()) app_cstr_buf(out, "  SOA:\n");
405       if (OUT_EQ_VLOG()) app_cstr_buf(out, "    NS: ");
406       pos = dns_app_name(base, out, pkt, pos, msg_len);
407       if (OUT_EQ_VLOG())
408         app_cstr_buf(out, "\n");
409       else if (out)
410         app_cstr_buf(out, " ");
411       if (OUT_EQ_VLOG()) app_cstr_buf(out, "    ROOT: ");
412       pos = dns_app_name(base, out, pkt, pos, msg_len);
413       if (OUT_EQ_VLOG())
414         app_cstr_buf(out, "\n");
415       else if (out)
416         app_cstr_buf(out, " ");
417 
418       if (16 > vstr_sc_posdiff(pos, msg_len))
419         return (dns_app_rr_unknown_data(base, pkt, pos, msg_len, len));
420 
421       num_serial  = get_b_uint32(pkt, pos); pos += 4;
422       num_refresh = get_b_uint32(pkt, pos); pos += 4;
423       num_retry   = get_b_uint32(pkt, pos); pos += 4;
424       num_expire  = get_b_uint32(pkt, pos); pos += 4;
425       num_min     = get_b_uint32(pkt, pos); pos += 4;
426       if (OUT_EQ_VLOG())
427         app_fmt(out, "    SERIAL: %u REFRESH: %u"
428                 " RETRY: %u EXPIRE: %u MIN: %u",
429                 num_serial, num_refresh, num_retry, num_expire, num_min);
430       else if (out)
431         app_fmt(out, " %u %u %u %u %u",
432                 num_serial, num_refresh, num_retry, num_expire, num_min);
433     }
434     else if (dns_type == DNS_TYPE_IN_PTR)
435     {
436       if (OUT_EQ_VLOG()) app_cstr_buf(out, "  PTR: ");
437       pos = dns_app_name(base, out, pkt, pos, msg_len);
438     }
439     else if (dns_type == DNS_TYPE_IN_HINFO)
440     {
441       if (OUT_EQ_VLOG()) app_cstr_buf(out, "  CPU: ");
442       pos = dns_app_txt(out, pkt, pos, msg_len);
443       if (out) app_cstr_buf(out, " ");
444       if (OUT_EQ_VLOG()) app_cstr_buf(out, "OS: ");
445       pos = dns_app_txt(out, pkt, pos, msg_len);
446     }
447     else if (dns_type == DNS_TYPE_IN_MX)
448     {
449       unsigned int num = 0;
450 
451       if (len < 4)
452         return (dns_app_rr_unknown_data(base, pkt, pos, msg_len, len));
453 
454       num = get_b_uint16(pkt, pos); pos += 2;
455       if (OUT_EQ_VLOG()) app_cstr_buf(out, "  PREF: ");
456       if (out) app_fmt(out, "%u ", num);
457       if (OUT_EQ_VLOG()) app_cstr_buf(out, "NAME: ");
458       pos = dns_app_name(base, out, pkt, pos, msg_len);
459     }
460     else if (dns_type == DNS_TYPE_IN_TXT)
461     {
462       if (OUT_EQ_VLOG()) app_cstr_buf(out, "  TXT: ");
463       pos = dns_app_txt(out, pkt, pos, msg_len);
464     }
465     else if (dns_type == DNS_TYPE_IN_SRV)
466     {
467       unsigned int num_pri = 0;
468       unsigned int num_weight = 0;
469       unsigned int num_port = 0;
470 
471       if (len < 8)
472         return (dns_app_rr_unknown_data(base, pkt, pos, msg_len, len));
473 
474       num_pri    = get_b_uint16(pkt, pos); pos += 2;
475       num_weight = get_b_uint16(pkt, pos); pos += 2;
476       num_port   = get_b_uint16(pkt, pos); pos += 2;
477 
478       if (OUT_EQ_VLOG())
479         app_fmt(out, "    PRI: %u WEIGHT: %u PORT: %u NAME: ",
480                 num_pri, num_weight, num_port);
481       else if (out)
482         app_fmt(out, " %u %u %u ",
483                 num_pri, num_weight, num_port);
484 
485       pos = dns_app_name(base, out, pkt, pos, msg_len);
486     }
487     else
488       return (dns_app_rr_unknown_data(base, pkt, pos, msg_len, len));
489   }
490 
491   return (pos);
492 }
493 
dns_dbg_prnt_pkt(Dns_base * base,Vstr_base * pkt)494 void dns_dbg_prnt_pkt(Dns_base *base, Vstr_base *pkt)
495 {
496   size_t pos = 1;
497   unsigned int id = 0;
498   unsigned int flags = 0;
499   unsigned int qdc = 0;
500   unsigned int anc = 0;
501   unsigned int nsc = 0;
502   unsigned int arc = 0;
503   unsigned int scan= 0;
504   const size_t msg_len = pkt->len;
505   int prefix = FALSE;
506 
507   if (!base->io_dbg->out_dbg)
508     return;
509 
510   prefix = vlg_prefix_set(base->io_dbg, FALSE);
511 
512   if (12 <= msg_len)
513   {
514     id    = get_b_uint16(pkt, pos); pos += 2;
515     flags = get_b_uint16(pkt, pos); pos += 2;
516     qdc   = get_b_uint16(pkt, pos); pos += 2;
517     anc   = get_b_uint16(pkt, pos); pos += 2;
518     nsc   = get_b_uint16(pkt, pos); pos += 2;
519     arc   = get_b_uint16(pkt, pos); pos += 2;
520 
521     vlg_dbg1(base->io_dbg, " id=%u\n", id);
522     vlg_dbg1(base->io_dbg, " %*s: op=%u |%s|%s|%s|%s| z=%d ret=%d ->"
523              " qd=%u an=%u ns=%u ar=%u\n",
524              (int)strlen("Response"),
525              ((flags & DNS_HDR_QR) ? "Response" : "Query"),
526              ((flags & DNS_HDR_OPCMASK) >> DNS_HDR_OPCOFF),
527              ((flags & DNS_HDR_AA) ? "AA" : "  "),
528              ((flags & DNS_HDR_TC) ? "TC" : "  "),
529              ((flags & DNS_HDR_RD) ? "RD" : "  "),
530              ((flags & DNS_HDR_RA) ? "RA" : "  "),
531              ((flags & DNS_HDR_ZMASK) >> DNS_HDR_ZOFF),
532              ((flags & DNS_HDR_RMASK) >> DNS_HDR_ROFF),
533              qdc, anc, nsc, arc);
534   }
535   else
536     vlg_dbg1(base->io_dbg, "<NO HDR>\n");
537 
538   scan = 0;
539   while ((scan++ < qdc) && (6 <= vstr_sc_posdiff(pos, msg_len)))
540   {
541     vlg_dbg1(base->io_dbg, " QUERY(%u/%u): ", scan, qdc);
542     pos = dns_app_label(base, base->io_dbg->out_vstr, pkt, pos, msg_len);
543     vlg_dbg1(base->io_dbg, " ");
544     pos = dns_app_class_type(base->io_dbg->out_vstr, pkt, pos, msg_len,
545                              NULL, NULL);
546     vlg_dbg1(base->io_dbg, "\n");
547   }
548 
549   scan = 0;
550   while ((scan++ < (anc + nsc + arc)) && (12 <= vstr_sc_posdiff(pos, msg_len)))
551   {
552     unsigned int dns_class = 0;
553     unsigned int dns_type  = 0;
554 
555     if (0) { }
556     else if (anc && (scan <= (anc)))
557       vlg_dbg1(base->io_dbg, " AN-RR(%u/%u): ", scan, anc);
558     else if (nsc && (scan <= (anc + nsc)))
559       vlg_dbg1(base->io_dbg, " NS-RR(%u/%u): ", scan - anc, nsc);
560     else
561       vlg_dbg1(base->io_dbg, " AR-RR(%u/%u): ", scan - (anc + nsc), arc);
562     pos = dns_app_name(base, base->io_dbg->out_vstr, pkt, pos, msg_len);
563     vlg_dbg1(base->io_dbg, " ");
564     pos = dns_app_class_type(base->io_dbg->out_vstr, pkt, pos, msg_len,
565                              &dns_class, &dns_type);
566     pos = dns_app_ttl(base->io_dbg->out_vstr, pkt, pos, msg_len);
567     vlg_dbg1(base->io_dbg, "\n");
568     pos = dns_app_rr_data(base, base->io_dbg->out_vstr, pkt, pos, msg_len,
569                           dns_class, dns_type);
570     vlg_dbg1(base->io_dbg, "\n");
571   }
572 
573   vlg_prefix_set(base->io_dbg, prefix);
574 }
575 
dns_app_recq_pkt(Dns_base * base,unsigned int qcount,...)576 void dns_app_recq_pkt(Dns_base *base, unsigned int qcount, ...)
577 {
578   Vstr_base *io_w = base->io_w_serv;
579   va_list ap;
580   size_t pos1 = 0;
581   size_t len1 = 0;
582   unsigned int id = 0;
583   Vstr_base *s1 = vstr_make_base(io_w->conf);
584   size_t srch_pos = 0;
585   size_t srch_len = 0;
586 
587   if (!s1)
588     errno = ENOMEM, err(EXIT_FAILURE, __func__);
589 
590   pos1 = io_w->len + 1;
591   app_b_uint16(io_w, 0); /* TCP length */
592 
593   id = rand(); id &= 0xFFFF;
594   app_b_uint16(io_w, id);
595   app_b_uint16(io_w, DNS_HDR_OPC_QUERY | (base->opt_recur ? DNS_HDR_RD : 0));
596 
597   app_b_uint16(io_w, qcount);
598   app_b_uint16(io_w, 0);
599   app_b_uint16(io_w, 0);
600   app_b_uint16(io_w, 0);
601 
602   va_start(ap, qcount);
603   while (qcount--)
604   {
605     const char *name       = va_arg(ap, const char *);
606     unsigned int dns_class = va_arg(ap, unsigned int);
607     unsigned int dns_type  = va_arg(ap, unsigned int);
608 
609     assert(name);
610 
611     vstr_sub_cstr_ptr(s1, 1, s1->len, name);
612 
613     if ((dns_class == DNS_CLASS_IN) && (dns_type == DNS_TYPE_IN_PTR))
614     { /* magic remapping for ptr */
615       unsigned char ipv4[4];
616       unsigned int  ipv6[8];
617       unsigned int ern = 0;
618 
619       if (0) { }
620       else if (vstr_parse_ipv4(s1, 1, s1->len, ipv4, NULL,
621                                VSTR_FLAG_PARSE_IPV4_FULL |
622                                VSTR_FLAG_PARSE_IPV4_ONLY, NULL, &ern) && !ern)
623       {
624         vstr_del(s1, 1, s1->len);
625         vstr_add_fmt(s1, s1->len, "%u.%u.%u.%u.in-addr.arpa",
626                      ipv4[3], ipv4[2], ipv4[1], ipv4[0]);
627       }
628       else if (vstr_parse_ipv6(s1, 1, s1->len, ipv6, NULL,
629                                VSTR_FLAG_PARSE_IPV6_ONLY, NULL, &ern) && !ern)
630       {
631         vstr_del(s1, 1, s1->len);
632 # define IP6_INT2BYTES(x)                                           \
633         (ipv6[(x)] >>  0) & 0xF,                                    \
634         (ipv6[(x)] >>  4) & 0xF,                                    \
635         (ipv6[(x)] >>  8) & 0xF,                                    \
636         (ipv6[(x)] >> 12) & 0xF
637         vstr_add_fmt(s1, s1->len,
638                      "%x.%x.%x.%x." "%x.%x.%x.%x."
639                      "%x.%x.%x.%x." "%x.%x.%x.%x."
640                      "%x.%x.%x.%x." "%x.%x.%x.%x."
641                      "%x.%x.%x.%x." "%x.%x.%x.%x."
642                      "ip6.int",
643                      IP6_INT2BYTES(7), IP6_INT2BYTES(6),
644                      IP6_INT2BYTES(5), IP6_INT2BYTES(4),
645                      IP6_INT2BYTES(3), IP6_INT2BYTES(2),
646                      IP6_INT2BYTES(1), IP6_INT2BYTES(0));
647 
648 # undef IP6_INT2BYTES
649       }
650 
651     }
652 
653     /* question */
654     srch_pos = vstr_csrch_cstr_chrs_fwd(s1, 1, s1->len, ".");
655     srch_len = vstr_sc_posdiff(srch_pos, s1->len);
656     while (srch_len)
657     {
658       size_t difflen = vstr_cspn_cstr_chrs_fwd(s1, srch_pos, srch_len, ".");
659 
660       if (!difflen) /* ignore spurious '.' */
661         difflen = vstr_spn_cstr_chrs_fwd(s1, srch_pos, srch_len, ".");
662       else
663       {
664         app_b_uint8(io_w, difflen);
665         app_vstr(io_w, s1, srch_pos, difflen, VSTR_TYPE_ADD_ALL_BUF);
666 
667         if (difflen != srch_len)
668           ++difflen;
669       }
670       assert(difflen <= srch_len);
671 
672       srch_len -= difflen;
673       srch_pos += difflen;
674     }
675     app_b_uint8(io_w, 0); /* 0 length label is terminator */
676 
677     app_b_uint16(io_w, dns_type);
678     app_b_uint16(io_w, dns_class);
679   }
680   va_end(ap);
681 
682   if (io_w->conf->malloc_bad)
683     errno = ENOMEM, err(EXIT_FAILURE, __func__);
684 
685   /* make the lengths correct */
686   len1 = io_w->len - (pos1 - 1);
687   sub_b_uint16(io_w, pos1, len1 - 2);
688 
689   vlg_dbg1(base->io_dbg,
690            "\n${rep_chr:%c%zu} send ${BKMG.u:%u} ${rep_chr:%c%zu}\n",
691            '=', 33, len1 - 2, '=', 33);
692   if (base->io_dbg->out_dbg >= 1)
693     vstr_sub_vstr(s1, 1, s1->len,
694                   io_w, pos1 + 2, len1 - 2, VSTR_TYPE_ADD_BUF_PTR);
695   dns_dbg_prnt_pkt(base, s1);
696   vlg_dbg1(base->io_dbg, "\n${rep_chr:%c%zu}\n", '-', 79);
697 
698   vstr_free_base(s1);
699   io_w->conf->malloc_bad = FALSE;
700 }
701 
dns_sc_ui_out(Dns_base * base,Vstr_base * pkt)702 void dns_sc_ui_out(Dns_base *base, Vstr_base *pkt)
703 {
704   Vstr_base *io_w = base->io_w_user;
705   size_t pos = 1;
706   unsigned int id = 0;
707   unsigned int flags = 0;
708   unsigned int qdc = 0;
709   unsigned int anc = 0;
710   unsigned int nsc = 0;
711   unsigned int arc = 0;
712   unsigned int scan = 0;
713   const size_t msg_len = pkt->len;
714 
715   if (12 <= msg_len)
716   {
717     id    = get_b_uint16(pkt, pos); pos += 2;
718     flags = get_b_uint16(pkt, pos); pos += 2;
719     qdc   = get_b_uint16(pkt, pos); pos += 2;
720     anc   = get_b_uint16(pkt, pos); pos += 2;
721     nsc   = get_b_uint16(pkt, pos); pos += 2;
722     arc   = get_b_uint16(pkt, pos); pos += 2;
723   }
724 
725   /* can't check id atm. */
726   if (!(flags & DNS_HDR_QR))
727     return;
728 
729   if (!anc)
730   {
731     scan = 0;
732     while ((scan++ < qdc) && (6 <= vstr_sc_posdiff(pos, msg_len)))
733     {
734       unsigned int hdr_r_code = ((flags & DNS_HDR_RMASK) >> DNS_HDR_ROFF);
735 
736       pos = dns_app_label(base, io_w, pkt, pos, msg_len);
737       app_cstr_buf(io_w, " ");
738       pos = dns_app_class_type(io_w, pkt, pos, msg_len, NULL, NULL);
739       app_fmt(io_w, ":ret=%d#%s\n", hdr_r_code, dns_name_hdr_r(hdr_r_code));
740     }
741     return;
742   }
743 
744   scan = 0;
745   while ((scan++ < qdc) && (6 <= vstr_sc_posdiff(pos, msg_len)))
746   {
747     pos = dns_app_label(base, NULL, pkt, pos, msg_len);
748     pos = dns_app_class_type(NULL, pkt, pos, msg_len, NULL, NULL);
749   }
750 
751   scan = 0;
752   while ((scan++ < anc) && (12 <= vstr_sc_posdiff(pos, msg_len)))
753   {
754     unsigned int dns_class = 0;
755     unsigned int dns_type  = 0;
756 
757     pos = dns_app_name(base, io_w, pkt, pos, msg_len);
758     app_cstr_buf(io_w, " ");
759     pos = dns_app_class_type(io_w, pkt, pos, msg_len, &dns_class, &dns_type);
760     app_cstr_buf(io_w, " ");
761     pos = dns_app_ttl(NULL, pkt, pos, msg_len);
762     pos = dns_app_rr_data(base, io_w, pkt, pos, msg_len, dns_class, dns_type);
763     app_cstr_buf(io_w, "\n");
764   }
765 }
766 
767