2 simple host/dig-like application using UDNS library
4 Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
5 This file is part of UDNS library, an async DNS stub resolver.
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.
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.
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
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
53 static char *progname;
54 static int verbose = 1;
60 * 0 - bare result and error messages
62 * 2 - received packet contents and `trying ...' stuff
63 * 3 - sent and received packet contents
66 static void die(int errnum, const char *fmt, ...) {
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);
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));
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 */
87 static void query_free(struct query *q) {
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");
105 static enum dns_class qcls = DNS_C_IN;
108 dnserror(struct query *q, int errnum) {
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)
119 static const unsigned char *
120 printtxt(const unsigned char *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);
130 fwrite(c, n, 1, stdout);
135 printhex(const unsigned char *c, const unsigned char *e) {
137 printf("%02x", *c++);
140 static unsigned char to_b64[] =
141 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
144 printb64(const unsigned char *c, const unsigned char *e) {
146 putchar(to_b64[c[0] >> 2]);
148 putchar(to_b64[(c[0] & 0x3) << 4 | c[1] >> 4]);
150 putchar(to_b64[(c[1] & 0xf) << 2 | c[2] >> 6]);
151 putchar(to_b64[c[2] & 0x3f]);
154 putchar(to_b64[(c[1] & 0xf) << 2]);
160 putchar(to_b64[(c[0] & 0x3) << 4]);
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);
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;
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 */
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",
202 dns_classname(rr->dnsrr_cls),
203 dns_typename(rr->dnsrr_typ));
206 printf("%s. %s ", dns_dntosp(rr->dnsrr_dn), dns_typename(rr->dnsrr_typ));
209 switch(rr->dnsrr_typ) {
219 if (dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto xperr;
220 printf("%s.", dns_dntosp(dn));
224 if (rr->dnsrr_dsz != 4) goto xperr;
225 printf("%d.%d.%d.%d", dptr[0], dptr[1], dptr[2], dptr[3]);
229 if (rr->dnsrr_dsz != 16) goto xperr;
230 printf("%s", dns_xntop(AF_INET6, dptr));
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));
240 /* first verify it */
241 for(c = dptr; c < dend; c += n) {
243 if (c + n > dend) goto xperr;
247 if (verbose > 0) printf(n++ ? "\" \"":"\"");
250 if (verbose > 0) putchar('"');
253 case DNS_T_HINFO: /* CPU, OS */
255 n = *c++; if ((c += n) >= dend) goto xperr;
256 n = *c++; if ((c += n) != dend) goto xperr;
258 if (verbose > 0) putchar('"');
260 if (verbose > 0) printf("\" \""); else putchar(' ');
262 if (verbose > 0) putchar('"');
267 if (dptr + 4 + 2 >= end) goto xperr;
268 printf("%s %d", dns_xntop(AF_INET, dptr), dptr[4]);
270 for (n = 0; c < dend; ++c, n += 8) {
273 for (b = 0; b < 8; ++b)
274 if (*c & (1 << (7-b))) printf(" %d", n + b);
279 case DNS_T_SRV: /* prio weight port targetDN */
282 if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
284 printf("%d %d %d %s.",
285 dns_get16(c+0), dns_get16(c+2), dns_get16(c+4),
289 case DNS_T_NAPTR: /* order pref flags serv regexp repl */
291 c += 4; /* order, pref */
292 for (n = 0; n < 3; ++n)
293 if (c >= dend) goto xperr;
295 if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
297 printf("%u %u", dns_get16(c+0), dns_get16(c+2));
299 for(n = 0; n < 3; ++n) {
301 if (verbose > 0) putchar('"');
303 if (verbose > 0) putchar('"');
305 printf(" %s.", dns_dntosp(dn));
310 /* flags(2) proto(1) algo(1) pubkey */
313 /* ktag(2) proto(1) algo(1) pubkey */
315 if (c + 2 + 1 + 1 > dend) goto xperr;
316 printf("%d %d %d", dns_get16(c), c[2], c[3]);
326 /* type(2) algo(1) labels(1) ottl(4) sexp(4) sinc(4) tag(2) sdn sig */
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));
334 printdate(dns_get32(dptr+12));
335 printf(" %d %s. ", dns_get16(dptr+10), dns_dntosp(dn));
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);
345 #if 0 /* unused RR types? */
346 case DNS_T_NSEC: /* nextDN bitmaps */
348 if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr;
349 printf("%s.", dns_dntosp(dn));
357 if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
358 dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
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));
372 if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
373 dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
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));
384 printhex(dptr, dend);
391 printf("<parse error>\n");
396 printsection(struct dns_parse *p, int nrr, const char *sname) {
400 if (verbose > 1) printf("\n;; %s section (%d):\n", sname, nrr);
403 while((r = dns_nextrr(p, &rr)) > 0)
405 if (r < 0) printf("<<ERROR>>\n");
409 /* dbgcb will only be called if verbose > 1 */
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) {
415 const unsigned char *cur, *end;
419 printf(";; trying %s.\n", dns_dntosp(dns_payload(pkt)));
420 printf(";; sending %d bytes query to ", r);
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));
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));
435 printf("<<unknown socket type %d>>\n", sa->sa_family);
436 if (code > 0 && verbose < 3) {
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");
444 printf(";; short packet (%d bytes)\n", r);
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");
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;
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));
473 printf(";; unexpected number of entries in QUERY section: %d\n",
475 printf("\n;; QUERY SECTION (%d):\n", numqd);
476 cur = dns_payload(pkt);
479 if (dns_getdn(pkt, &cur, end, p.dnsp_dnbuf, DNS_MAXDN) <= 0 ||
481 printf("; invalid query section\n");
484 r = printf(";%s.", dns_dntosp(p.dnsp_dnbuf));
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)));
492 p.dnsp_cur = p.dnsp_ans = cur;
495 p.dnsp_qcls = p.dnsp_qtyp = 0;
496 p.dnsp_ttl = 0xffffffffu;
499 r = printsection(&p, dns_numan(pkt), "ANSWER");
501 r = printsection(&p, dns_numns(pkt), "AUTHORITY");
503 r = printsection(&p, dns_numar(pkt), "ADDITIONAL");
507 static void dnscb(struct dns_ctx *ctx, void *result, void *data) {
508 int r = dns_status(ctx);
509 struct query *q = data;
513 unsigned char dn[DNS_MAXDN];
514 const unsigned char *pkt, *cur, *end;
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;
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))
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) {
538 printf("%s.", dns_dntosp(dn));
539 printf(" CNAME %s.\n", dns_dntosp(p.dnsp_dnbuf));
541 dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn));
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))
563 int main(int argc, char **argv) {
569 char *ns[DNS_MAXSERV];
572 enum dns_type qtyp = 0;
573 struct dns_ctx *nctx = NULL;
576 if (!(progname = strrchr(argv[0], '/'))) progname = argv[0];
577 else argv[0] = ++progname;
580 die(0, "try `%s -h' for help", progname);
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.
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;
592 if (optarg[0] == '*' && !optarg[1])
594 else if ((i = dns_findtypename(optarg)) <= 0)
595 die(0, "unrecognized query type `%s'", optarg);
599 if (optarg[0] == '*' && !optarg[1])
601 else if ((i = dns_findclassname(optarg)) < 0)
602 die(0, "unrecognized query class `%s'", optarg);
610 if (nns >= DNS_MAXSERV)
611 die(0, "too many nameservers, %d max", DNS_MAXSERV);
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)
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;
627 die(0, "invalid option: `%s'", opt);
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"
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);
658 die(0, "try `%s -h' for help", progname);
661 argc -= optind; argv += optind;
663 die(0, "no name(s) to query specified");
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.
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;
681 if (dns_open(nctx) < 0)
682 die(errno, "unable to initialize dns context");
685 rr = dns_resolve_a4(nctx, ns[i], 0);
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)
697 r = dns_add_serv_s(NULL, (struct sockaddr *)&sin);
699 die(errno, "unable to add nameserver %s",
700 dns_xntop(AF_INET, &sin.sin_addr));
707 die(errno, "unable to initialize dns context");
710 dns_set_dbgfn(NULL, dbgcb);
713 dns_set_opt(NULL, DNS_OPT_FLAGS, flags);
715 for (i = 0; i < argc; ++i) {
716 char *name = argv[i];
719 struct in6_addr addr6;
721 unsigned char dn[DNS_MAXDN];
722 enum dns_type l_qtyp = 0;
724 if (dns_pton(AF_INET, name, &a.addr) > 0) {
725 dns_a4todn(&a.addr, 0, dn, sizeof(dn));
730 else if (dns_pton(AF_INET6, name, &a.addr6) > 0) {
731 dns_a6todn(&a.addr6, 0, dn, sizeof(dn));
736 else if (!dns_ptodn(name, strlen(name), dn, sizeof(dn), &abs))
737 die(0, "invalid name `%s'\n", name);
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));
749 while((i = dns_timeouts(NULL, -1, now)) > 0) {
753 i = select(fd+1, &fds, 0, 0, &tv);
755 if (i > 0) dns_ioevent(NULL, now);
758 return errors ? 1 : notfound ? 100 : 0;