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