1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * SPDX-License-Identifier: MPL-2.0
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 https://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 <unistd.h>
18
19 #include <isc/app.h>
20 #include <isc/buffer.h>
21 #include <isc/commandline.h>
22 #include <isc/event.h>
23 #include <isc/netaddr.h>
24 #include <isc/parseint.h>
25 #include <isc/print.h>
26 #include <isc/string.h>
27 #include <isc/task.h>
28 #include <isc/util.h>
29
30 #include <dns/byaddr.h>
31 #include <dns/fixedname.h>
32 #include <dns/message.h>
33 #include <dns/name.h>
34 #include <dns/rdata.h>
35 #include <dns/rdataclass.h>
36 #include <dns/rdataset.h>
37 #include <dns/rdatastruct.h>
38 #include <dns/rdatatype.h>
39
40 #include <dig/dig.h>
41
42 #if defined(HAVE_READLINE)
43 #if defined(HAVE_EDIT_READLINE_READLINE_H)
44 #include <edit/readline/readline.h>
45 #if defined(HAVE_EDIT_READLINE_HISTORY_H)
46 #include <edit/readline/history.h>
47 #endif /* if defined(HAVE_EDIT_READLINE_HISTORY_H) */
48 #elif defined(HAVE_EDITLINE_READLINE_H)
49 #include <editline/readline.h>
50 #elif defined(HAVE_READLINE_READLINE_H)
51 /* Prevent deprecated functions being declared. */
52 #define _FUNCTION_DEF 1
53 /* Ensure rl_message() gets prototype. */
54 #define USE_VARARGS 1
55 #define PREFER_STDARG 1
56 #include <readline/readline.h>
57 #if defined(HAVE_READLINE_HISTORY_H)
58 #include <readline/history.h>
59 #endif /* if defined(HAVE_READLINE_HISTORY_H) */
60 #endif /* if defined(HAVE_EDIT_READLINE_READLINE_H) */
61 #endif /* if defined(HAVE_READLINE) */
62
63 static bool short_form = true, tcpmode = false, tcpmode_set = false,
64 identify = false, stats = true, comments = true,
65 section_question = true, section_answer = true,
66 section_authority = true, section_additional = true, recurse = true,
67 aaonly = false, nofail = true, default_lookups = true,
68 a_noanswer = false;
69
70 static bool interactive;
71
72 static bool in_use = false;
73 static char defclass[MXRD] = "IN";
74 static char deftype[MXRD] = "A";
75 static isc_event_t *global_event = NULL;
76 static int query_error = 1, print_error = 0;
77
78 static char domainopt[DNS_NAME_MAXTEXT];
79
80 static const char *rcodetext[] = { "NOERROR", "FORMERR", "SERVFAIL",
81 "NXDOMAIN", "NOTIMP", "REFUSED",
82 "YXDOMAIN", "YXRRSET", "NXRRSET",
83 "NOTAUTH", "NOTZONE", "RESERVED11",
84 "RESERVED12", "RESERVED13", "RESERVED14",
85 "RESERVED15", "BADVERS" };
86
87 static const char *rtypetext[] = {
88 "rtype_0 = ", /* 0 */
89 "internet address = ", /* 1 */
90 "nameserver = ", /* 2 */
91 "md = ", /* 3 */
92 "mf = ", /* 4 */
93 "canonical name = ", /* 5 */
94 "soa = ", /* 6 */
95 "mb = ", /* 7 */
96 "mg = ", /* 8 */
97 "mr = ", /* 9 */
98 "rtype_10 = ", /* 10 */
99 "protocol = ", /* 11 */
100 "name = ", /* 12 */
101 "hinfo = ", /* 13 */
102 "minfo = ", /* 14 */
103 "mail exchanger = ", /* 15 */
104 "text = ", /* 16 */
105 "rp = ", /* 17 */
106 "afsdb = ", /* 18 */
107 "x25 address = ", /* 19 */
108 "isdn address = ", /* 20 */
109 "rt = ", /* 21 */
110 "nsap = ", /* 22 */
111 "nsap_ptr = ", /* 23 */
112 "signature = ", /* 24 */
113 "key = ", /* 25 */
114 "px = ", /* 26 */
115 "gpos = ", /* 27 */
116 "has AAAA address ", /* 28 */
117 "loc = ", /* 29 */
118 "next = ", /* 30 */
119 "rtype_31 = ", /* 31 */
120 "rtype_32 = ", /* 32 */
121 "service = ", /* 33 */
122 "rtype_34 = ", /* 34 */
123 "naptr = ", /* 35 */
124 "kx = ", /* 36 */
125 "cert = ", /* 37 */
126 "v6 address = ", /* 38 */
127 "dname = ", /* 39 */
128 "rtype_40 = ", /* 40 */
129 "optional = " /* 41 */
130 };
131
132 #define N_KNOWN_RRTYPES (sizeof(rtypetext) / sizeof(rtypetext[0]))
133
134 static void
135 flush_lookup_list(void);
136 static void
137 getinput(isc_task_t *task, isc_event_t *event);
138
139 static char *
rcode_totext(dns_rcode_t rcode)140 rcode_totext(dns_rcode_t rcode) {
141 static char buf[sizeof("?65535")];
142 union {
143 const char *consttext;
144 char *deconsttext;
145 } totext;
146
147 if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) {
148 snprintf(buf, sizeof(buf), "?%u", rcode);
149 totext.deconsttext = buf;
150 } else {
151 totext.consttext = rcodetext[rcode];
152 }
153 return (totext.deconsttext);
154 }
155
156 static void
query_finished(void)157 query_finished(void) {
158 isc_event_t *event = global_event;
159
160 flush_lookup_list();
161 debug("dighost_shutdown()");
162
163 if (!in_use) {
164 isc_app_shutdown();
165 return;
166 }
167
168 isc_task_send(global_task, &event);
169 }
170
171 static void
printsoa(dns_rdata_t * rdata)172 printsoa(dns_rdata_t *rdata) {
173 dns_rdata_soa_t soa;
174 isc_result_t result;
175 char namebuf[DNS_NAME_FORMATSIZE];
176
177 result = dns_rdata_tostruct(rdata, &soa, NULL);
178 check_result(result, "dns_rdata_tostruct");
179
180 dns_name_format(&soa.origin, namebuf, sizeof(namebuf));
181 printf("\torigin = %s\n", namebuf);
182 dns_name_format(&soa.contact, namebuf, sizeof(namebuf));
183 printf("\tmail addr = %s\n", namebuf);
184 printf("\tserial = %u\n", soa.serial);
185 printf("\trefresh = %u\n", soa.refresh);
186 printf("\tretry = %u\n", soa.retry);
187 printf("\texpire = %u\n", soa.expire);
188 printf("\tminimum = %u\n", soa.minimum);
189 dns_rdata_freestruct(&soa);
190 }
191
192 static void
printaddr(dns_rdata_t * rdata)193 printaddr(dns_rdata_t *rdata) {
194 isc_result_t result;
195 char text[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
196 isc_buffer_t b;
197
198 isc_buffer_init(&b, text, sizeof(text));
199 result = dns_rdata_totext(rdata, NULL, &b);
200 check_result(result, "dns_rdata_totext");
201 printf("Address: %.*s\n", (int)isc_buffer_usedlength(&b),
202 (char *)isc_buffer_base(&b));
203 }
204
205 static void
printrdata(dns_rdata_t * rdata)206 printrdata(dns_rdata_t *rdata) {
207 isc_result_t result;
208 isc_buffer_t *b = NULL;
209 unsigned int size = 1024;
210 bool done = false;
211
212 if (rdata->type < N_KNOWN_RRTYPES) {
213 printf("%s", rtypetext[rdata->type]);
214 } else {
215 printf("rdata_%d = ", rdata->type);
216 }
217
218 while (!done) {
219 isc_buffer_allocate(mctx, &b, size);
220 result = dns_rdata_totext(rdata, NULL, b);
221 if (result == ISC_R_SUCCESS) {
222 printf("%.*s\n", (int)isc_buffer_usedlength(b),
223 (char *)isc_buffer_base(b));
224 done = true;
225 } else if (result != ISC_R_NOSPACE) {
226 check_result(result, "dns_rdata_totext");
227 }
228 isc_buffer_free(&b);
229 size *= 2;
230 }
231 }
232
233 static isc_result_t
printsection(dig_query_t * query,dns_message_t * msg,bool headers,dns_section_t section)234 printsection(dig_query_t *query, dns_message_t *msg, bool headers,
235 dns_section_t section) {
236 isc_result_t result, loopresult;
237 dns_name_t *name;
238 dns_rdataset_t *rdataset = NULL;
239 dns_rdata_t rdata = DNS_RDATA_INIT;
240 char namebuf[DNS_NAME_FORMATSIZE];
241
242 UNUSED(query);
243 UNUSED(headers);
244
245 debug("printsection()");
246
247 result = dns_message_firstname(msg, section);
248 if (result == ISC_R_NOMORE) {
249 return (ISC_R_SUCCESS);
250 } else if (result != ISC_R_SUCCESS) {
251 return (result);
252 }
253 for (;;) {
254 name = NULL;
255 dns_message_currentname(msg, section, &name);
256 for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL;
257 rdataset = ISC_LIST_NEXT(rdataset, link))
258 {
259 loopresult = dns_rdataset_first(rdataset);
260 while (loopresult == ISC_R_SUCCESS) {
261 dns_rdataset_current(rdataset, &rdata);
262 switch (rdata.type) {
263 case dns_rdatatype_a:
264 case dns_rdatatype_aaaa:
265 if (section != DNS_SECTION_ANSWER) {
266 goto def_short_section;
267 }
268 dns_name_format(name, namebuf,
269 sizeof(namebuf));
270 printf("Name:\t%s\n", namebuf);
271 printaddr(&rdata);
272 break;
273 case dns_rdatatype_soa:
274 dns_name_format(name, namebuf,
275 sizeof(namebuf));
276 printf("%s\n", namebuf);
277 printsoa(&rdata);
278 break;
279 default:
280 def_short_section:
281 dns_name_format(name, namebuf,
282 sizeof(namebuf));
283 printf("%s\t", namebuf);
284 printrdata(&rdata);
285 break;
286 }
287 dns_rdata_reset(&rdata);
288 loopresult = dns_rdataset_next(rdataset);
289 }
290 }
291 result = dns_message_nextname(msg, section);
292 if (result == ISC_R_NOMORE) {
293 break;
294 } else if (result != ISC_R_SUCCESS) {
295 return (result);
296 }
297 }
298 return (ISC_R_SUCCESS);
299 }
300
301 static isc_result_t
detailsection(dig_query_t * query,dns_message_t * msg,bool headers,dns_section_t section)302 detailsection(dig_query_t *query, dns_message_t *msg, bool headers,
303 dns_section_t section) {
304 isc_result_t result, loopresult;
305 dns_name_t *name;
306 dns_rdataset_t *rdataset = NULL;
307 dns_rdata_t rdata = DNS_RDATA_INIT;
308 char namebuf[DNS_NAME_FORMATSIZE];
309
310 UNUSED(query);
311
312 debug("detailsection()");
313
314 if (headers) {
315 switch (section) {
316 case DNS_SECTION_QUESTION:
317 puts(" QUESTIONS:");
318 break;
319 case DNS_SECTION_ANSWER:
320 puts(" ANSWERS:");
321 break;
322 case DNS_SECTION_AUTHORITY:
323 puts(" AUTHORITY RECORDS:");
324 break;
325 case DNS_SECTION_ADDITIONAL:
326 puts(" ADDITIONAL RECORDS:");
327 break;
328 }
329 }
330
331 result = dns_message_firstname(msg, section);
332 if (result == ISC_R_NOMORE) {
333 return (ISC_R_SUCCESS);
334 } else if (result != ISC_R_SUCCESS) {
335 return (result);
336 }
337 for (;;) {
338 name = NULL;
339 dns_message_currentname(msg, section, &name);
340 for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL;
341 rdataset = ISC_LIST_NEXT(rdataset, link))
342 {
343 if (section == DNS_SECTION_QUESTION) {
344 dns_name_format(name, namebuf, sizeof(namebuf));
345 printf("\t%s, ", namebuf);
346 dns_rdatatype_format(rdataset->type, namebuf,
347 sizeof(namebuf));
348 printf("type = %s, ", namebuf);
349 dns_rdataclass_format(rdataset->rdclass,
350 namebuf, sizeof(namebuf));
351 printf("class = %s\n", namebuf);
352 }
353 loopresult = dns_rdataset_first(rdataset);
354 while (loopresult == ISC_R_SUCCESS) {
355 dns_rdataset_current(rdataset, &rdata);
356
357 dns_name_format(name, namebuf, sizeof(namebuf));
358 printf(" -> %s\n", namebuf);
359
360 switch (rdata.type) {
361 case dns_rdatatype_soa:
362 printsoa(&rdata);
363 break;
364 default:
365 printf("\t");
366 printrdata(&rdata);
367 }
368 dns_rdata_reset(&rdata);
369 printf("\tttl = %u\n", rdataset->ttl);
370 loopresult = dns_rdataset_next(rdataset);
371 }
372 }
373 result = dns_message_nextname(msg, section);
374 if (result == ISC_R_NOMORE) {
375 break;
376 } else if (result != ISC_R_SUCCESS) {
377 return (result);
378 }
379 }
380 return (ISC_R_SUCCESS);
381 }
382
383 static void
received(unsigned int bytes,isc_sockaddr_t * from,dig_query_t * query)384 received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) {
385 UNUSED(bytes);
386 UNUSED(from);
387 UNUSED(query);
388 }
389
390 static void
trying(char * frm,dig_lookup_t * lookup)391 trying(char *frm, dig_lookup_t *lookup) {
392 UNUSED(frm);
393 UNUSED(lookup);
394 }
395
396 static void
chase_cnamechain(dns_message_t * msg,dns_name_t * qname)397 chase_cnamechain(dns_message_t *msg, dns_name_t *qname) {
398 isc_result_t result;
399 dns_rdataset_t *rdataset;
400 dns_rdata_cname_t cname;
401 dns_rdata_t rdata = DNS_RDATA_INIT;
402 unsigned int i = msg->counts[DNS_SECTION_ANSWER];
403
404 while (i-- > 0) {
405 rdataset = NULL;
406 result = dns_message_findname(msg, DNS_SECTION_ANSWER, qname,
407 dns_rdatatype_cname, 0, NULL,
408 &rdataset);
409 if (result != ISC_R_SUCCESS) {
410 return;
411 }
412 result = dns_rdataset_first(rdataset);
413 check_result(result, "dns_rdataset_first");
414 dns_rdata_reset(&rdata);
415 dns_rdataset_current(rdataset, &rdata);
416 result = dns_rdata_tostruct(&rdata, &cname, NULL);
417 check_result(result, "dns_rdata_tostruct");
418 dns_name_copynf(&cname.cname, qname);
419 dns_rdata_freestruct(&cname);
420 }
421 }
422
423 static isc_result_t
printmessage(dig_query_t * query,const isc_buffer_t * msgbuf,dns_message_t * msg,bool headers)424 printmessage(dig_query_t *query, const isc_buffer_t *msgbuf, dns_message_t *msg,
425 bool headers) {
426 char servtext[ISC_SOCKADDR_FORMATSIZE];
427
428 UNUSED(msgbuf);
429
430 /* I've we've gotten this far, we've reached a server. */
431 query_error = 0;
432
433 debug("printmessage()");
434
435 if (!default_lookups || query->lookup->rdtype == dns_rdatatype_a) {
436 isc_sockaddr_format(&query->sockaddr, servtext,
437 sizeof(servtext));
438 printf("Server:\t\t%s\n", query->userarg);
439 printf("Address:\t%s\n", servtext);
440
441 puts("");
442 }
443
444 if (!short_form) {
445 puts("------------");
446 /* detailheader(query, msg);*/
447 detailsection(query, msg, true, DNS_SECTION_QUESTION);
448 detailsection(query, msg, true, DNS_SECTION_ANSWER);
449 detailsection(query, msg, true, DNS_SECTION_AUTHORITY);
450 detailsection(query, msg, true, DNS_SECTION_ADDITIONAL);
451 puts("------------");
452 }
453
454 if (msg->rcode != 0) {
455 char nametext[DNS_NAME_FORMATSIZE];
456 dns_name_format(query->lookup->name, nametext,
457 sizeof(nametext));
458 printf("** server can't find %s: %s\n", nametext,
459 rcode_totext(msg->rcode));
460 debug("returning with rcode == 0");
461
462 /* the lookup failed */
463 print_error |= 1;
464 return (ISC_R_SUCCESS);
465 }
466
467 if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) {
468 char namestr[DNS_NAME_FORMATSIZE];
469 dig_lookup_t *lookup;
470 dns_fixedname_t fixed;
471 dns_name_t *name;
472
473 /* Add AAAA lookup. */
474 name = dns_fixedname_initname(&fixed);
475 dns_name_copynf(query->lookup->name, name);
476 chase_cnamechain(msg, name);
477 dns_name_format(name, namestr, sizeof(namestr));
478 lookup = clone_lookup(query->lookup, false);
479 if (lookup != NULL) {
480 strlcpy(lookup->textname, namestr,
481 sizeof(lookup->textname));
482 lookup->rdtype = dns_rdatatype_aaaa;
483 lookup->rdtypeset = true;
484 lookup->origin = NULL;
485 lookup->retries = tries;
486 ISC_LIST_APPEND(lookup_list, lookup, link);
487 }
488 }
489
490 if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0 &&
491 (!default_lookups || query->lookup->rdtype == dns_rdatatype_a))
492 {
493 puts("Non-authoritative answer:");
494 }
495 if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) {
496 printsection(query, msg, headers, DNS_SECTION_ANSWER);
497 } else {
498 if (default_lookups && query->lookup->rdtype == dns_rdatatype_a)
499 {
500 a_noanswer = true;
501 } else if (!default_lookups ||
502 (query->lookup->rdtype == dns_rdatatype_aaaa &&
503 a_noanswer))
504 {
505 printf("*** Can't find %s: No answer\n",
506 query->lookup->textname);
507 }
508 }
509
510 if (((msg->flags & DNS_MESSAGEFLAG_AA) == 0) &&
511 (query->lookup->rdtype != dns_rdatatype_a) &&
512 (query->lookup->rdtype != dns_rdatatype_aaaa))
513 {
514 puts("\nAuthoritative answers can be found from:");
515 printsection(query, msg, headers, DNS_SECTION_AUTHORITY);
516 printsection(query, msg, headers, DNS_SECTION_ADDITIONAL);
517 }
518 return (ISC_R_SUCCESS);
519 }
520
521 static void
show_settings(bool full,bool serv_only)522 show_settings(bool full, bool serv_only) {
523 dig_server_t *srv;
524 isc_sockaddr_t sockaddr;
525 dig_searchlist_t *listent;
526 isc_result_t result;
527
528 srv = ISC_LIST_HEAD(server_list);
529
530 while (srv != NULL) {
531 char sockstr[ISC_SOCKADDR_FORMATSIZE];
532
533 result = get_address(srv->servername, port, &sockaddr);
534 check_result(result, "get_address");
535
536 isc_sockaddr_format(&sockaddr, sockstr, sizeof(sockstr));
537 printf("Default server: %s\nAddress: %s\n", srv->userarg,
538 sockstr);
539 if (!full) {
540 return;
541 }
542 srv = ISC_LIST_NEXT(srv, link);
543 }
544 if (serv_only) {
545 return;
546 }
547 printf("\nSet options:\n");
548 printf(" %s\t\t\t%s\t\t%s\n", tcpmode ? "vc" : "novc",
549 short_form ? "nodebug" : "debug", debugging ? "d2" : "nod2");
550 printf(" %s\t\t%s\n", usesearch ? "search" : "nosearch",
551 recurse ? "recurse" : "norecurse");
552 printf(" timeout = %u\t\tretry = %d\tport = %u\tndots = %d\n", timeout,
553 tries, port, ndots);
554 printf(" querytype = %-8s\tclass = %s\n", deftype, defclass);
555 printf(" srchlist = ");
556 for (listent = ISC_LIST_HEAD(search_list); listent != NULL;
557 listent = ISC_LIST_NEXT(listent, link))
558 {
559 printf("%s", listent->origin);
560 if (ISC_LIST_NEXT(listent, link) != NULL) {
561 printf("/");
562 }
563 }
564 printf("\n");
565 }
566
567 static bool
testtype(char * typetext)568 testtype(char *typetext) {
569 isc_result_t result;
570 isc_textregion_t tr;
571 dns_rdatatype_t rdtype;
572
573 tr.base = typetext;
574 tr.length = strlen(typetext);
575 result = dns_rdatatype_fromtext(&rdtype, &tr);
576 if (result == ISC_R_SUCCESS) {
577 return (true);
578 } else {
579 printf("unknown query type: %s\n", typetext);
580 return (false);
581 }
582 }
583
584 static bool
testclass(char * typetext)585 testclass(char *typetext) {
586 isc_result_t result;
587 isc_textregion_t tr;
588 dns_rdataclass_t rdclass;
589
590 tr.base = typetext;
591 tr.length = strlen(typetext);
592 result = dns_rdataclass_fromtext(&rdclass, &tr);
593 if (result == ISC_R_SUCCESS) {
594 return (true);
595 } else {
596 printf("unknown query class: %s\n", typetext);
597 return (false);
598 }
599 }
600
601 static void
set_port(const char * value)602 set_port(const char *value) {
603 uint32_t n;
604 isc_result_t result = parse_uint(&n, value, 65535, "port");
605 if (result == ISC_R_SUCCESS) {
606 port = (uint16_t)n;
607 }
608 }
609
610 static void
set_timeout(const char * value)611 set_timeout(const char *value) {
612 uint32_t n;
613 isc_result_t result = parse_uint(&n, value, UINT_MAX, "timeout");
614 if (result == ISC_R_SUCCESS) {
615 timeout = n;
616 }
617 }
618
619 static void
set_tries(const char * value)620 set_tries(const char *value) {
621 uint32_t n;
622 isc_result_t result = parse_uint(&n, value, INT_MAX, "tries");
623 if (result == ISC_R_SUCCESS) {
624 tries = n;
625 }
626 }
627
628 static void
set_ndots(const char * value)629 set_ndots(const char *value) {
630 uint32_t n;
631 isc_result_t result = parse_uint(&n, value, 128, "ndots");
632 if (result == ISC_R_SUCCESS) {
633 ndots = n;
634 }
635 }
636
637 static void
version(void)638 version(void) {
639 fputs("nslookup " VERSION "\n", stderr);
640 }
641
642 static void
setoption(char * opt)643 setoption(char *opt) {
644 size_t l = strlen(opt);
645
646 #define CHECKOPT(A, N) \
647 ((l >= N) && (l < sizeof(A)) && (strncasecmp(opt, A, l) == 0))
648
649 if (CHECKOPT("all", 3)) {
650 show_settings(true, false);
651 } else if (strncasecmp(opt, "class=", 6) == 0) {
652 if (testclass(&opt[6])) {
653 strlcpy(defclass, &opt[6], sizeof(defclass));
654 }
655 } else if (strncasecmp(opt, "cl=", 3) == 0) {
656 if (testclass(&opt[3])) {
657 strlcpy(defclass, &opt[3], sizeof(defclass));
658 }
659 } else if (strncasecmp(opt, "type=", 5) == 0) {
660 if (testtype(&opt[5])) {
661 strlcpy(deftype, &opt[5], sizeof(deftype));
662 default_lookups = false;
663 }
664 } else if (strncasecmp(opt, "ty=", 3) == 0) {
665 if (testtype(&opt[3])) {
666 strlcpy(deftype, &opt[3], sizeof(deftype));
667 default_lookups = false;
668 }
669 } else if (strncasecmp(opt, "querytype=", 10) == 0) {
670 if (testtype(&opt[10])) {
671 strlcpy(deftype, &opt[10], sizeof(deftype));
672 default_lookups = false;
673 }
674 } else if (strncasecmp(opt, "query=", 6) == 0) {
675 if (testtype(&opt[6])) {
676 strlcpy(deftype, &opt[6], sizeof(deftype));
677 default_lookups = false;
678 }
679 } else if (strncasecmp(opt, "qu=", 3) == 0) {
680 if (testtype(&opt[3])) {
681 strlcpy(deftype, &opt[3], sizeof(deftype));
682 default_lookups = false;
683 }
684 } else if (strncasecmp(opt, "q=", 2) == 0) {
685 if (testtype(&opt[2])) {
686 strlcpy(deftype, &opt[2], sizeof(deftype));
687 default_lookups = false;
688 }
689 } else if (strncasecmp(opt, "domain=", 7) == 0) {
690 strlcpy(domainopt, &opt[7], sizeof(domainopt));
691 set_search_domain(domainopt);
692 usesearch = true;
693 } else if (strncasecmp(opt, "do=", 3) == 0) {
694 strlcpy(domainopt, &opt[3], sizeof(domainopt));
695 set_search_domain(domainopt);
696 usesearch = true;
697 } else if (strncasecmp(opt, "port=", 5) == 0) {
698 set_port(&opt[5]);
699 } else if (strncasecmp(opt, "po=", 3) == 0) {
700 set_port(&opt[3]);
701 } else if (strncasecmp(opt, "timeout=", 8) == 0) {
702 set_timeout(&opt[8]);
703 } else if (strncasecmp(opt, "t=", 2) == 0) {
704 set_timeout(&opt[2]);
705 } else if (CHECKOPT("recurse", 3)) {
706 recurse = true;
707 } else if (CHECKOPT("norecurse", 5)) {
708 recurse = false;
709 } else if (strncasecmp(opt, "retry=", 6) == 0) {
710 set_tries(&opt[6]);
711 } else if (strncasecmp(opt, "ret=", 4) == 0) {
712 set_tries(&opt[4]);
713 } else if (CHECKOPT("defname", 3)) {
714 usesearch = true;
715 } else if (CHECKOPT("nodefname", 5)) {
716 usesearch = false;
717 } else if (CHECKOPT("vc", 2)) {
718 tcpmode = true;
719 tcpmode_set = true;
720 } else if (CHECKOPT("novc", 4)) {
721 tcpmode = false;
722 tcpmode_set = true;
723 } else if (CHECKOPT("debug", 3)) {
724 short_form = false;
725 showsearch = true;
726 } else if (CHECKOPT("nodebug", 5)) {
727 short_form = true;
728 showsearch = false;
729 } else if (CHECKOPT("d2", 2)) {
730 debugging = true;
731 } else if (CHECKOPT("nod2", 4)) {
732 debugging = false;
733 } else if (CHECKOPT("search", 3)) {
734 usesearch = true;
735 } else if (CHECKOPT("nosearch", 5)) {
736 usesearch = false;
737 } else if (CHECKOPT("sil", 3)) {
738 /* deprecation_msg = false; */
739 } else if (CHECKOPT("fail", 3)) {
740 nofail = false;
741 } else if (CHECKOPT("nofail", 5)) {
742 nofail = true;
743 } else if (strncasecmp(opt, "ndots=", 6) == 0) {
744 set_ndots(&opt[6]);
745 } else {
746 printf("*** Invalid option: %s\n", opt);
747 }
748 }
749
750 static void
addlookup(char * opt)751 addlookup(char *opt) {
752 dig_lookup_t *lookup;
753 isc_result_t result;
754 isc_textregion_t tr;
755 dns_rdatatype_t rdtype;
756 dns_rdataclass_t rdclass;
757 char store[MXNAME];
758
759 debug("addlookup()");
760
761 a_noanswer = false;
762
763 tr.base = deftype;
764 tr.length = strlen(deftype);
765 result = dns_rdatatype_fromtext(&rdtype, &tr);
766 if (result != ISC_R_SUCCESS) {
767 printf("unknown query type: %s\n", deftype);
768 rdclass = dns_rdatatype_a;
769 }
770 tr.base = defclass;
771 tr.length = strlen(defclass);
772 result = dns_rdataclass_fromtext(&rdclass, &tr);
773 if (result != ISC_R_SUCCESS) {
774 printf("unknown query class: %s\n", defclass);
775 rdclass = dns_rdataclass_in;
776 }
777 lookup = make_empty_lookup();
778 if (get_reverse(store, sizeof(store), opt, true) == ISC_R_SUCCESS) {
779 strlcpy(lookup->textname, store, sizeof(lookup->textname));
780 lookup->rdtype = dns_rdatatype_ptr;
781 lookup->rdtypeset = true;
782 } else {
783 strlcpy(lookup->textname, opt, sizeof(lookup->textname));
784 lookup->rdtype = rdtype;
785 lookup->rdtypeset = true;
786 }
787 lookup->rdclass = rdclass;
788 lookup->rdclassset = true;
789 lookup->trace = false;
790 lookup->trace_root = lookup->trace;
791 lookup->ns_search_only = false;
792 lookup->identify = identify;
793 lookup->recurse = recurse;
794 lookup->aaonly = aaonly;
795 lookup->retries = tries;
796 lookup->comments = comments;
797 if (lookup->rdtype == dns_rdatatype_any && !tcpmode_set) {
798 lookup->tcp_mode = true;
799 } else {
800 lookup->tcp_mode = tcpmode;
801 }
802 lookup->stats = stats;
803 lookup->section_question = section_question;
804 lookup->section_answer = section_answer;
805 lookup->section_authority = section_authority;
806 lookup->section_additional = section_additional;
807 lookup->new_search = true;
808 lookup->besteffort = false;
809 if (nofail) {
810 lookup->servfail_stops = false;
811 }
812 ISC_LIST_INIT(lookup->q);
813 ISC_LINK_INIT(lookup, link);
814 ISC_LIST_APPEND(lookup_list, lookup, link);
815 lookup->origin = NULL;
816 ISC_LIST_INIT(lookup->my_server_list);
817 debug("looking up %s", lookup->textname);
818 }
819
820 static void
do_next_command(char * input)821 do_next_command(char *input) {
822 char *ptr, *arg, *last;
823
824 if ((ptr = strtok_r(input, " \t\r\n", &last)) == NULL) {
825 return;
826 }
827 arg = strtok_r(NULL, " \t\r\n", &last);
828 if ((strcasecmp(ptr, "set") == 0) && (arg != NULL)) {
829 setoption(arg);
830 } else if ((strcasecmp(ptr, "server") == 0) ||
831 (strcasecmp(ptr, "lserver") == 0))
832 {
833 isc_app_block();
834 set_nameserver(arg);
835 check_ra = false;
836 isc_app_unblock();
837 show_settings(true, true);
838 } else if (strcasecmp(ptr, "exit") == 0) {
839 in_use = false;
840 } else if (strcasecmp(ptr, "help") == 0 || strcasecmp(ptr, "?") == 0) {
841 printf("The '%s' command is not yet implemented.\n", ptr);
842 } else if (strcasecmp(ptr, "finger") == 0 ||
843 strcasecmp(ptr, "root") == 0 || strcasecmp(ptr, "ls") == 0 ||
844 strcasecmp(ptr, "view") == 0)
845 {
846 printf("The '%s' command is not implemented.\n", ptr);
847 } else {
848 addlookup(ptr);
849 }
850 }
851
852 static void
get_next_command(void)853 get_next_command(void) {
854 char *buf;
855 char *ptr;
856
857 fflush(stdout);
858 buf = isc_mem_allocate(mctx, COMMSIZE);
859 isc_app_block();
860 if (interactive) {
861 #ifdef HAVE_READLINE
862 ptr = readline("> ");
863 if (ptr != NULL) {
864 add_history(ptr);
865 }
866 #else /* ifdef HAVE_READLINE */
867 fputs("> ", stderr);
868 fflush(stderr);
869 ptr = fgets(buf, COMMSIZE, stdin);
870 #endif /* ifdef HAVE_READLINE */
871 } else {
872 ptr = fgets(buf, COMMSIZE, stdin);
873 }
874 isc_app_unblock();
875 if (ptr == NULL) {
876 in_use = false;
877 } else {
878 do_next_command(ptr);
879 }
880 #ifdef HAVE_READLINE
881 if (interactive) {
882 free(ptr);
883 }
884 #endif /* ifdef HAVE_READLINE */
885 isc_mem_free(mctx, buf);
886 }
887
888 ISC_PLATFORM_NORETURN_PRE static void
889 usage(void) ISC_PLATFORM_NORETURN_POST;
890
891 static void
usage(void)892 usage(void) {
893 fprintf(stderr, "Usage:\n");
894 fprintf(stderr, " nslookup [-opt ...] # interactive mode "
895 "using default server\n");
896 fprintf(stderr, " nslookup [-opt ...] - server # interactive mode "
897 "using 'server'\n");
898 fprintf(stderr, " nslookup [-opt ...] host # just look up "
899 "'host' using default server\n");
900 fprintf(stderr, " nslookup [-opt ...] host server # just look up "
901 "'host' using 'server'\n");
902 exit(1);
903 }
904
905 static void
parse_args(int argc,char ** argv)906 parse_args(int argc, char **argv) {
907 bool have_lookup = false;
908
909 usesearch = true;
910 for (argc--, argv++; argc > 0 && argv[0] != NULL; argc--, argv++) {
911 debug("main parsing %s", argv[0]);
912 if (argv[0][0] == '-') {
913 if (strncasecmp(argv[0], "-ver", 4) == 0) {
914 version();
915 exit(0);
916 } else if (argv[0][1] != 0) {
917 setoption(&argv[0][1]);
918 } else {
919 have_lookup = true;
920 }
921 } else {
922 if (!have_lookup) {
923 have_lookup = true;
924 in_use = true;
925 addlookup(argv[0]);
926 } else {
927 if (argv[1] != NULL) {
928 usage();
929 }
930 set_nameserver(argv[0]);
931 check_ra = false;
932 }
933 }
934 }
935 }
936
937 static void
flush_lookup_list(void)938 flush_lookup_list(void) {
939 dig_lookup_t *l, *lp;
940 dig_query_t *q, *qp;
941 dig_server_t *s, *sp;
942
943 lookup_counter = 0;
944 l = ISC_LIST_HEAD(lookup_list);
945 while (l != NULL) {
946 q = ISC_LIST_HEAD(l->q);
947 while (q != NULL) {
948 if (q->sock != NULL) {
949 isc_socket_cancel(q->sock, NULL,
950 ISC_SOCKCANCEL_ALL);
951 isc_socket_detach(&q->sock);
952 }
953 isc_buffer_invalidate(&q->recvbuf);
954 isc_buffer_invalidate(&q->lengthbuf);
955 qp = q;
956 q = ISC_LIST_NEXT(q, link);
957 ISC_LIST_DEQUEUE(l->q, qp, link);
958 isc_mem_free(mctx, qp);
959 }
960 s = ISC_LIST_HEAD(l->my_server_list);
961 while (s != NULL) {
962 sp = s;
963 s = ISC_LIST_NEXT(s, link);
964 ISC_LIST_DEQUEUE(l->my_server_list, sp, link);
965 isc_mem_free(mctx, sp);
966 }
967 if (l->sendmsg != NULL) {
968 dns_message_detach(&l->sendmsg);
969 }
970 lp = l;
971 l = ISC_LIST_NEXT(l, link);
972 ISC_LIST_DEQUEUE(lookup_list, lp, link);
973 isc_mem_free(mctx, lp);
974 }
975 }
976
977 static void
getinput(isc_task_t * task,isc_event_t * event)978 getinput(isc_task_t *task, isc_event_t *event) {
979 UNUSED(task);
980 if (global_event == NULL) {
981 global_event = event;
982 }
983 while (in_use) {
984 get_next_command();
985 if (ISC_LIST_HEAD(lookup_list) != NULL) {
986 start_lookup();
987 return;
988 }
989 }
990 isc_app_shutdown();
991 }
992
993 int
main(int argc,char ** argv)994 main(int argc, char **argv) {
995 isc_result_t result;
996
997 interactive = isatty(0);
998
999 ISC_LIST_INIT(lookup_list);
1000 ISC_LIST_INIT(server_list);
1001 ISC_LIST_INIT(search_list);
1002
1003 check_ra = true;
1004
1005 /* setup dighost callbacks */
1006 dighost_printmessage = printmessage;
1007 dighost_received = received;
1008 dighost_trying = trying;
1009 dighost_shutdown = query_finished;
1010
1011 result = isc_app_start();
1012 check_result(result, "isc_app_start");
1013
1014 setup_libs();
1015 progname = argv[0];
1016
1017 setup_system(false, false);
1018 parse_args(argc, argv);
1019 if (keyfile[0] != 0) {
1020 setup_file_key();
1021 } else if (keysecret[0] != 0) {
1022 setup_text_key();
1023 }
1024 if (domainopt[0] != '\0') {
1025 set_search_domain(domainopt);
1026 }
1027 if (in_use) {
1028 result = isc_app_onrun(mctx, global_task, onrun_callback, NULL);
1029 } else {
1030 result = isc_app_onrun(mctx, global_task, getinput, NULL);
1031 }
1032 check_result(result, "isc_app_onrun");
1033 in_use = !in_use;
1034
1035 (void)isc_app_run();
1036
1037 puts("");
1038 debug("done, and starting to shut down");
1039 if (global_event != NULL) {
1040 isc_event_free(&global_event);
1041 }
1042 cancel_all();
1043 destroy_libs();
1044 isc_app_finish();
1045
1046 return (query_error | print_error);
1047 }
1048