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