1 /* dnsget.c
2    simple host/dig-like application using UDNS library
3 
4    Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
5    This file is part of UDNS library, an async DNS stub resolver.
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11 
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16 
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library, in file named COPYING.LGPL; if not,
19    write to the Free Software Foundation, Inc., 59 Temple Place,
20    Suite 330, Boston, MA  02111-1307  USA
21 
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 #ifdef WINDOWS
28 #include <windows.h>
29 #include <winsock2.h>
30 #else
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <sys/time.h>
35 #include <unistd.h>
36 #endif
37 #include <time.h>
38 #include <stdarg.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include "udns.h"
44 
45 #ifndef HAVE_GETOPT
46 # include "getopt.c"
47 #endif
48 
49 #ifndef AF_INET6
50 # define AF_INET6 10
51 #endif
52 
53 static char *progname;
54 static int verbose = 1;
55 static int errors;
56 static int notfound;
57 
58 /* verbosity level:
59  * <0 - bare result
60  *  0 - bare result and error messages
61  *  1 - readable result
62  *  2 - received packet contents and `trying ...' stuff
63  *  3 - sent and received packet contents
64  */
65 
die(int errnum,const char * fmt,...)66 static void die(int errnum, const char *fmt, ...) {
67   va_list ap;
68   fprintf(stderr, "%s: ", progname);
69   va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
70   if (errnum) fprintf(stderr, ": %s\n", strerror(errnum));
71   else putc('\n', stderr);
72   fflush(stderr);
73   exit(1);
74 }
75 
dns_xntop(int af,const void * src)76 static const char *dns_xntop(int af, const void *src) {
77   static char buf[6*5+4*4];
78   return dns_ntop(af, src, buf, sizeof(buf));
79 }
80 
81 struct query {
82   const char *name;		/* original query string */
83   unsigned char *dn;		/* the DN being looked up */
84   enum dns_type qtyp;		/* type of the query */
85 };
86 
query_free(struct query * q)87 static void query_free(struct query *q) {
88   free(q->dn);
89   free(q);
90 }
91 
92 static struct query *
query_new(const char * name,const unsigned char * dn,enum dns_type qtyp)93 query_new(const char *name, const unsigned char *dn, enum dns_type qtyp) {
94   struct query *q = malloc(sizeof(*q));
95   unsigned l = dns_dnlen(dn);
96   unsigned char *cdn = malloc(l);
97   if (!q || !cdn) die(0, "out of memory");
98   memcpy(cdn, dn, l);
99   q->name = name;
100   q->dn = cdn;
101   q->qtyp = qtyp;
102   return q;
103 }
104 
105 static enum dns_class qcls = DNS_C_IN;
106 
107 static void
dnserror(struct query * q,int errnum)108 dnserror(struct query *q, int errnum) {
109   if (verbose >= 0)
110     fprintf(stderr, "%s: unable to lookup %s record for %s: %s\n", progname,
111             dns_typename(q->qtyp), dns_dntosp(q->dn), dns_strerror(errnum));
112   if (errnum == DNS_E_NXDOMAIN || errnum == DNS_E_NODATA)
113     ++notfound;
114   else
115     ++errors;
116   query_free(q);
117 }
118 
119 static const unsigned char *
printtxt(const unsigned char * c)120 printtxt(const unsigned char *c) {
121   unsigned n = *c++;
122   const unsigned char *e = c + n;
123   if (verbose > 0) while(c < e) {
124     if (*c < ' ' || *c >= 127) printf("\\%03u", *c);
125     else if (*c == '\\' || *c == '"') printf("\\%c", *c);
126     else putchar(*c);
127     ++c;
128   }
129   else
130    fwrite(c, n, 1, stdout);
131   return e;
132 }
133 
134 static void
printhex(const unsigned char * c,const unsigned char * e)135 printhex(const unsigned char *c, const unsigned char *e) {
136   while(c < e)
137     printf("%02x", *c++);
138 }
139 
140 static unsigned char to_b64[] =
141 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
142 
143 static void
printb64(const unsigned char * c,const unsigned char * e)144 printb64(const unsigned char *c, const unsigned char *e) {
145   while(c < e) {
146     putchar(to_b64[c[0] >> 2]);
147     if (c+1 < e) {
148       putchar(to_b64[(c[0] & 0x3) << 4 | c[1] >> 4]);
149       if (c+2 < e) {
150         putchar(to_b64[(c[1] & 0xf) << 2 | c[2] >> 6]);
151         putchar(to_b64[c[2] & 0x3f]);
152       }
153       else {
154         putchar(to_b64[(c[1] & 0xf) << 2]);
155 	putchar('=');
156 	break;
157       }
158     }
159     else {
160       putchar(to_b64[(c[0] & 0x3) << 4]);
161       putchar('=');
162       putchar('=');
163       break;
164     }
165     c += 3;
166   }
167 }
168 
169 static void
printdate(time_t time)170 printdate(time_t time) {
171   struct tm *tm = gmtime(&time);
172   printf("%04d%02d%02d%02d%02d%02d",
173     tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
174     tm->tm_hour, tm->tm_min, tm->tm_sec);
175 }
176 
177 static void
printrr(const struct dns_parse * p,struct dns_rr * rr)178 printrr(const struct dns_parse *p, struct dns_rr *rr) {
179   const unsigned char *pkt = p->dnsp_pkt;
180   const unsigned char *end = p->dnsp_end;
181   const unsigned char *dptr = rr->dnsrr_dptr;
182   const unsigned char *dend = rr->dnsrr_dend;
183   unsigned char *dn = rr->dnsrr_dn;
184   const unsigned char *c;
185   unsigned n;
186 
187   if (verbose > 0) {
188     if (verbose > 1) {
189       if (!p->dnsp_rrl && !rr->dnsrr_dn[0] && rr->dnsrr_typ == DNS_T_OPT) {
190         printf(";EDNS%d OPT record (UDPsize: %d, ERcode: %d, Flags: 0x%02x): %d bytes\n",
191                (rr->dnsrr_ttl>>16) & 0xff,	/* version */
192                rr->dnsrr_cls,			/* udp size */
193                (rr->dnsrr_ttl>>24) & 0xff,	/* extended rcode */
194                rr->dnsrr_ttl & 0xffff,		/* flags */
195                rr->dnsrr_dsz);
196         return;
197       }
198       n = printf("%s.", dns_dntosp(rr->dnsrr_dn));
199       printf("%s%u\t%s\t%s\t",
200              n > 15 ? "\t" : n > 7 ? "\t\t" : "\t\t\t",
201              rr->dnsrr_ttl,
202              dns_classname(rr->dnsrr_cls),
203              dns_typename(rr->dnsrr_typ));
204     }
205     else
206       printf("%s. %s ", dns_dntosp(rr->dnsrr_dn), dns_typename(rr->dnsrr_typ));
207   }
208 
209   switch(rr->dnsrr_typ) {
210 
211   case DNS_T_CNAME:
212   case DNS_T_PTR:
213   case DNS_T_NS:
214   case DNS_T_MB:
215   case DNS_T_MD:
216   case DNS_T_MF:
217   case DNS_T_MG:
218   case DNS_T_MR:
219     if (dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto xperr;
220     printf("%s.", dns_dntosp(dn));
221     break;
222 
223   case DNS_T_A:
224     if (rr->dnsrr_dsz != 4) goto xperr;
225     printf("%d.%d.%d.%d", dptr[0], dptr[1], dptr[2], dptr[3]);
226     break;
227 
228   case DNS_T_AAAA:
229     if (rr->dnsrr_dsz != 16) goto xperr;
230     printf("%s", dns_xntop(AF_INET6, dptr));
231     break;
232 
233   case DNS_T_MX:
234     c = dptr + 2;
235     if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
236     printf("%d %s.", dns_get16(dptr), dns_dntosp(dn));
237     break;
238 
239   case DNS_T_TXT:
240     /* first verify it */
241     for(c = dptr; c < dend; c += n) {
242       n = *c++;
243       if (c + n > dend) goto xperr;
244     }
245     c = dptr; n = 0;
246     while (c < dend) {
247       if (verbose > 0) printf(n++ ? "\" \"":"\"");
248       c = printtxt(c);
249     }
250     if (verbose > 0) putchar('"');
251     break;
252 
253   case DNS_T_HINFO:	/* CPU, OS */
254     c = dptr;
255     n = *c++; if ((c += n) >= dend) goto xperr;
256     n = *c++; if ((c += n) != dend) goto xperr;
257     c = dptr;
258     if (verbose > 0) putchar('"');
259     c = printtxt(c);
260     if (verbose > 0) printf("\" \""); else putchar(' ');
261     printtxt(c);
262     if (verbose > 0) putchar('"');
263     break;
264 
265   case DNS_T_WKS:
266     c = dptr;
267     if (dptr + 4 + 2 >= end) goto xperr;
268     printf("%s %d", dns_xntop(AF_INET, dptr), dptr[4]);
269     c = dptr + 5;
270     for (n = 0; c < dend; ++c, n += 8) {
271       if (*c) {
272         unsigned b;
273         for (b = 0; b < 8; ++b)
274           if (*c & (1 << (7-b))) printf(" %d", n + b);
275       }
276     }
277     break;
278 
279   case DNS_T_SRV:	/* prio weight port targetDN */
280     c = dptr;
281     c += 2 + 2 + 2;
282     if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
283     c = dptr;
284     printf("%d %d %d %s.",
285            dns_get16(c+0), dns_get16(c+2), dns_get16(c+4),
286            dns_dntosp(dn));
287     break;
288 
289   case DNS_T_NAPTR:	/* order pref flags serv regexp repl */
290     c = dptr;
291     c += 4;	/* order, pref */
292     for (n = 0; n < 3; ++n)
293       if (c >= dend) goto xperr;
294       else c += *c + 1;
295     if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
296     c = dptr;
297     printf("%u %u", dns_get16(c+0), dns_get16(c+2));
298     c += 4;
299     for(n = 0; n < 3; ++n) {
300       putchar(' ');
301       if (verbose > 0) putchar('"');
302       c = printtxt(c);
303       if (verbose > 0) putchar('"');
304     }
305     printf(" %s.", dns_dntosp(dn));
306     break;
307 
308   case DNS_T_KEY:
309   case DNS_T_DNSKEY:
310     /* flags(2) proto(1) algo(1) pubkey */
311   case DNS_T_DS:
312   case DNS_T_DLV:
313     /* ktag(2) proto(1) algo(1) pubkey */
314     c = dptr;
315     if (c + 2 + 1 + 1 > dend) goto xperr;
316     printf("%d %d %d", dns_get16(c), c[2], c[3]);
317     c += 2 + 1 + 1;
318     if (c < dend) {
319       putchar(' ');
320       printb64(c, dend);
321     }
322     break;
323 
324   case DNS_T_SIG:
325   case DNS_T_RRSIG:
326     /* type(2) algo(1) labels(1) ottl(4) sexp(4) sinc(4) tag(2) sdn sig */
327     c = dptr;
328     c += 2 + 1 + 1 + 4 + 4 + 4 + 2;
329     if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr;
330     printf("%s %u %u %u ",
331            dns_typename(dns_get16(dptr)), dptr[2], dptr[3], dns_get32(dptr+4));
332     printdate(dns_get32(dptr+8));
333     putchar(' ');
334     printdate(dns_get32(dptr+12));
335     printf(" %d %s. ", dns_get16(dptr+10), dns_dntosp(dn));
336     printb64(c, dend);
337     break;
338 
339   case DNS_T_SSHFP: /* algo(1), fp type(1), fp... */
340     if (dend < dptr + 3) goto xperr;
341     printf("%u %u ", dptr[0], dptr[1]); /* algo, fp type */
342     printhex(dptr + 2, dend);
343     break;
344 
345 #if 0	/* unused RR types? */
346   case DNS_T_NSEC: /* nextDN bitmaps */
347     c = dptr;
348     if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr;
349     printf("%s.", dns_dntosp(dn));
350     unfinished.
351     break;
352 #endif
353 
354 
355   case DNS_T_SOA:
356     c = dptr;
357     if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
358         dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
359         c + 4*5 != dend)
360       goto xperr;
361     dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
362     printf("%s. ", dns_dntosp(dn));
363     dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
364     printf("%s. ", dns_dntosp(dn));
365     printf("%u %u %u %u %u",
366            dns_get32(dptr), dns_get32(dptr+4), dns_get32(dptr+8),
367            dns_get32(dptr+12), dns_get32(dptr+16));
368     break;
369 
370   case DNS_T_MINFO:
371     c = dptr;
372     if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
373         dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
374 	c != dend)
375       goto xperr;
376     dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
377     printf("%s. ", dns_dntosp(dn));
378     dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
379     printf("%s.", dns_dntosp(dn));
380     break;
381 
382   case DNS_T_NULL:
383   default:
384     printhex(dptr, dend);
385     break;
386   }
387   putchar('\n');
388   return;
389 
390 xperr:
391   printf("<parse error>\n");
392   ++errors;
393 }
394 
395 static int
printsection(struct dns_parse * p,int nrr,const char * sname)396 printsection(struct dns_parse *p, int nrr, const char *sname) {
397   struct dns_rr rr;
398   int r;
399   if (!nrr) return 0;
400   if (verbose > 1) printf("\n;; %s section (%d):\n", sname, nrr);
401 
402   p->dnsp_rrl = nrr;
403   while((r = dns_nextrr(p, &rr)) > 0)
404     printrr(p, &rr);
405   if (r < 0) printf("<<ERROR>>\n");
406   return r;
407 }
408 
409 /* dbgcb will only be called if verbose > 1 */
410 static void
dbgcb(int code,const struct sockaddr * sa,unsigned slen,const unsigned char * pkt,int r,const struct dns_query * unused_q,void * unused_data)411 dbgcb(int code, const struct sockaddr *sa, unsigned slen,
412       const unsigned char *pkt, int r,
413       const struct dns_query *unused_q, void *unused_data) {
414   struct dns_parse p;
415   const unsigned char *cur, *end;
416   int numqd;
417 
418   if (code > 0)	{
419     printf(";; trying %s.\n", dns_dntosp(dns_payload(pkt)));
420     printf(";; sending %d bytes query to ", r);
421   }
422   else
423     printf(";; received %d bytes response from ", r);
424   if (sa->sa_family == AF_INET && slen >= sizeof(struct sockaddr_in))
425     printf("%s port %d\n",
426            dns_xntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr),
427            htons(((struct sockaddr_in*)sa)->sin_port));
428 #ifdef HAVE_IPv6
429   else if (sa->sa_family == AF_INET6 && slen >= sizeof(struct sockaddr_in6))
430     printf("%s port %d\n",
431            dns_xntop(AF_INET6, &((struct sockaddr_in6*)sa)->sin6_addr),
432            htons(((struct sockaddr_in6*)sa)->sin6_port));
433 #endif
434   else
435     printf("<<unknown socket type %d>>\n", sa->sa_family);
436   if (code > 0 && verbose < 3) {
437     putchar('\n');
438     return;
439   }
440 
441   if (code == -2) printf(";; reply from unexpected source\n");
442   if (code == -5) printf(";; reply to a query we didn't sent (or old)\n");
443   if (r < DNS_HSIZE) {
444     printf(";; short packet (%d bytes)\n", r);
445     return;
446   }
447   if (dns_opcode(pkt) != 0)
448     printf(";; unexpected opcode %d\n", dns_opcode(pkt));
449   if (dns_tc(pkt) != 0)
450     printf(";; warning: TC bit set, probably incomplete reply\n");
451 
452   printf(";; ->>HEADER<<- opcode: ");
453   switch(dns_opcode(pkt)) {
454   case 0: printf("QUERY"); break;
455   case 1: printf("IQUERY"); break;
456   case 2: printf("STATUS"); break;
457   default: printf("UNKNOWN(%u)", dns_opcode(pkt)); break;
458   }
459   printf(", status: %s, id: %d, size: %d\n;; flags:",
460          dns_rcodename(dns_rcode(pkt)), dns_qid(pkt), r);
461   if (dns_qr(pkt)) printf(" qr");
462   if (dns_aa(pkt)) printf(" aa");
463   if (dns_tc(pkt)) printf(" tc");
464   if (dns_rd(pkt)) printf(" rd");
465   if (dns_ra(pkt)) printf(" ra");
466   /* if (dns_z(pkt))  printf(" z"); only one reserved bit left */
467   if (dns_ad(pkt)) printf(" ad");
468   if (dns_cd(pkt)) printf(" cd");
469   numqd = dns_numqd(pkt);
470   printf("; QUERY: %d, ANSWER: %d, AUTHORITY: %d, ADDITIONAL: %d\n",
471          numqd, dns_numan(pkt), dns_numns(pkt), dns_numar(pkt));
472   if (numqd != 1)
473     printf(";; unexpected number of entries in QUERY section: %d\n",
474            numqd);
475   printf("\n;; QUERY SECTION (%d):\n", numqd);
476   cur = dns_payload(pkt);
477   end = pkt + r;
478   while(numqd--) {
479     if (dns_getdn(pkt, &cur, end, p.dnsp_dnbuf, DNS_MAXDN) <= 0 ||
480         cur + 4 > end) {
481       printf("; invalid query section\n");
482       return;
483     }
484     r = printf(";%s.", dns_dntosp(p.dnsp_dnbuf));
485     printf("%s%s\t%s\n",
486            r > 23 ? "\t" : r > 15 ? "\t\t" : r > 7 ? "\t\t\t" : "\t\t\t\t",
487            dns_classname(dns_get16(cur+2)), dns_typename(dns_get16(cur)));
488     cur += 4;
489   }
490 
491   p.dnsp_pkt = pkt;
492   p.dnsp_cur = p.dnsp_ans = cur;
493   p.dnsp_end = end;
494   p.dnsp_qdn = NULL;
495   p.dnsp_qcls = p.dnsp_qtyp = 0;
496   p.dnsp_ttl = 0xffffffffu;
497   p.dnsp_nrr = 0;
498 
499   r = printsection(&p, dns_numan(pkt), "ANSWER");
500   if (r == 0)
501     r = printsection(&p, dns_numns(pkt), "AUTHORITY");
502   if (r == 0)
503     r = printsection(&p, dns_numar(pkt), "ADDITIONAL");
504   putchar('\n');
505 }
506 
dnscb(struct dns_ctx * ctx,void * result,void * data)507 static void dnscb(struct dns_ctx *ctx, void *result, void *data) {
508   int r = dns_status(ctx);
509   struct query *q = data;
510   struct dns_parse p;
511   struct dns_rr rr;
512   unsigned nrr;
513   unsigned char dn[DNS_MAXDN];
514   const unsigned char *pkt, *cur, *end;
515   if (!result) {
516     dnserror(q, r);
517     return;
518   }
519   pkt = result; end = pkt + r; cur = dns_payload(pkt);
520   dns_getdn(pkt, &cur, end, dn, sizeof(dn));
521   dns_initparse(&p, NULL, pkt, cur, end);
522   p.dnsp_qcls = p.dnsp_qtyp = 0;
523   nrr = 0;
524   while((r = dns_nextrr(&p, &rr)) > 0) {
525     if (!dns_dnequal(dn, rr.dnsrr_dn)) continue;
526     if ((qcls == DNS_C_ANY || qcls == rr.dnsrr_cls) &&
527         (q->qtyp == DNS_T_ANY || q->qtyp == rr.dnsrr_typ))
528       ++nrr;
529     else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) {
530       if (dns_getdn(pkt, &rr.dnsrr_dptr, end,
531                     p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 ||
532           rr.dnsrr_dptr != rr.dnsrr_dend) {
533         r = DNS_E_PROTOCOL;
534         break;
535       }
536       else {
537         if (verbose == 1) {
538           printf("%s.", dns_dntosp(dn));
539           printf(" CNAME %s.\n", dns_dntosp(p.dnsp_dnbuf));
540         }
541         dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn));
542       }
543     }
544   }
545   if (!r && !nrr)
546     r = DNS_E_NODATA;
547   if (r < 0) {
548     dnserror(q, r);
549     free(result);
550     return;
551   }
552   if (verbose < 2) {	/* else it is already printed by dbgfn */
553     dns_rewind(&p, NULL);
554     p.dnsp_qtyp = q->qtyp == DNS_T_ANY ? 0 : q->qtyp;
555     p.dnsp_qcls = qcls == DNS_C_ANY ? 0 : qcls;
556     while(dns_nextrr(&p, &rr))
557       printrr(&p, &rr);
558   }
559   free(result);
560   query_free(q);
561 }
562 
main(int argc,char ** argv)563 int main(int argc, char **argv) {
564   int i;
565   int fd;
566   fd_set fds;
567   struct timeval tv;
568   time_t now;
569   char *ns[DNS_MAXSERV];
570   int nns = 0;
571   struct query *q;
572   enum dns_type qtyp = 0;
573   struct dns_ctx *nctx = NULL;
574   int flags = 0;
575 
576   if (!(progname = strrchr(argv[0], '/'))) progname = argv[0];
577   else argv[0] = ++progname;
578 
579   if (argc <= 1)
580     die(0, "try `%s -h' for help", progname);
581 
582   if (dns_init(NULL, 0) < 0 || !(nctx = dns_new(NULL)))
583     die(errno, "unable to initialize dns library");
584   /* we keep two dns contexts: one may be needed to resolve
585    * nameservers if given as names, using default options.
586    */
587 
588   while((i = getopt(argc, argv, "vqt:c:an:o:f:h")) != EOF) switch(i) {
589   case 'v': ++verbose; break;
590   case 'q': --verbose; break;
591   case 't':
592     if (optarg[0] == '*' && !optarg[1])
593       i = DNS_T_ANY;
594     else if ((i = dns_findtypename(optarg)) <= 0)
595       die(0, "unrecognized query type `%s'", optarg);
596     qtyp = i;
597     break;
598   case 'c':
599     if (optarg[0] == '*' && !optarg[1])
600       i = DNS_C_ANY;
601     else if ((i = dns_findclassname(optarg)) < 0)
602       die(0, "unrecognized query class `%s'", optarg);
603     qcls = i;
604     break;
605   case 'a':
606     qtyp = DNS_T_ANY;
607     ++verbose;
608     break;
609   case 'n':
610     if (nns >= DNS_MAXSERV)
611       die(0, "too many nameservers, %d max", DNS_MAXSERV);
612     ns[nns++] = optarg;
613     break;
614   case 'o':
615   case 'f': {
616     char *opt;
617     const char *const delim = " \t,;";
618     for(opt = strtok(optarg, delim); opt != NULL; opt = strtok(NULL, delim)) {
619       if (dns_set_opts(NULL, optarg) == 0)
620         ;
621       else if (strcmp(opt, "aa") == 0) flags |= DNS_AAONLY;
622       else if (strcmp(optarg, "nord") == 0) flags |= DNS_NORD;
623       else if (strcmp(optarg, "dnssec") == 0) flags |= DNS_SET_DO;
624       else if (strcmp(optarg, "do")     == 0) flags |= DNS_SET_DO;
625       else if (strcmp(optarg, "cd") == 0) flags |= DNS_SET_CD;
626       else
627         die(0, "invalid option: `%s'", opt);
628     }
629     break;
630   }
631   case 'h':
632     printf(
633 "%s: simple DNS query tool (using udns version %s)\n"
634 "Usage: %s [options] domain-name...\n"
635 "where options are:\n"
636 " -h - print this help and exit\n"
637 " -v - be more verbose\n"
638 " -q - be less verbose\n"
639 " -t type - set query type (A, AAA, PTR etc)\n"
640 " -c class - set query class (IN (default), CH, HS, *)\n"
641 " -a - equivalent to -t ANY -v\n"
642 " -n ns - use given nameserver(s) instead of default\n"
643 "  (may be specified multiple times)\n"
644 " -o opt,opt,... (comma- or space-separated list,\n"
645 "                 may be specified more than once):\n"
646 "  set resovler options (the same as setting $RES_OPTIONS):\n"
647 "   timeout:sec  - initial query timeout\n"
648 "   attempts:num - number of attempt to resovle a query\n"
649 "   ndots:num    - if name has more than num dots, lookup it before search\n"
650 "   port:num     - port number for queries instead of default 53\n"
651 "   udpbuf:num   - size of UDP buffer (use EDNS0 if >512)\n"
652 "  or query flags:\n"
653 "   aa,nord,dnssec,do,cd - set query flag (auth-only, no recursion,\n"
654 "     enable DNSSEC (DNSSEC Ok), check disabled)\n"
655       , progname, dns_version(), progname);
656     return 0;
657   default:
658     die(0, "try `%s -h' for help", progname);
659   }
660 
661   argc -= optind; argv += optind;
662   if (!argc)
663     die(0, "no name(s) to query specified");
664 
665   if (nns) {
666     /* if nameservers given as names, resolve them.
667      * We only allow IPv4 nameservers as names for now.
668      * Ok, it is easy enouth to try both AAAA and A,
669      * but the question is what to do by default.
670      */
671     struct sockaddr_in sin;
672     int j, r = 0, opened = 0;
673     memset(&sin, 0, sizeof(sin));
674     sin.sin_family = AF_INET;
675     sin.sin_port = htons(dns_set_opt(NULL, DNS_OPT_PORT, -1));
676     dns_add_serv(NULL, NULL);
677     for(i = 0; i < nns; ++i) {
678       if (dns_pton(AF_INET, ns[i], &sin.sin_addr) <= 0) {
679         struct dns_rr_a4 *rr;
680         if (!opened) {
681           if (dns_open(nctx) < 0)
682             die(errno, "unable to initialize dns context");
683           opened = 1;
684         }
685         rr = dns_resolve_a4(nctx, ns[i], 0);
686         if (!rr)
687           die(0, "unable to resolve nameserver %s: %s",
688               ns[i], dns_strerror(dns_status(nctx)));
689         for(j = 0; j < rr->dnsa4_nrr; ++j) {
690           sin.sin_addr = rr->dnsa4_addr[j];
691           if ((r = dns_add_serv_s(NULL, (struct sockaddr *)&sin)) < 0)
692             break;
693         }
694         free(rr);
695       }
696       else
697         r = dns_add_serv_s(NULL, (struct sockaddr *)&sin);
698       if (r < 0)
699         die(errno, "unable to add nameserver %s",
700              dns_xntop(AF_INET, &sin.sin_addr));
701     }
702   }
703   dns_free(nctx);
704 
705   fd = dns_open(NULL);
706   if (fd < 0)
707     die(errno, "unable to initialize dns context");
708 
709   if (verbose > 1)
710     dns_set_dbgfn(NULL, dbgcb);
711 
712   if (flags)
713     dns_set_opt(NULL, DNS_OPT_FLAGS, flags);
714 
715   for (i = 0; i < argc; ++i) {
716     char *name = argv[i];
717     union {
718       struct in_addr addr;
719       struct in6_addr addr6;
720     } a;
721     unsigned char dn[DNS_MAXDN];
722     enum dns_type l_qtyp = 0;
723     int abs;
724     if (dns_pton(AF_INET, name, &a.addr) > 0) {
725       dns_a4todn(&a.addr, 0, dn, sizeof(dn));
726       l_qtyp = DNS_T_PTR;
727       abs = 1;
728     }
729 #ifdef HAVE_IPv6
730     else if (dns_pton(AF_INET6, name, &a.addr6) > 0) {
731       dns_a6todn(&a.addr6, 0, dn, sizeof(dn));
732       l_qtyp = DNS_T_PTR;
733       abs = 1;
734     }
735 #endif
736     else if (!dns_ptodn(name, strlen(name), dn, sizeof(dn), &abs))
737       die(0, "invalid name `%s'\n", name);
738     else
739       l_qtyp = DNS_T_A;
740     if (qtyp) l_qtyp = qtyp;
741     q = query_new(name, dn, l_qtyp);
742     if (abs) abs = DNS_NOSRCH;
743     if (!dns_submit_dn(NULL, dn, qcls, l_qtyp, abs, 0, dnscb, q))
744       dnserror(q, dns_status(NULL));
745   }
746 
747   FD_ZERO(&fds);
748   now = 0;
749   while((i = dns_timeouts(NULL, -1, now)) > 0) {
750     FD_SET(fd, &fds);
751     tv.tv_sec = i;
752     tv.tv_usec = 0;
753     i = select(fd+1, &fds, 0, 0, &tv);
754     now = time(NULL);
755     if (i > 0) dns_ioevent(NULL, now);
756   }
757 
758   return errors ? 1 : notfound ? 100 : 0;
759 }
760