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