mdig.c revision 1.6 1 /* $NetBSD: mdig.c,v 1.6 2020/05/24 19:46:19 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14 #include <inttypes.h>
15 #include <stdbool.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 #include <isc/app.h>
21 #include <isc/base64.h>
22 #include <isc/hash.h>
23 #include <isc/hex.h>
24 #include <isc/log.h>
25 #include <isc/mem.h>
26 #include <isc/net.h>
27 #include <isc/nonce.h>
28 #include <isc/parseint.h>
29 #include <isc/print.h>
30 #include <isc/random.h>
31 #include <isc/sockaddr.h>
32 #include <isc/socket.h>
33 #include <isc/string.h>
34 #include <isc/task.h>
35 #include <isc/timer.h>
36 #include <isc/util.h>
37
38 #include <dns/byaddr.h>
39 #include <dns/dispatch.h>
40 #include <dns/events.h>
41 #include <dns/fixedname.h>
42 #include <dns/message.h>
43 #include <dns/name.h>
44 #include <dns/rdata.h>
45 #include <dns/rdataclass.h>
46 #include <dns/rdataset.h>
47 #include <dns/rdatatype.h>
48 #include <dns/request.h>
49 #include <dns/resolver.h>
50 #include <dns/result.h>
51 #include <dns/types.h>
52 #include <dns/view.h>
53
54 #include <dst/result.h>
55
56 #include <bind9/getaddresses.h>
57
58 #define CHECK(str, x) \
59 { \
60 if ((x) != ISC_R_SUCCESS) { \
61 fprintf(stderr, "mdig: %s failed with %s\n", (str), \
62 isc_result_totext(x)); \
63 exit(-1); \
64 } \
65 }
66
67 #define RUNCHECK(x) RUNTIME_CHECK((x) == ISC_R_SUCCESS)
68
69 #define ADD_STRING(b, s) \
70 { \
71 if (strlen(s) >= isc_buffer_availablelength(b)) \
72 return ((ISC_R_NOSPACE)); \
73 else \
74 isc_buffer_putstr(b, s); \
75 }
76
77 #define MXNAME (DNS_NAME_MAXTEXT + 1)
78 #define COMMSIZE 0xffff
79 #define OUTPUTBUF 32767
80 #define MAXPORT 0xffff
81 #define PORT 53
82 #define MAXTIMEOUT 0xffff
83 #define TCPTIMEOUT 10
84 #define UDPTIMEOUT 5
85 #define MAXTRIES 0xffffffff
86
87 static isc_mem_t *mctx;
88 static dns_requestmgr_t *requestmgr;
89 static const char *batchname;
90 static FILE *batchfp;
91 static bool have_ipv4 = false;
92 static bool have_ipv6 = false;
93 static bool have_src = false;
94 static bool tcp_mode = false;
95 static bool besteffort = true;
96 static bool display_short_form = false;
97 static bool display_headers = true;
98 static bool display_comments = true;
99 static int display_rrcomments = 0;
100 static bool display_ttlunits = true;
101 static bool display_ttl = true;
102 static bool display_class = true;
103 static bool display_crypto = true;
104 static bool display_multiline = false;
105 static bool display_question = true;
106 static bool display_answer = true;
107 static bool display_authority = true;
108 static bool display_additional = true;
109 static bool display_unknown_format = false;
110 static bool yaml = false;
111 static bool continue_on_error = false;
112 static uint32_t display_splitwidth = 0xffffffff;
113 static isc_sockaddr_t srcaddr;
114 static char *server;
115 static isc_sockaddr_t dstaddr;
116 static in_port_t port = 53;
117 static isc_dscp_t dscp = -1;
118 static unsigned char cookie_secret[33];
119 static int onfly = 0;
120 static char hexcookie[81];
121
122 struct query {
123 char textname[MXNAME]; /*% Name we're going to be
124 * looking up */
125 bool recurse;
126 bool have_aaonly;
127 bool have_adflag;
128 bool have_cdflag;
129 bool have_zflag;
130 bool dnssec;
131 bool expire;
132 bool send_cookie;
133 char *cookie;
134 bool nsid;
135 dns_rdatatype_t rdtype;
136 dns_rdataclass_t rdclass;
137 uint16_t udpsize;
138 int16_t edns;
139 dns_ednsopt_t *ednsopts;
140 unsigned int ednsoptscnt;
141 unsigned int ednsflags;
142 isc_sockaddr_t *ecs_addr;
143 unsigned int timeout;
144 unsigned int udptimeout;
145 unsigned int udpretries;
146 ISC_LINK(struct query) link;
147 };
148 static struct query default_query;
149 static ISC_LIST(struct query) queries;
150
151 #define EDNSOPTS 100U
152 /*% opcode text */
153 static const char *const opcodetext[] = {
154 "QUERY", "IQUERY", "STATUS", "RESERVED3",
155 "NOTIFY", "UPDATE", "RESERVED6", "RESERVED7",
156 "RESERVED8", "RESERVED9", "RESERVED10", "RESERVED11",
157 "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15"
158 };
159
160 /*% return code text */
161 static const char *const rcodetext[] = {
162 "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP",
163 "REFUSED", "YXDOMAIN", "YXRRSET", "NXRRSET", "NOTAUTH",
164 "NOTZONE", "RESERVED11", "RESERVED12", "RESERVED13", "RESERVED14",
165 "RESERVED15", "BADVERS"
166 };
167
168 /*% safe rcodetext[] */
169 static char *
170 rcode_totext(dns_rcode_t rcode) {
171 static char buf[sizeof("?65535")];
172 union {
173 const char *consttext;
174 char *deconsttext;
175 } totext;
176
177 if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) {
178 snprintf(buf, sizeof(buf), "?%u", rcode);
179 totext.deconsttext = buf;
180 } else {
181 totext.consttext = rcodetext[rcode];
182 }
183 return (totext.deconsttext);
184 }
185
186 /* receive response event handler */
187 static void
188 recvresponse(isc_task_t *task, isc_event_t *event) {
189 dns_requestevent_t *reqev = (dns_requestevent_t *)event;
190 isc_result_t result;
191 dns_message_t *query = NULL, *response = NULL;
192 unsigned int parseflags = 0;
193 isc_buffer_t *msgbuf = NULL, *buf = NULL;
194 unsigned int len = OUTPUTBUF;
195 dns_master_style_t *style = NULL;
196 unsigned int styleflags = 0;
197 dns_messagetextflag_t flags;
198
199 UNUSED(task);
200
201 REQUIRE(reqev != NULL);
202 query = reqev->ev_arg;
203
204 if (reqev->result != ISC_R_SUCCESS) {
205 fprintf(stderr, "response failed with %s\n",
206 isc_result_totext(reqev->result));
207 if (continue_on_error) {
208 goto cleanup;
209 } else {
210 exit(-1);
211 }
212 }
213
214 result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &response);
215 CHECK("dns_message_create", result);
216
217 parseflags |= DNS_MESSAGEPARSE_PRESERVEORDER;
218 if (besteffort) {
219 parseflags |= DNS_MESSAGEPARSE_BESTEFFORT;
220 parseflags |= DNS_MESSAGEPARSE_IGNORETRUNCATION;
221 }
222
223 msgbuf = dns_request_getanswer(reqev->request);
224 result = dns_request_getresponse(reqev->request, response, parseflags);
225 CHECK("dns_request_getresponse", result);
226
227 styleflags |= DNS_STYLEFLAG_REL_OWNER;
228 if (yaml) {
229 styleflags |= DNS_STYLEFLAG_YAML;
230 response->indent.string = " ";
231 response->indent.count = 3;
232 } else {
233 if (display_comments) {
234 styleflags |= DNS_STYLEFLAG_COMMENT;
235 }
236 if (display_unknown_format) {
237 styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT;
238 }
239 if (display_rrcomments > 0) {
240 styleflags |= DNS_STYLEFLAG_RRCOMMENT;
241 }
242 if (display_ttlunits) {
243 styleflags |= DNS_STYLEFLAG_TTL_UNITS;
244 }
245 if (!display_ttl) {
246 styleflags |= DNS_STYLEFLAG_NO_TTL;
247 }
248 if (!display_class) {
249 styleflags |= DNS_STYLEFLAG_NO_CLASS;
250 }
251 if (!display_crypto) {
252 styleflags |= DNS_STYLEFLAG_NOCRYPTO;
253 }
254 if (display_multiline) {
255 styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
256 styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
257 styleflags |= DNS_STYLEFLAG_REL_DATA;
258 styleflags |= DNS_STYLEFLAG_OMIT_TTL;
259 styleflags |= DNS_STYLEFLAG_TTL;
260 styleflags |= DNS_STYLEFLAG_MULTILINE;
261 styleflags |= DNS_STYLEFLAG_COMMENT;
262 /* Turn on rrcomments unless explicitly disabled */
263 if (display_rrcomments >= 0) {
264 styleflags |= DNS_STYLEFLAG_RRCOMMENT;
265 }
266 }
267 }
268 if (display_multiline || (!display_ttl && !display_class)) {
269 result = dns_master_stylecreate(&style, styleflags, 24, 24, 24,
270 32, 80, 8, display_splitwidth,
271 mctx);
272 } else if (!display_ttl || !display_class) {
273 result = dns_master_stylecreate(&style, styleflags, 24, 24, 32,
274 40, 80, 8, display_splitwidth,
275 mctx);
276 } else {
277 result = dns_master_stylecreate(&style, styleflags, 24, 32, 40,
278 48, 80, 8, display_splitwidth,
279 mctx);
280 }
281 CHECK("dns_master_stylecreate2", result);
282
283 flags = 0;
284 if (!display_headers) {
285 flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
286 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
287 }
288 if (!display_comments) {
289 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
290 }
291
292 isc_buffer_allocate(mctx, &buf, len);
293
294 if (yaml) {
295 char sockstr[ISC_SOCKADDR_FORMATSIZE];
296 uint16_t sport;
297 char *hash;
298 int pf;
299
300 printf("-\n");
301 printf(" type: MESSAGE\n");
302 printf(" message:\n");
303
304 if (((response->flags & DNS_MESSAGEFLAG_RD) != 0) &&
305 ((response->flags & DNS_MESSAGEFLAG_RA) != 0))
306 {
307 printf(" type: RECURSIVE_RESPONSE\n");
308 } else {
309 printf(" type: AUTH_RESPONSE\n");
310 }
311
312 printf(" message_size: %ub\n",
313 isc_buffer_usedlength(msgbuf));
314
315 pf = isc_sockaddr_pf(&dstaddr);
316 if (pf == PF_INET || pf == PF_INET6) {
317 printf(" socket_family: %s\n",
318 pf == PF_INET ? "INET" : "INET6");
319
320 printf(" socket_protocol: %s\n",
321 tcp_mode ? "TCP" : "UDP");
322
323 sport = isc_sockaddr_getport(&dstaddr);
324 isc_sockaddr_format(&dstaddr, sockstr, sizeof(sockstr));
325 hash = strchr(sockstr, '#');
326 if (hash != NULL) {
327 *hash = '\0';
328 }
329 printf(" response_address: %s\n", sockstr);
330 printf(" response_port: %u\n", sport);
331 }
332
333 if (have_src) {
334 sport = isc_sockaddr_getport(&srcaddr);
335 isc_sockaddr_format(&srcaddr, sockstr, sizeof(sockstr));
336 hash = strchr(sockstr, '#');
337 if (hash != NULL) {
338 *hash = '\0';
339 }
340 printf(" query_address: %s\n", sockstr);
341 printf(" query_port: %u\n", sport);
342 }
343
344 printf(" %s:\n", "response_message_data");
345 result = dns_message_headertotext(response, style, flags, buf);
346 CHECK("dns_message_headertotext", result);
347 } else if (display_comments && !display_short_form) {
348 printf(";; Got answer:\n");
349
350 if (display_headers) {
351 printf(";; ->>HEADER<<- opcode: %s, status: %s, "
352 "id: %u\n",
353 opcodetext[response->opcode],
354 rcode_totext(response->rcode), response->id);
355 printf(";; flags:");
356 if ((response->flags & DNS_MESSAGEFLAG_QR) != 0) {
357 printf(" qr");
358 }
359 if ((response->flags & DNS_MESSAGEFLAG_AA) != 0) {
360 printf(" aa");
361 }
362 if ((response->flags & DNS_MESSAGEFLAG_TC) != 0) {
363 printf(" tc");
364 }
365 if ((response->flags & DNS_MESSAGEFLAG_RD) != 0) {
366 printf(" rd");
367 }
368 if ((response->flags & DNS_MESSAGEFLAG_RA) != 0) {
369 printf(" ra");
370 }
371 if ((response->flags & DNS_MESSAGEFLAG_AD) != 0) {
372 printf(" ad");
373 }
374 if ((response->flags & DNS_MESSAGEFLAG_CD) != 0) {
375 printf(" cd");
376 }
377 if ((response->flags & 0x0040U) != 0) {
378 printf("; MBZ: 0x4");
379 }
380
381 printf("; QUERY: %u, ANSWER: %u, "
382 "AUTHORITY: %u, ADDITIONAL: %u\n",
383 response->counts[DNS_SECTION_QUESTION],
384 response->counts[DNS_SECTION_ANSWER],
385 response->counts[DNS_SECTION_AUTHORITY],
386 response->counts[DNS_SECTION_ADDITIONAL]);
387
388 if ((response->flags & DNS_MESSAGEFLAG_RD) != 0 &&
389 (response->flags & DNS_MESSAGEFLAG_RA) == 0)
390 {
391 printf(";; WARNING: recursion requested "
392 "but not available\n");
393 }
394 }
395 }
396
397 repopulate_buffer:
398
399 if (display_comments && display_headers && !display_short_form) {
400 result = dns_message_pseudosectiontotext(
401 response, DNS_PSEUDOSECTION_OPT, style, flags, buf);
402 if (result == ISC_R_NOSPACE) {
403 buftoosmall:
404 len += OUTPUTBUF;
405 isc_buffer_free(&buf);
406 isc_buffer_allocate(mctx, &buf, len);
407 goto repopulate_buffer;
408 }
409 CHECK("dns_message_pseudosectiontotext", result);
410 }
411
412 if (display_question && display_headers && !display_short_form) {
413 result = dns_message_sectiontotext(
414 response, DNS_SECTION_QUESTION, style, flags, buf);
415 if (result == ISC_R_NOSPACE) {
416 goto buftoosmall;
417 }
418 CHECK("dns_message_sectiontotext", result);
419 }
420
421 if (display_answer && !display_short_form) {
422 result = dns_message_sectiontotext(response, DNS_SECTION_ANSWER,
423 style, flags, buf);
424 if (result == ISC_R_NOSPACE) {
425 goto buftoosmall;
426 }
427 CHECK("dns_message_sectiontotext", result);
428 } else if (display_answer) {
429 dns_name_t *name;
430 dns_rdataset_t *rdataset;
431 isc_result_t loopresult;
432 dns_name_t empty_name;
433 dns_rdata_t rdata = DNS_RDATA_INIT;
434 unsigned int answerstyleflags = 0;
435
436 if (!display_crypto) {
437 answerstyleflags |= DNS_STYLEFLAG_NOCRYPTO;
438 }
439 if (display_unknown_format) {
440 answerstyleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT;
441 }
442
443 dns_name_init(&empty_name, NULL);
444 result = dns_message_firstname(response, DNS_SECTION_ANSWER);
445 if (result != ISC_R_NOMORE) {
446 CHECK("dns_message_firstname", result);
447 }
448
449 for (;;) {
450 if (result == ISC_R_NOMORE) {
451 break;
452 }
453 CHECK("dns_message_nextname", result);
454 name = NULL;
455 dns_message_currentname(response, DNS_SECTION_ANSWER,
456 &name);
457
458 for (rdataset = ISC_LIST_HEAD(name->list);
459 rdataset != NULL;
460 rdataset = ISC_LIST_NEXT(rdataset, link))
461 {
462 loopresult = dns_rdataset_first(rdataset);
463 while (loopresult == ISC_R_SUCCESS) {
464 dns_rdataset_current(rdataset, &rdata);
465 result = dns_rdata_tofmttext(
466 &rdata, NULL, answerstyleflags,
467 0, 60, " ", buf);
468 if (result == ISC_R_NOSPACE) {
469 goto buftoosmall;
470 }
471 CHECK("dns_rdata_tofmttext", result);
472 loopresult =
473 dns_rdataset_next(rdataset);
474 dns_rdata_reset(&rdata);
475 if (strlen("\n") >=
476 isc_buffer_availablelength(buf)) {
477 goto buftoosmall;
478 }
479 isc_buffer_putstr(buf, "\n");
480 }
481 }
482 result = dns_message_nextname(response,
483 DNS_SECTION_ANSWER);
484 }
485 }
486
487 if (display_authority && !display_short_form) {
488 result = dns_message_sectiontotext(
489 response, DNS_SECTION_AUTHORITY, style, flags, buf);
490 if (result == ISC_R_NOSPACE) {
491 goto buftoosmall;
492 }
493 CHECK("dns_message_sectiontotext", result);
494 }
495
496 if (display_additional && !display_short_form) {
497 result = dns_message_sectiontotext(
498 response, DNS_SECTION_ADDITIONAL, style, flags, buf);
499 if (result == ISC_R_NOSPACE) {
500 goto buftoosmall;
501 }
502 CHECK("dns_message_sectiontotext", result);
503 }
504
505 if (display_additional && !display_short_form && display_headers) {
506 /*
507 * Only print the signature on the first record.
508 */
509 result = dns_message_pseudosectiontotext(
510 response, DNS_PSEUDOSECTION_TSIG, style, flags, buf);
511 if (result == ISC_R_NOSPACE) {
512 goto buftoosmall;
513 }
514 CHECK("dns_message_pseudosectiontotext", result);
515 result = dns_message_pseudosectiontotext(
516 response, DNS_PSEUDOSECTION_SIG0, style, flags, buf);
517 if (result == ISC_R_NOSPACE) {
518 goto buftoosmall;
519 }
520 CHECK("dns_message_pseudosectiontotext", result);
521 }
522
523 if (display_headers && display_comments && !display_short_form && !yaml)
524 {
525 printf("\n");
526 }
527
528 printf("%.*s", (int)isc_buffer_usedlength(buf),
529 (char *)isc_buffer_base(buf));
530 isc_buffer_free(&buf);
531
532 cleanup:
533 fflush(stdout);
534 if (style != NULL) {
535 dns_master_styledestroy(&style, mctx);
536 }
537 if (query != NULL) {
538 dns_message_destroy(&query);
539 }
540 if (response != NULL) {
541 dns_message_destroy(&response);
542 }
543 dns_request_destroy(&reqev->request);
544 isc_event_free(&event);
545
546 if (--onfly == 0) {
547 isc_app_shutdown();
548 }
549 return;
550 }
551
552 /*%
553 * Add EDNS0 option record to a message. Currently, the only supported
554 * options are UDP buffer size, the DO bit, and EDNS options
555 * (e.g., NSID, COOKIE, client-subnet)
556 */
557 static void
558 add_opt(dns_message_t *msg, uint16_t udpsize, uint16_t edns, unsigned int flags,
559 dns_ednsopt_t *opts, size_t count) {
560 dns_rdataset_t *rdataset = NULL;
561 isc_result_t result;
562
563 result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags,
564 opts, count);
565 CHECK("dns_message_buildopt", result);
566 result = dns_message_setopt(msg, rdataset);
567 CHECK("dns_message_setopt", result);
568 }
569
570 static void
571 compute_cookie(unsigned char *cookie, size_t len) {
572 /* XXXMPA need to fix, should be per server. */
573 INSIST(len >= 8U);
574 memmove(cookie, cookie_secret, 8);
575 }
576
577 static isc_result_t
578 sendquery(struct query *query, isc_task_t *task) {
579 dns_request_t *request;
580 dns_message_t *message;
581 dns_name_t *qname;
582 dns_rdataset_t *qrdataset;
583 isc_result_t result;
584 dns_fixedname_t queryname;
585 isc_buffer_t buf;
586 unsigned int options;
587
588 onfly++;
589
590 dns_fixedname_init(&queryname);
591 isc_buffer_init(&buf, query->textname, strlen(query->textname));
592 isc_buffer_add(&buf, strlen(query->textname));
593 result = dns_name_fromtext(dns_fixedname_name(&queryname), &buf,
594 dns_rootname, 0, NULL);
595 CHECK("dns_name_fromtext", result);
596
597 message = NULL;
598 result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &message);
599 CHECK("dns_message_create", result);
600
601 message->opcode = dns_opcode_query;
602 if (query->recurse) {
603 message->flags |= DNS_MESSAGEFLAG_RD;
604 }
605 if (query->have_aaonly) {
606 message->flags |= DNS_MESSAGEFLAG_AA;
607 }
608 if (query->have_adflag) {
609 message->flags |= DNS_MESSAGEFLAG_AD;
610 }
611 if (query->have_cdflag) {
612 message->flags |= DNS_MESSAGEFLAG_CD;
613 }
614 if (query->have_zflag) {
615 message->flags |= 0x0040U;
616 }
617 message->rdclass = query->rdclass;
618 message->id = (unsigned short)(random() & 0xFFFF);
619
620 qname = NULL;
621 result = dns_message_gettempname(message, &qname);
622 CHECK("dns_message_gettempname", result);
623
624 qrdataset = NULL;
625 result = dns_message_gettemprdataset(message, &qrdataset);
626 CHECK("dns_message_gettemprdataset", result);
627
628 dns_name_init(qname, NULL);
629 dns_name_clone(dns_fixedname_name(&queryname), qname);
630 dns_rdataset_makequestion(qrdataset, query->rdclass, query->rdtype);
631 ISC_LIST_APPEND(qname->list, qrdataset, link);
632 dns_message_addname(message, qname, DNS_SECTION_QUESTION);
633
634 if (query->udpsize > 0 || query->dnssec || query->edns > -1 ||
635 query->ecs_addr != NULL)
636 {
637 dns_ednsopt_t opts[EDNSOPTS + DNS_EDNSOPTIONS];
638 unsigned int flags;
639 int i = 0;
640 char ecsbuf[20];
641 unsigned char cookie[40];
642
643 if (query->udpsize == 0) {
644 query->udpsize = 4096;
645 }
646 if (query->edns < 0) {
647 query->edns = 0;
648 }
649
650 if (query->nsid) {
651 INSIST(i < DNS_EDNSOPTIONS);
652 opts[i].code = DNS_OPT_NSID;
653 opts[i].length = 0;
654 opts[i].value = NULL;
655 i++;
656 }
657
658 if (query->ecs_addr != NULL) {
659 uint8_t addr[16], family;
660 uint32_t plen;
661 struct sockaddr *sa;
662 struct sockaddr_in *sin;
663 struct sockaddr_in6 *sin6;
664 size_t addrl;
665 isc_buffer_t b;
666
667 sa = &query->ecs_addr->type.sa;
668 plen = query->ecs_addr->length;
669
670 /* Round up prefix len to a multiple of 8 */
671 addrl = (plen + 7) / 8;
672
673 INSIST(i < DNS_EDNSOPTIONS);
674 opts[i].code = DNS_OPT_CLIENT_SUBNET;
675 opts[i].length = (uint16_t)addrl + 4;
676 CHECK("isc_buffer_allocate", result);
677 isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf));
678 if (sa->sa_family == AF_INET) {
679 family = 1;
680 sin = (struct sockaddr_in *)sa;
681 memmove(addr, &sin->sin_addr, 4);
682 if ((plen % 8) != 0) {
683 addr[addrl - 1] &= ~0U
684 << (8 - (plen % 8));
685 }
686 } else {
687 family = 2;
688 sin6 = (struct sockaddr_in6 *)sa;
689 memmove(addr, &sin6->sin6_addr, 16);
690 }
691
692 /* Mask off last address byte */
693 if (addrl > 0 && (plen % 8) != 0) {
694 addr[addrl - 1] &= ~0U << (8 - (plen % 8));
695 }
696
697 /* family */
698 isc_buffer_putuint16(&b, family);
699 /* source prefix-length */
700 isc_buffer_putuint8(&b, plen);
701 /* scope prefix-length */
702 isc_buffer_putuint8(&b, 0);
703 /* address */
704 if (addrl > 0) {
705 isc_buffer_putmem(&b, addr, (unsigned)addrl);
706 }
707
708 opts[i].value = (uint8_t *)ecsbuf;
709 i++;
710 }
711
712 if (query->send_cookie) {
713 INSIST(i < DNS_EDNSOPTIONS);
714 opts[i].code = DNS_OPT_COOKIE;
715 if (query->cookie != NULL) {
716 isc_buffer_t b;
717
718 isc_buffer_init(&b, cookie, sizeof(cookie));
719 result = isc_hex_decodestring(query->cookie,
720 &b);
721 CHECK("isc_hex_decodestring", result);
722 opts[i].value = isc_buffer_base(&b);
723 opts[i].length = isc_buffer_usedlength(&b);
724 } else {
725 compute_cookie(cookie, 8);
726 opts[i].length = 8;
727 opts[i].value = cookie;
728 }
729 i++;
730 }
731
732 if (query->expire) {
733 INSIST(i < DNS_EDNSOPTIONS);
734 opts[i].code = DNS_OPT_EXPIRE;
735 opts[i].length = 0;
736 opts[i].value = NULL;
737 i++;
738 }
739
740 if (query->ednsoptscnt != 0) {
741 memmove(&opts[i], query->ednsopts,
742 sizeof(dns_ednsopt_t) * query->ednsoptscnt);
743 i += query->ednsoptscnt;
744 }
745
746 flags = query->ednsflags;
747 flags &= ~DNS_MESSAGEEXTFLAG_DO;
748 if (query->dnssec) {
749 flags |= DNS_MESSAGEEXTFLAG_DO;
750 }
751 add_opt(message, query->udpsize, query->edns, flags, opts, i);
752 }
753
754 options = 0;
755 if (tcp_mode) {
756 options |= DNS_REQUESTOPT_TCP | DNS_REQUESTOPT_SHARE;
757 }
758 request = NULL;
759 result = dns_request_createvia(
760 requestmgr, message, have_src ? &srcaddr : NULL, &dstaddr, dscp,
761 options, NULL, query->timeout, query->udptimeout,
762 query->udpretries, task, recvresponse, message, &request);
763 CHECK("dns_request_createvia4", result);
764
765 return (ISC_R_SUCCESS);
766 }
767
768 static void
769 sendqueries(isc_task_t *task, isc_event_t *event) {
770 struct query *query = (struct query *)event->ev_arg;
771
772 isc_event_free(&event);
773
774 while (query != NULL) {
775 struct query *next = ISC_LIST_NEXT(query, link);
776
777 sendquery(query, task);
778 query = next;
779 }
780
781 if (onfly == 0) {
782 isc_app_shutdown();
783 }
784 return;
785 }
786
787 ISC_PLATFORM_NORETURN_PRE static void
788 usage(void) ISC_PLATFORM_NORETURN_POST;
789
790 static void
791 usage(void) {
792 fputs("Usage: mdig @server {global-opt} host\n"
793 " {local-opt} [ host {local-opt} [...]]\n",
794 stderr);
795 fputs("\nUse \"mdig -h\" (or \"mdig -h | more\") "
796 "for complete list of options\n",
797 stderr);
798 exit(1);
799 }
800
801 /*% help */
802 static void
803 help(void) {
804 fputs("Usage: mdig @server {global-opt} host\n"
805 " {local-opt} [ host {local-opt} [...]]\n",
806 stdout);
807 fputs("Where:\n"
808 " anywhere opt is one of:\n"
809 " -f filename (batch mode)\n"
810 " -h (print help and exit)\n"
811 " -v (print version and exit)\n"
812 " global opt is one of:\n"
813 " -4 (use IPv4 query transport "
814 "only)\n"
815 " -6 (use IPv6 query transport "
816 "only)\n"
817 " -b address[#port] (bind to source "
818 "address/port)\n"
819 " -p port (specify port number)\n"
820 " -m (enable memory usage "
821 "debugging)\n"
822 " +[no]dscp[=###] (Set the DSCP value to ### "
823 "[0..63])\n"
824 " +[no]vc (TCP mode)\n"
825 " +[no]tcp (TCP mode, alternate "
826 "syntax)\n"
827 " +[no]besteffort (Try to parse even illegal "
828 "messages)\n"
829 " +[no]cl (Control display of class "
830 "in records)\n"
831 " +[no]comments (Control display of "
832 "comment lines)\n"
833 " +[no]rrcomments (Control display of "
834 "per-record "
835 "comments)\n"
836 " +[no]crypto (Control display of "
837 "cryptographic "
838 "fields in records)\n"
839 " +[no]question (Control display of "
840 "question)\n"
841 " +[no]answer (Control display of "
842 "answer)\n"
843 " +[no]authority (Control display of "
844 "authority)\n"
845 " +[no]additional (Control display of "
846 "additional)\n"
847 " +[no]short (Disable everything except "
848 "short\n"
849 " form of answer)\n"
850 " +[no]ttlid (Control display of ttls "
851 "in records)\n"
852 " +[no]ttlunits (Display TTLs in "
853 "human-readable units)\n"
854 " +[no]unknownformat (Print RDATA in RFC 3597 "
855 "\"unknown\" format)\n"
856 " +[no]all (Set or clear all display "
857 "flags)\n"
858 " +[no]multiline (Print records in an "
859 "expanded format)\n"
860 " +[no]split=## (Split hex/base64 fields "
861 "into chunks)\n"
862 " local opt is one of:\n"
863 " -c class (specify query class)\n"
864 " -t type (specify query type)\n"
865 " -x dot-notation (shortcut for reverse "
866 "lookups)\n"
867 " +timeout=### (Set query timeout) "
868 "[UDP=5,TCP=10]\n"
869 " +udptimeout=### (Set timeout before UDP "
870 "retry)\n"
871 " +tries=### (Set number of UDP "
872 "attempts) [3]\n"
873 " +retry=### (Set number of UDP "
874 "retries) [2]\n"
875 " +bufsize=### (Set EDNS0 Max UDP packet "
876 "size)\n"
877 " +subnet=addr (Set edns-client-subnet "
878 "option)\n"
879 " +[no]edns[=###] (Set EDNS version) [0]\n"
880 " +ednsflags=### (Set EDNS flag bits)\n"
881 " +ednsopt=###[:value] (Send specified EDNS "
882 "option)\n"
883 " +noednsopt (Clear list of +ednsopt "
884 "options)\n"
885 " +[no]recurse (Recursive mode)\n"
886 " +[no]aaonly (Set AA flag in query "
887 "(+[no]aaflag))\n"
888 " +[no]adflag (Set AD flag in query)\n"
889 " +[no]cdflag (Set CD flag in query)\n"
890 " +[no]zflag (Set Z flag in query)\n"
891 " +[no]dnssec (Request DNSSEC records)\n"
892 " +[no]expire (Request time to expire)\n"
893 " +[no]cookie[=###] (Send a COOKIE option)\n"
894 " +[no]nsid (Request Name Server ID)\n",
895 stdout);
896 }
897
898 ISC_PLATFORM_NORETURN_PRE static void
899 fatal(const char *format, ...)
900 ISC_FORMAT_PRINTF(1, 2) ISC_PLATFORM_NORETURN_POST;
901
902 static void
903 fatal(const char *format, ...) {
904 va_list args;
905
906 fflush(stdout);
907 fprintf(stderr, "mdig: ");
908 va_start(args, format);
909 vfprintf(stderr, format, args);
910 va_end(args);
911 fprintf(stderr, "\n");
912 exit(-2);
913 }
914
915 static isc_result_t
916 parse_uint_helper(uint32_t *uip, const char *value, uint32_t max,
917 const char *desc, int base) {
918 uint32_t n;
919 isc_result_t result = isc_parse_uint32(&n, value, base);
920 if (result == ISC_R_SUCCESS && n > max) {
921 result = ISC_R_RANGE;
922 }
923 if (result != ISC_R_SUCCESS) {
924 printf("invalid %s '%s': %s\n", desc, value,
925 isc_result_totext(result));
926 return (result);
927 }
928 *uip = n;
929 return (ISC_R_SUCCESS);
930 }
931
932 static isc_result_t
933 parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc) {
934 return (parse_uint_helper(uip, value, max, desc, 10));
935 }
936
937 static isc_result_t
938 parse_xint(uint32_t *uip, const char *value, uint32_t max, const char *desc) {
939 return (parse_uint_helper(uip, value, max, desc, 0));
940 }
941
942 static void
943 newopts(struct query *query) {
944 size_t len = sizeof(query->ednsopts[0]) * EDNSOPTS;
945 size_t i;
946
947 query->ednsopts = isc_mem_allocate(mctx, len);
948
949 for (i = 0; i < EDNSOPTS; i++) {
950 query->ednsopts[i].code = 0;
951 query->ednsopts[i].length = 0;
952 query->ednsopts[i].value = NULL;
953 }
954 }
955
956 static void
957 save_opt(struct query *query, char *code, char *value) {
958 uint32_t num;
959 isc_buffer_t b;
960 isc_result_t result;
961
962 if (query->ednsopts == NULL) {
963 newopts(query);
964 }
965
966 if (query->ednsoptscnt == EDNSOPTS) {
967 fatal("too many ednsopts");
968 }
969
970 result = parse_uint(&num, code, 65535, "ednsopt");
971 if (result != ISC_R_SUCCESS) {
972 fatal("bad edns code point: %s", code);
973 }
974
975 query->ednsopts[query->ednsoptscnt].code = num;
976 query->ednsopts[query->ednsoptscnt].length = 0;
977 query->ednsopts[query->ednsoptscnt].value = NULL;
978
979 if (value != NULL) {
980 char *buf;
981 buf = isc_mem_allocate(mctx, strlen(value) / 2 + 1);
982 isc_buffer_init(&b, buf, strlen(value) / 2 + 1);
983 result = isc_hex_decodestring(value, &b);
984 CHECK("isc_hex_decodestring", result);
985 query->ednsopts[query->ednsoptscnt].value = isc_buffer_base(&b);
986 query->ednsopts[query->ednsoptscnt].length =
987 isc_buffer_usedlength(&b);
988 }
989
990 query->ednsoptscnt++;
991 }
992
993 static isc_result_t
994 parse_netprefix(isc_sockaddr_t **sap, const char *value) {
995 isc_sockaddr_t *sa = NULL;
996 struct in_addr in4;
997 struct in6_addr in6;
998 uint32_t netmask = 0xffffffff;
999 char *slash = NULL;
1000 bool parsed = false;
1001 char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX/128")];
1002
1003 if (strlcpy(buf, value, sizeof(buf)) >= sizeof(buf)) {
1004 fatal("invalid prefix '%s'\n", value);
1005 }
1006
1007 slash = strchr(buf, '/');
1008 if (slash != NULL) {
1009 isc_result_t result;
1010 *slash = '\0';
1011 result = isc_parse_uint32(&netmask, slash + 1, 10);
1012 if (result != ISC_R_SUCCESS) {
1013 fatal("invalid prefix length in '%s': %s\n", value,
1014 isc_result_totext(result));
1015 }
1016 } else if (strcmp(value, "0") == 0) {
1017 netmask = 0;
1018 }
1019
1020 sa = isc_mem_allocate(mctx, sizeof(*sa));
1021 if (inet_pton(AF_INET6, buf, &in6) == 1) {
1022 parsed = true;
1023 isc_sockaddr_fromin6(sa, &in6, 0);
1024 if (netmask > 128) {
1025 netmask = 128;
1026 }
1027 } else if (inet_pton(AF_INET, buf, &in4) == 1) {
1028 parsed = true;
1029 isc_sockaddr_fromin(sa, &in4, 0);
1030 if (netmask > 32) {
1031 netmask = 32;
1032 }
1033 } else if (netmask != 0xffffffff) {
1034 int i;
1035
1036 for (i = 0; i < 3 && strlen(buf) < sizeof(buf) - 2; i++) {
1037 strlcat(buf, ".0", sizeof(buf));
1038 if (inet_pton(AF_INET, buf, &in4) == 1) {
1039 parsed = true;
1040 isc_sockaddr_fromin(sa, &in4, 0);
1041 break;
1042 }
1043 }
1044
1045 if (netmask > 32) {
1046 netmask = 32;
1047 }
1048 }
1049
1050 if (!parsed) {
1051 fatal("invalid address '%s'", value);
1052 }
1053
1054 sa->length = netmask;
1055 *sap = sa;
1056
1057 return (ISC_R_SUCCESS);
1058 }
1059
1060 /*%
1061 * Append 'len' bytes of 'text' at '*p', failing with
1062 * ISC_R_NOSPACE if that would advance p past 'end'.
1063 */
1064 static isc_result_t
1065 append(const char *text, int len, char **p, char *end) {
1066 if (len > end - *p) {
1067 return (ISC_R_NOSPACE);
1068 }
1069 memmove(*p, text, len);
1070 *p += len;
1071 return (ISC_R_SUCCESS);
1072 }
1073
1074 static isc_result_t
1075 reverse_octets(const char *in, char **p, char *end) {
1076 const char *dot = strchr(in, '.');
1077 int len;
1078 if (dot != NULL) {
1079 isc_result_t result;
1080 result = reverse_octets(dot + 1, p, end);
1081 CHECK("reverse_octets", result);
1082 result = append(".", 1, p, end);
1083 CHECK("append", result);
1084 len = (int)(dot - in);
1085 } else {
1086 len = strlen(in);
1087 }
1088 return (append(in, len, p, end));
1089 }
1090
1091 static void
1092 get_reverse(char *reverse, size_t len, const char *value) {
1093 int r;
1094 isc_result_t result;
1095 isc_netaddr_t addr;
1096
1097 addr.family = AF_INET6;
1098 r = inet_pton(AF_INET6, value, &addr.type.in6);
1099 if (r > 0) {
1100 /* This is a valid IPv6 address. */
1101 dns_fixedname_t fname;
1102 dns_name_t *name;
1103 unsigned int options = 0;
1104
1105 name = dns_fixedname_initname(&fname);
1106 result = dns_byaddr_createptrname(&addr, options, name);
1107 CHECK("dns_byaddr_createptrname2", result);
1108 dns_name_format(name, reverse, (unsigned int)len);
1109 return;
1110 } else {
1111 /*
1112 * Not a valid IPv6 address. Assume IPv4.
1113 * Construct the in-addr.arpa name by blindly
1114 * reversing octets whether or not they look like
1115 * integers, so that this can be used for RFC2317
1116 * names and such.
1117 */
1118 char *p = reverse;
1119 char *end = reverse + len;
1120 result = reverse_octets(value, &p, end);
1121 CHECK("reverse_octets", result);
1122 /* Append .in-addr.arpa. and a terminating NUL. */
1123 result = append(".in-addr.arpa.", 15, &p, end);
1124 CHECK("append", result);
1125 return;
1126 }
1127 }
1128
1129 /*%
1130 * We're not using isc_commandline_parse() here since the command line
1131 * syntax of mdig is quite a bit different from that which can be described
1132 * by that routine.
1133 * XXX doc options
1134 */
1135
1136 static void
1137 plus_option(char *option, struct query *query, bool global) {
1138 isc_result_t result;
1139 char *cmd, *value, *last = NULL, *code;
1140 uint32_t num;
1141 bool state = true;
1142 size_t n;
1143
1144 INSIST(option != NULL);
1145
1146 if ((cmd = strtok_r(option, "=", &last)) == NULL) {
1147 printf(";; Invalid option %s\n", option);
1148 return;
1149 }
1150 if (strncasecmp(cmd, "no", 2) == 0) {
1151 cmd += 2;
1152 state = false;
1153 }
1154 /* parse the rest of the string */
1155 value = strtok_r(NULL, "", &last);
1156
1157 #define FULLCHECK(A) \
1158 do { \
1159 size_t _l = strlen(cmd); \
1160 if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
1161 goto invalid_option; \
1162 } while (0)
1163 #define FULLCHECK2(A, B) \
1164 do { \
1165 size_t _l = strlen(cmd); \
1166 if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
1167 (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \
1168 goto invalid_option; \
1169 } while (0)
1170 #define GLOBAL() \
1171 do { \
1172 if (!global) \
1173 goto global_option; \
1174 } while (0)
1175
1176 switch (cmd[0]) {
1177 case 'a':
1178 switch (cmd[1]) {
1179 case 'a': /* aaonly / aaflag */
1180 FULLCHECK2("aaonly", "aaflag");
1181 query->have_aaonly = state;
1182 break;
1183 case 'd':
1184 switch (cmd[2]) {
1185 case 'd': /* additional */
1186 FULLCHECK("additional");
1187 display_additional = state;
1188 break;
1189 case 'f': /* adflag */
1190 case '\0': /* +ad is a synonym for +adflag */
1191 FULLCHECK("adflag");
1192 query->have_adflag = state;
1193 break;
1194 default:
1195 goto invalid_option;
1196 }
1197 break;
1198 case 'l': /* all */
1199 FULLCHECK("all");
1200 GLOBAL();
1201 display_question = state;
1202 display_answer = state;
1203 display_authority = state;
1204 display_additional = state;
1205 display_comments = state;
1206 display_rrcomments = state ? 1 : -1;
1207 break;
1208 case 'n': /* answer */
1209 FULLCHECK("answer");
1210 GLOBAL();
1211 display_answer = state;
1212 break;
1213 case 'u': /* authority */
1214 FULLCHECK("authority");
1215 GLOBAL();
1216 display_authority = state;
1217 break;
1218 default:
1219 goto invalid_option;
1220 }
1221 break;
1222 case 'b':
1223 switch (cmd[1]) {
1224 case 'e': /* besteffort */
1225 FULLCHECK("besteffort");
1226 GLOBAL();
1227 besteffort = state;
1228 break;
1229 case 'u': /* bufsize */
1230 FULLCHECK("bufsize");
1231 if (value == NULL) {
1232 goto need_value;
1233 }
1234 if (!state) {
1235 goto invalid_option;
1236 }
1237 result = parse_uint(&num, value, COMMSIZE,
1238 "buffer size");
1239 CHECK("parse_uint(buffer size)", result);
1240 query->udpsize = num;
1241 break;
1242 default:
1243 goto invalid_option;
1244 }
1245 break;
1246 case 'c':
1247 switch (cmd[1]) {
1248 case 'd': /* cdflag */
1249 switch (cmd[2]) {
1250 case 'f': /* cdflag */
1251 case '\0': /* +cd is a synonym for +cdflag */
1252 FULLCHECK("cdflag");
1253 query->have_cdflag = state;
1254 break;
1255 default:
1256 goto invalid_option;
1257 }
1258 break;
1259 case 'l': /* cl */
1260 FULLCHECK("cl");
1261 GLOBAL();
1262 display_class = state;
1263 break;
1264 case 'o': /* comments */
1265 switch (cmd[2]) {
1266 case 'm':
1267 FULLCHECK("comments");
1268 GLOBAL();
1269 display_comments = state;
1270 break;
1271 case 'n':
1272 FULLCHECK("continue");
1273 GLOBAL();
1274 continue_on_error = state;
1275 break;
1276 case 'o':
1277 FULLCHECK("cookie");
1278 if (state && query->edns == -1) {
1279 query->edns = 0;
1280 }
1281 query->send_cookie = state;
1282 if (value != NULL) {
1283 n = strlcpy(hexcookie, value,
1284 sizeof(hexcookie));
1285 if (n >= sizeof(hexcookie)) {
1286 fatal("COOKIE data too large");
1287 }
1288 query->cookie = hexcookie;
1289 } else {
1290 query->cookie = NULL;
1291 }
1292 break;
1293 default:
1294 goto invalid_option;
1295 }
1296 break;
1297 case 'r':
1298 FULLCHECK("crypto");
1299 GLOBAL();
1300 display_crypto = state;
1301 break;
1302 default:
1303 goto invalid_option;
1304 }
1305 break;
1306 case 'd':
1307 switch (cmd[1]) {
1308 case 'n': /* dnssec */
1309 FULLCHECK("dnssec");
1310 if (state && query->edns == -1) {
1311 query->edns = 0;
1312 }
1313 query->dnssec = state;
1314 break;
1315 case 's': /* dscp */
1316 FULLCHECK("dscp");
1317 GLOBAL();
1318 if (!state) {
1319 dscp = -1;
1320 break;
1321 }
1322 if (value == NULL) {
1323 goto need_value;
1324 }
1325 result = parse_uint(&num, value, 0x3f, "DSCP");
1326 CHECK("parse_uint(DSCP)", result);
1327 dscp = num;
1328 break;
1329 default:
1330 goto invalid_option;
1331 }
1332 break;
1333 case 'e':
1334 switch (cmd[1]) {
1335 case 'd':
1336 switch (cmd[2]) {
1337 case 'n':
1338 switch (cmd[3]) {
1339 case 's':
1340 switch (cmd[4]) {
1341 case 0:
1342 FULLCHECK("edns");
1343 if (!state) {
1344 query->edns = -1;
1345 break;
1346 }
1347 if (value == NULL) {
1348 query->edns = 0;
1349 break;
1350 }
1351 result = parse_uint(&num, value,
1352 255,
1353 "edns");
1354 CHECK("parse_uint(edns)",
1355 result);
1356 query->edns = num;
1357 break;
1358 case 'f':
1359 FULLCHECK("ednsflags");
1360 if (!state) {
1361 query->ednsflags = 0;
1362 break;
1363 }
1364 if (value == NULL) {
1365 query->ednsflags = 0;
1366 break;
1367 }
1368 result = parse_xint(
1369 &num, value, 0xffff,
1370 "ednsflags");
1371 CHECK("parse_xint(ednsflags)",
1372 result);
1373 query->ednsflags = num;
1374 break;
1375 case 'o':
1376 FULLCHECK("ednsopt");
1377 if (!state) {
1378 query->ednsoptscnt = 0;
1379 break;
1380 }
1381 code = NULL;
1382 if (value != NULL) {
1383 code = strtok_r(value,
1384 ":",
1385 &last);
1386 }
1387 if (code == NULL) {
1388 fatal("ednsopt no "
1389 "code point "
1390 "specified");
1391 }
1392 value = strtok_r(NULL, "\0",
1393 &last);
1394 save_opt(query, code, value);
1395 break;
1396 default:
1397 goto invalid_option;
1398 }
1399 break;
1400 default:
1401 goto invalid_option;
1402 }
1403 break;
1404 default:
1405 goto invalid_option;
1406 }
1407 break;
1408 case 'x':
1409 FULLCHECK("expire");
1410 query->expire = state;
1411 break;
1412 default:
1413 goto invalid_option;
1414 }
1415 break;
1416 case 'm': /* multiline */
1417 FULLCHECK("multiline");
1418 GLOBAL();
1419 display_multiline = state;
1420 break;
1421 case 'n':
1422 FULLCHECK("nsid");
1423 if (state && query->edns == -1) {
1424 query->edns = 0;
1425 }
1426 query->nsid = state;
1427 break;
1428 case 'q':
1429 FULLCHECK("question");
1430 GLOBAL();
1431 display_question = state;
1432 break;
1433 case 'r':
1434 switch (cmd[1]) {
1435 case 'e':
1436 switch (cmd[2]) {
1437 case 'c': /* recurse */
1438 FULLCHECK("recurse");
1439 query->recurse = state;
1440 break;
1441 case 't': /* retry / retries */
1442 FULLCHECK2("retry", "retries");
1443 if (value == NULL) {
1444 goto need_value;
1445 }
1446 if (!state) {
1447 goto invalid_option;
1448 }
1449 result = parse_uint(&query->udpretries, value,
1450 MAXTRIES - 1, "udpretries");
1451 CHECK("parse_uint(udpretries)", result);
1452 query->udpretries++;
1453 break;
1454 default:
1455 goto invalid_option;
1456 }
1457 break;
1458 case 'r':
1459 FULLCHECK("rrcomments");
1460 GLOBAL();
1461 display_rrcomments = state ? 1 : -1;
1462 break;
1463 default:
1464 goto invalid_option;
1465 }
1466 break;
1467 case 's':
1468 switch (cmd[1]) {
1469 case 'h':
1470 FULLCHECK("short");
1471 GLOBAL();
1472 display_short_form = state;
1473 if (state) {
1474 display_question = false;
1475 display_answer = true;
1476 display_authority = false;
1477 display_additional = false;
1478 display_comments = false;
1479 display_rrcomments = -1;
1480 }
1481 break;
1482 case 'p': /* split */
1483 FULLCHECK("split");
1484 GLOBAL();
1485 if (value != NULL && !state) {
1486 goto invalid_option;
1487 }
1488 if (!state) {
1489 display_splitwidth = 0;
1490 break;
1491 } else if (value == NULL) {
1492 break;
1493 }
1494
1495 result = parse_uint(&display_splitwidth, value, 1023,
1496 "split");
1497 if ((display_splitwidth % 4) != 0) {
1498 display_splitwidth =
1499 ((display_splitwidth + 3) / 4) * 4;
1500 fprintf(stderr,
1501 ";; Warning, split must be "
1502 "a multiple of 4; adjusting "
1503 "to %u\n",
1504 display_splitwidth);
1505 }
1506 /*
1507 * There is an adjustment done in the
1508 * totext_<rrtype>() functions which causes
1509 * splitwidth to shrink. This is okay when we're
1510 * using the default width but incorrect in this
1511 * case, so we correct for it
1512 */
1513 if (display_splitwidth) {
1514 display_splitwidth += 3;
1515 }
1516 CHECK("parse_uint(split)", result);
1517 break;
1518 case 'u': /* subnet */
1519 FULLCHECK("subnet");
1520 if (state && value == NULL) {
1521 goto need_value;
1522 }
1523 if (!state) {
1524 if (query->ecs_addr != NULL) {
1525 isc_mem_free(mctx, query->ecs_addr);
1526 query->ecs_addr = NULL;
1527 }
1528 break;
1529 }
1530 if (query->edns == -1) {
1531 query->edns = 0;
1532 }
1533 result = parse_netprefix(&query->ecs_addr, value);
1534 CHECK("parse_netprefix", result);
1535 break;
1536 default:
1537 goto invalid_option;
1538 }
1539 break;
1540 case 't':
1541 switch (cmd[1]) {
1542 case 'c': /* tcp */
1543 FULLCHECK("tcp");
1544 GLOBAL();
1545 tcp_mode = state;
1546 break;
1547 case 'i': /* timeout */
1548 FULLCHECK("timeout");
1549 if (value == NULL) {
1550 goto need_value;
1551 }
1552 if (!state) {
1553 goto invalid_option;
1554 }
1555 result = parse_uint(&query->timeout, value, MAXTIMEOUT,
1556 "timeout");
1557 CHECK("parse_uint(timeout)", result);
1558 if (query->timeout == 0) {
1559 query->timeout = 1;
1560 }
1561 break;
1562 case 'r':
1563 FULLCHECK("tries");
1564 if (value == NULL) {
1565 goto need_value;
1566 }
1567 if (!state) {
1568 goto invalid_option;
1569 }
1570 result = parse_uint(&query->udpretries, value, MAXTRIES,
1571 "udpretries");
1572 CHECK("parse_uint(udpretries)", result);
1573 if (query->udpretries == 0) {
1574 query->udpretries = 1;
1575 }
1576 break;
1577 case 't':
1578 switch (cmd[2]) {
1579 case 'l':
1580 switch (cmd[3]) {
1581 case 0:
1582 case 'i': /* ttlid */
1583 FULLCHECK2("ttl", "ttlid");
1584 GLOBAL();
1585 display_ttl = state;
1586 break;
1587 case 'u': /* ttlunits */
1588 FULLCHECK("ttlunits");
1589 GLOBAL();
1590 display_ttl = true;
1591 display_ttlunits = state;
1592 break;
1593 default:
1594 goto invalid_option;
1595 }
1596 break;
1597 default:
1598 goto invalid_option;
1599 }
1600 break;
1601 default:
1602 goto invalid_option;
1603 }
1604 break;
1605 case 'u':
1606 switch (cmd[1]) {
1607 case 'd':
1608 FULLCHECK("udptimeout");
1609 if (value == NULL) {
1610 goto need_value;
1611 }
1612 if (!state) {
1613 goto invalid_option;
1614 }
1615 result = parse_uint(&query->udptimeout, value,
1616 MAXTIMEOUT, "udptimeout");
1617 CHECK("parse_uint(udptimeout)", result);
1618 break;
1619 case 'n':
1620 FULLCHECK("unknownformat");
1621 display_unknown_format = state;
1622 break;
1623 default:
1624 goto invalid_option;
1625 }
1626 break;
1627 case 'v':
1628 FULLCHECK("vc");
1629 GLOBAL();
1630 tcp_mode = state;
1631 break;
1632 case 'y': /* yaml */
1633 FULLCHECK("yaml");
1634 yaml = state;
1635 if (state) {
1636 display_rrcomments = state;
1637 }
1638 break;
1639 case 'z': /* zflag */
1640 FULLCHECK("zflag");
1641 query->have_zflag = state;
1642 break;
1643 global_option:
1644 fprintf(stderr, "Ignored late global option: +%s\n", option);
1645 break;
1646 default:
1647 invalid_option:
1648 need_value:
1649 fprintf(stderr, "Invalid option: +%s\n", option);
1650 usage();
1651 }
1652 return;
1653 }
1654
1655 /*%
1656 * #true returned if value was used
1657 */
1658 static const char *single_dash_opts = "46himv";
1659 static const char *dash_opts = "46bcfhiptvx";
1660 static bool
1661 dash_option(const char *option, char *next, struct query *query, bool global,
1662 bool *setname) {
1663 char opt;
1664 const char *value;
1665 isc_result_t result;
1666 bool value_from_next;
1667 isc_consttextregion_t tr;
1668 dns_rdatatype_t rdtype;
1669 dns_rdataclass_t rdclass;
1670 char textname[MXNAME];
1671 struct in_addr in4;
1672 struct in6_addr in6;
1673 in_port_t srcport;
1674 char *hash;
1675 uint32_t num;
1676
1677 while (strpbrk(option, single_dash_opts) == &option[0]) {
1678 /*
1679 * Since the -[46hiv] options do not take an argument,
1680 * account for them (in any number and/or combination)
1681 * if they appear as the first character(s) of an opt.
1682 */
1683 opt = option[0];
1684 switch (opt) {
1685 case '4':
1686 GLOBAL();
1687 if (have_ipv4) {
1688 isc_net_disableipv6();
1689 have_ipv6 = false;
1690 } else {
1691 fatal("can't find IPv4 networking");
1692 /* NOTREACHED */
1693 return (false);
1694 }
1695 break;
1696 case '6':
1697 GLOBAL();
1698 if (have_ipv6) {
1699 isc_net_disableipv4();
1700 have_ipv4 = false;
1701 } else {
1702 fatal("can't find IPv6 networking");
1703 /* NOTREACHED */
1704 return (false);
1705 }
1706 break;
1707 case 'h':
1708 help();
1709 exit(0);
1710 break;
1711 case 'i':
1712 /* deprecated */
1713 break;
1714 case 'm':
1715 /*
1716 * handled by preparse_args()
1717 */
1718 break;
1719 case 'v':
1720 fputs("mDiG " VERSION "\n", stderr);
1721 exit(0);
1722 break;
1723 }
1724 if (strlen(option) > 1U) {
1725 option = &option[1];
1726 } else {
1727 return (false);
1728 }
1729 }
1730 opt = option[0];
1731 if (strlen(option) > 1U) {
1732 value_from_next = false;
1733 value = &option[1];
1734 } else {
1735 value_from_next = true;
1736 value = next;
1737 }
1738 if (value == NULL) {
1739 goto invalid_option;
1740 }
1741 switch (opt) {
1742 case 'b':
1743 GLOBAL();
1744 hash = strchr(value, '#');
1745 if (hash != NULL) {
1746 result = parse_uint(&num, hash + 1, MAXPORT,
1747 "port number");
1748 CHECK("parse_uint(srcport)", result);
1749 srcport = num;
1750 *hash = '\0';
1751 } else {
1752 srcport = 0;
1753 }
1754 if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) {
1755 isc_sockaddr_fromin6(&srcaddr, &in6, srcport);
1756 isc_net_disableipv4();
1757 } else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) {
1758 isc_sockaddr_fromin(&srcaddr, &in4, srcport);
1759 isc_net_disableipv6();
1760 } else {
1761 if (hash != NULL) {
1762 *hash = '#';
1763 }
1764 fatal("invalid address %s", value);
1765 }
1766 if (hash != NULL) {
1767 *hash = '#';
1768 }
1769 have_src = true;
1770 return (value_from_next);
1771 case 'c':
1772 tr.base = value;
1773 tr.length = strlen(value);
1774 result = dns_rdataclass_fromtext(&rdclass,
1775 (isc_textregion_t *)&tr);
1776 CHECK("dns_rdataclass_fromtext", result);
1777 query->rdclass = rdclass;
1778 return (value_from_next);
1779 case 'f':
1780 batchname = value;
1781 return (value_from_next);
1782 case 'p':
1783 GLOBAL();
1784 result = parse_uint(&num, value, MAXPORT, "port number");
1785 CHECK("parse_uint(port)", result);
1786 port = num;
1787 return (value_from_next);
1788 case 't':
1789 tr.base = value;
1790 tr.length = strlen(value);
1791 result = dns_rdatatype_fromtext(&rdtype,
1792 (isc_textregion_t *)&tr);
1793 CHECK("dns_rdatatype_fromtext", result);
1794 query->rdtype = rdtype;
1795 return (value_from_next);
1796 case 'x':
1797 get_reverse(textname, sizeof(textname), value);
1798 strlcpy(query->textname, textname, sizeof(query->textname));
1799 query->rdtype = dns_rdatatype_ptr;
1800 query->rdclass = dns_rdataclass_in;
1801 *setname = true;
1802 return (value_from_next);
1803 global_option:
1804 fprintf(stderr, "Ignored late global option: -%s\n", option);
1805 usage();
1806 default:
1807 invalid_option:
1808 fprintf(stderr, "Invalid option: -%s\n", option);
1809 usage();
1810 }
1811 /* NOTREACHED */
1812 return (false);
1813 }
1814
1815 static struct query *
1816 clone_default_query() {
1817 struct query *query;
1818
1819 query = isc_mem_allocate(mctx, sizeof(struct query));
1820 memmove(query, &default_query, sizeof(struct query));
1821 if (default_query.ecs_addr != NULL) {
1822 size_t len = sizeof(isc_sockaddr_t);
1823
1824 query->ecs_addr = isc_mem_allocate(mctx, len);
1825 memmove(query->ecs_addr, default_query.ecs_addr, len);
1826 }
1827
1828 if (query->timeout == 0) {
1829 query->timeout = tcp_mode ? TCPTIMEOUT : UDPTIMEOUT;
1830 }
1831
1832 return (query);
1833 }
1834
1835 /*%
1836 * Because we may be trying to do memory allocation recording, we're going
1837 * to need to parse the arguments for the -m *before* we start the main
1838 * argument parsing routine.
1839 *
1840 * I'd prefer not to have to do this, but I am not quite sure how else to
1841 * fix the problem. Argument parsing in mdig involves memory allocation
1842 * by its nature, so it can't be done in the main argument parser.
1843 */
1844 static void
1845 preparse_args(int argc, char **argv) {
1846 int rc;
1847 char **rv;
1848 char *option;
1849 bool ipv4only = false, ipv6only = false;
1850
1851 rc = argc;
1852 rv = argv;
1853 for (rc--, rv++; rc > 0; rc--, rv++) {
1854 if (rv[0][0] != '-') {
1855 continue;
1856 }
1857 option = &rv[0][1];
1858 while (strpbrk(option, single_dash_opts) == &option[0]) {
1859 switch (option[0]) {
1860 case 'm':
1861 isc_mem_debugging = ISC_MEM_DEBUGTRACE |
1862 ISC_MEM_DEBUGRECORD;
1863 break;
1864 case '4':
1865 if (ipv6only) {
1866 fatal("only one of -4 and -6 allowed");
1867 }
1868 ipv4only = true;
1869 break;
1870 case '6':
1871 if (ipv4only) {
1872 fatal("only one of -4 and -6 allowed");
1873 }
1874 ipv6only = true;
1875 break;
1876 }
1877 option = &option[1];
1878 }
1879 if (strlen(option) == 0U) {
1880 continue;
1881 }
1882 /* Look for dash value option. */
1883 if (strpbrk(option, dash_opts) != &option[0] ||
1884 strlen(option) > 1U) {
1885 /* Error or value in option. */
1886 continue;
1887 }
1888 /* Dash value is next argument so we need to skip it. */
1889 rc--, rv++;
1890 /* Handle missing argument */
1891 if (rc == 0) {
1892 break;
1893 }
1894 }
1895 }
1896
1897 static void
1898 parse_args(bool is_batchfile, int argc, char **argv) {
1899 struct query *query = NULL;
1900 char batchline[MXNAME];
1901 int bargc;
1902 char *bargv[64];
1903 int rc;
1904 char **rv;
1905 bool global = true;
1906 char *last;
1907
1908 /*
1909 * The semantics for parsing the args is a bit complex; if
1910 * we don't have a host yet, make the arg apply globally,
1911 * otherwise make it apply to the latest host. This is
1912 * a bit different than the previous versions, but should
1913 * form a consistent user interface.
1914 *
1915 * First, create a "default query" which won't actually be used
1916 * anywhere, except for cloning into new queries
1917 */
1918
1919 if (!is_batchfile) {
1920 default_query.textname[0] = 0;
1921 default_query.recurse = true;
1922 default_query.have_aaonly = false;
1923 default_query.have_adflag = true; /*XXX*/
1924 default_query.have_cdflag = false;
1925 default_query.have_zflag = false;
1926 default_query.dnssec = false;
1927 default_query.expire = false;
1928 default_query.send_cookie = false;
1929 default_query.cookie = NULL;
1930 default_query.nsid = false;
1931 default_query.rdtype = dns_rdatatype_a;
1932 default_query.rdclass = dns_rdataclass_in;
1933 default_query.udpsize = 0;
1934 default_query.edns = 0; /*XXX*/
1935 default_query.ednsopts = NULL;
1936 default_query.ednsoptscnt = 0;
1937 default_query.ednsflags = 0;
1938 default_query.ecs_addr = NULL;
1939 default_query.timeout = 0;
1940 default_query.udptimeout = 0;
1941 default_query.udpretries = 3;
1942 ISC_LINK_INIT(&default_query, link);
1943 }
1944
1945 if (is_batchfile) {
1946 /* Processing '-f batchfile'. */
1947 query = clone_default_query();
1948 global = false;
1949 } else {
1950 query = &default_query;
1951 }
1952
1953 rc = argc;
1954 rv = argv;
1955 for (rc--, rv++; rc > 0; rc--, rv++) {
1956 if (strncmp(rv[0], "%", 1) == 0) {
1957 break;
1958 }
1959 if (rv[0][0] == '@') {
1960 if (server != NULL) {
1961 fatal("server already set to @%s", server);
1962 }
1963 server = &rv[0][1];
1964 } else if (rv[0][0] == '+') {
1965 plus_option(&rv[0][1], query, global);
1966 } else if (rv[0][0] == '-') {
1967 bool setname = false;
1968
1969 if (rc <= 1) {
1970 if (dash_option(&rv[0][1], NULL, query, global,
1971 &setname)) {
1972 rc--;
1973 rv++;
1974 }
1975 } else {
1976 if (dash_option(&rv[0][1], rv[1], query, global,
1977 &setname)) {
1978 rc--;
1979 rv++;
1980 }
1981 }
1982 if (setname) {
1983 if (query == &default_query) {
1984 query = clone_default_query();
1985 }
1986 ISC_LIST_APPEND(queries, query, link);
1987
1988 default_query.textname[0] = 0;
1989 query = clone_default_query();
1990 global = false;
1991 }
1992 } else {
1993 /*
1994 * Anything which isn't an option
1995 */
1996 if (query == &default_query) {
1997 query = clone_default_query();
1998 }
1999 strlcpy(query->textname, rv[0],
2000 sizeof(query->textname));
2001 ISC_LIST_APPEND(queries, query, link);
2002
2003 query = clone_default_query();
2004 global = false;
2005 /* XXX Error message */
2006 }
2007 }
2008
2009 /*
2010 * If we have a batchfile, read the query list from it.
2011 */
2012 if ((batchname != NULL) && !is_batchfile) {
2013 if (strcmp(batchname, "-") == 0) {
2014 batchfp = stdin;
2015 } else {
2016 batchfp = fopen(batchname, "r");
2017 }
2018 if (batchfp == NULL) {
2019 perror(batchname);
2020 fatal("couldn't open batch file '%s'", batchname);
2021 }
2022 while (fgets(batchline, sizeof(batchline), batchfp) != 0) {
2023 if (batchline[0] == '\r' || batchline[0] == '\n' ||
2024 batchline[0] == '#' || batchline[0] == ';')
2025 {
2026 continue;
2027 }
2028 for (bargc = 1, bargv[bargc] = strtok_r(
2029 batchline, " \t\r\n", &last);
2030 (bargc < 14) && bargv[bargc]; bargc++,
2031 bargv[bargc] = strtok_r(NULL, " \t\r\n", &last))
2032 {
2033 /* empty body */
2034 }
2035
2036 bargv[0] = argv[0];
2037 parse_args(true, bargc, (char **)bargv);
2038 }
2039 if (batchfp != stdin) {
2040 fclose(batchfp);
2041 }
2042 }
2043 if (query != &default_query) {
2044 if (query->ecs_addr != NULL) {
2045 isc_mem_free(mctx, query->ecs_addr);
2046 }
2047 isc_mem_free(mctx, query);
2048 }
2049 }
2050
2051 /*% Main processing routine for mdig */
2052 int
2053 main(int argc, char *argv[]) {
2054 struct query *query;
2055 isc_result_t result;
2056 isc_sockaddr_t bind_any;
2057 isc_log_t *lctx;
2058 isc_logconfig_t *lcfg;
2059 isc_taskmgr_t *taskmgr;
2060 isc_task_t *task;
2061 isc_timermgr_t *timermgr;
2062 isc_socketmgr_t *socketmgr;
2063 dns_dispatchmgr_t *dispatchmgr;
2064 unsigned int attrs, attrmask;
2065 dns_dispatch_t *dispatchvx;
2066 dns_view_t *view;
2067 int ns;
2068 unsigned int i;
2069
2070 RUNCHECK(isc_app_start());
2071
2072 dns_result_register();
2073
2074 if (isc_net_probeipv4() == ISC_R_SUCCESS) {
2075 have_ipv4 = true;
2076 }
2077 if (isc_net_probeipv6() == ISC_R_SUCCESS) {
2078 have_ipv6 = true;
2079 }
2080 if (!have_ipv4 && !have_ipv6) {
2081 fatal("could not find either IPv4 or IPv6");
2082 }
2083
2084 preparse_args(argc, argv);
2085
2086 mctx = NULL;
2087 isc_mem_create(&mctx);
2088
2089 lctx = NULL;
2090 lcfg = NULL;
2091 isc_log_create(mctx, &lctx, &lcfg);
2092
2093 RUNCHECK(dst_lib_init(mctx, NULL));
2094 isc_nonce_buf(cookie_secret, sizeof(cookie_secret));
2095
2096 ISC_LIST_INIT(queries);
2097 parse_args(false, argc, argv);
2098 if (server == NULL) {
2099 fatal("a server '@xxx' is required");
2100 }
2101
2102 ns = 0;
2103 result = bind9_getaddresses(server, port, &dstaddr, 1, &ns);
2104 if (result != ISC_R_SUCCESS) {
2105 fatal("couldn't get address for '%s': %s", server,
2106 isc_result_totext(result));
2107 }
2108
2109 if (isc_sockaddr_pf(&dstaddr) == PF_INET && have_ipv6) {
2110 isc_net_disableipv6();
2111 have_ipv6 = false;
2112 } else if (isc_sockaddr_pf(&dstaddr) == PF_INET6 && have_ipv4) {
2113 isc_net_disableipv4();
2114 have_ipv4 = false;
2115 }
2116 if (have_ipv4 && have_ipv6) {
2117 fatal("can't choose between IPv4 and IPv6");
2118 }
2119
2120 taskmgr = NULL;
2121 RUNCHECK(isc_taskmgr_create(mctx, 1, 0, NULL, &taskmgr));
2122 task = NULL;
2123 RUNCHECK(isc_task_create(taskmgr, 0, &task));
2124 timermgr = NULL;
2125
2126 RUNCHECK(isc_timermgr_create(mctx, &timermgr));
2127 socketmgr = NULL;
2128 RUNCHECK(isc_socketmgr_create(mctx, &socketmgr));
2129 dispatchmgr = NULL;
2130 RUNCHECK(dns_dispatchmgr_create(mctx, &dispatchmgr));
2131
2132 attrs = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_MAKEQUERY;
2133 if (have_ipv4) {
2134 isc_sockaddr_any(&bind_any);
2135 attrs |= DNS_DISPATCHATTR_IPV4;
2136 } else {
2137 isc_sockaddr_any6(&bind_any);
2138 attrs |= DNS_DISPATCHATTR_IPV6;
2139 }
2140 attrmask = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP |
2141 DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
2142 dispatchvx = NULL;
2143 RUNCHECK(dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
2144 have_src ? &srcaddr : &bind_any, 4096, 100,
2145 100, 17, 19, attrs, attrmask,
2146 &dispatchvx));
2147 requestmgr = NULL;
2148 RUNCHECK(dns_requestmgr_create(
2149 mctx, timermgr, socketmgr, taskmgr, dispatchmgr,
2150 have_ipv4 ? dispatchvx : NULL, have_ipv6 ? dispatchvx : NULL,
2151 &requestmgr));
2152
2153 view = NULL;
2154 RUNCHECK(dns_view_create(mctx, 0, "_test", &view));
2155
2156 query = ISC_LIST_HEAD(queries);
2157 RUNCHECK(isc_app_onrun(mctx, task, sendqueries, query));
2158
2159 (void)isc_app_run();
2160
2161 query = ISC_LIST_HEAD(queries);
2162 while (query != NULL) {
2163 struct query *next = ISC_LIST_NEXT(query, link);
2164
2165 if (query->ednsopts != NULL) {
2166 for (i = 0; i < EDNSOPTS; i++) {
2167 if (query->ednsopts[i].value != NULL) {
2168 isc_mem_free(mctx,
2169 query->ednsopts[i].value);
2170 }
2171 }
2172 isc_mem_free(mctx, query->ednsopts);
2173 }
2174 if (query->ecs_addr != NULL) {
2175 isc_mem_free(mctx, query->ecs_addr);
2176 query->ecs_addr = NULL;
2177 }
2178 isc_mem_free(mctx, query);
2179 query = next;
2180 }
2181
2182 if (default_query.ecs_addr != NULL) {
2183 isc_mem_free(mctx, default_query.ecs_addr);
2184 }
2185
2186 dns_view_detach(&view);
2187
2188 dns_requestmgr_shutdown(requestmgr);
2189 dns_requestmgr_detach(&requestmgr);
2190
2191 dns_dispatch_detach(&dispatchvx);
2192 dns_dispatchmgr_destroy(&dispatchmgr);
2193
2194 isc_socketmgr_destroy(&socketmgr);
2195 isc_timermgr_destroy(&timermgr);
2196
2197 isc_task_shutdown(task);
2198 isc_task_detach(&task);
2199 isc_taskmgr_destroy(&taskmgr);
2200
2201 dst_lib_destroy();
2202
2203 isc_log_destroy(&lctx);
2204
2205 isc_mem_destroy(&mctx);
2206
2207 isc_app_finish();
2208
2209 return (0);
2210 }
2211