diff options
Diffstat (limited to 'networking/nslookup.c')
-rw-r--r-- | networking/nslookup.c | 755 |
1 files changed, 740 insertions, 15 deletions
diff --git a/networking/nslookup.c b/networking/nslookup.c index 45c218e6c..fd241a5ca 100644 --- a/networking/nslookup.c +++ b/networking/nslookup.c | |||
@@ -1,31 +1,31 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | |
3 | * Mini nslookup implementation for busybox | ||
4 | * | ||
5 | * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu | ||
6 | * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> | ||
7 | * | ||
8 | * Correct default name server display and explicit name server option | ||
9 | * added by Ben Zeckel <bzeckel@hmc.edu> June 2001 | ||
10 | * | ||
11 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
12 | */ | ||
13 | //config:config NSLOOKUP | 3 | //config:config NSLOOKUP |
14 | //config: bool "nslookup (4.5 kb)" | 4 | //config: bool "nslookup (4.5 kb)" |
15 | //config: default y | 5 | //config: default y |
16 | //config: help | 6 | //config: help |
17 | //config: nslookup is a tool to query Internet name servers. | 7 | //config: nslookup is a tool to query Internet name servers. |
8 | //config: | ||
9 | //config:config FEATURE_NSLOOKUP_BIG | ||
10 | //config: bool "Use internal resolver code instead of libc" | ||
11 | //config: depends on NSLOOKUP | ||
12 | //config: default y | ||
13 | //config: | ||
14 | //config:config FEATURE_NSLOOKUP_LONG_OPTIONS | ||
15 | //config: bool "Enable long options" | ||
16 | //config: default y | ||
17 | //config: depends on FEATURE_NSLOOKUP_BIG && LONG_OPTS | ||
18 | 18 | ||
19 | //applet:IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP)) | 19 | //applet:IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP)) |
20 | 20 | ||
21 | //kbuild:lib-$(CONFIG_NSLOOKUP) += nslookup.o | 21 | //kbuild:lib-$(CONFIG_NSLOOKUP) += nslookup.o |
22 | 22 | ||
23 | //usage:#define nslookup_trivial_usage | 23 | //usage:#define nslookup_trivial_usage |
24 | //usage: "[HOST] [SERVER]" | 24 | //usage: IF_FEATURE_NSLOOKUP_BIG("[-type=QUERY_TYPE] [-debug] ") "HOST [DNS_SERVER]" |
25 | //usage:#define nslookup_full_usage "\n\n" | 25 | //usage:#define nslookup_full_usage "\n\n" |
26 | //usage: "Query the nameserver for the IP address of the given HOST\n" | 26 | //usage: "Query DNS about HOST" |
27 | //usage: "optionally using a specified DNS server" | 27 | //usage: IF_FEATURE_NSLOOKUP_BIG("\n") |
28 | //usage: | 28 | //usage: IF_FEATURE_NSLOOKUP_BIG("\nQUERY_TYPE: soa,ns,a,"IF_FEATURE_IPV6("aaaa,")"cname,mx,txt,ptr,any") |
29 | //usage:#define nslookup_example_usage | 29 | //usage:#define nslookup_example_usage |
30 | //usage: "$ nslookup localhost\n" | 30 | //usage: "$ nslookup localhost\n" |
31 | //usage: "Server: default\n" | 31 | //usage: "Server: default\n" |
@@ -35,7 +35,26 @@ | |||
35 | //usage: "Address: 127.0.0.1\n" | 35 | //usage: "Address: 127.0.0.1\n" |
36 | 36 | ||
37 | #include <resolv.h> | 37 | #include <resolv.h> |
38 | #include <net/if.h> /* for IFNAMSIZ */ | ||
39 | //#include <arpa/inet.h> | ||
40 | //#include <netdb.h> | ||
38 | #include "libbb.h" | 41 | #include "libbb.h" |
42 | #include "common_bufsiz.h" | ||
43 | |||
44 | |||
45 | #if !ENABLE_FEATURE_NSLOOKUP_BIG | ||
46 | |||
47 | /* | ||
48 | * Mini nslookup implementation for busybox | ||
49 | * | ||
50 | * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu | ||
51 | * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> | ||
52 | * | ||
53 | * Correct default name server display and explicit name server option | ||
54 | * added by Ben Zeckel <bzeckel@hmc.edu> June 2001 | ||
55 | * | ||
56 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
57 | */ | ||
39 | 58 | ||
40 | /* | 59 | /* |
41 | * I'm only implementing non-interactive mode; | 60 | * I'm only implementing non-interactive mode; |
@@ -207,3 +226,709 @@ int nslookup_main(int argc, char **argv) | |||
207 | 226 | ||
208 | return print_host(argv[1], "Name:"); | 227 | return print_host(argv[1], "Name:"); |
209 | } | 228 | } |
229 | |||
230 | |||
231 | #else /****** A version from LEDE / OpenWRT ******/ | ||
232 | |||
233 | /* | ||
234 | * musl compatible nslookup | ||
235 | * | ||
236 | * Copyright (C) 2017 Jo-Philipp Wich <jo@mein.io> | ||
237 | * | ||
238 | * Permission to use, copy, modify, and/or distribute this software for any | ||
239 | * purpose with or without fee is hereby granted, provided that the above | ||
240 | * copyright notice and this permission notice appear in all copies. | ||
241 | * | ||
242 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
243 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
244 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
245 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
246 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
247 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
248 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
249 | */ | ||
250 | |||
251 | #if 0 | ||
252 | # define dbg(...) fprintf(stderr, __VA_ARGS__) | ||
253 | #else | ||
254 | # define dbg(...) ((void)0) | ||
255 | #endif | ||
256 | |||
257 | struct ns { | ||
258 | const char *name; | ||
259 | len_and_sockaddr *lsa; | ||
260 | int failures; | ||
261 | int replies; | ||
262 | }; | ||
263 | |||
264 | struct query { | ||
265 | const char *name; | ||
266 | unsigned qlen; | ||
267 | // unsigned latency; | ||
268 | // uint8_t rcode; | ||
269 | unsigned char query[512]; | ||
270 | // unsigned char reply[512]; | ||
271 | }; | ||
272 | |||
273 | static const struct { | ||
274 | unsigned char type; | ||
275 | char name[7]; | ||
276 | } qtypes[] = { | ||
277 | { ns_t_soa, "SOA" }, | ||
278 | { ns_t_ns, "NS" }, | ||
279 | { ns_t_a, "A" }, | ||
280 | #if ENABLE_FEATURE_IPV6 | ||
281 | { ns_t_aaaa, "AAAA" }, | ||
282 | #endif | ||
283 | { ns_t_cname, "CNAME" }, | ||
284 | { ns_t_mx, "MX" }, | ||
285 | { ns_t_txt, "TXT" }, | ||
286 | { ns_t_ptr, "PTR" }, | ||
287 | { ns_t_any, "ANY" }, | ||
288 | }; | ||
289 | |||
290 | static const char *const rcodes[] = { | ||
291 | "NOERROR", // 0 | ||
292 | "FORMERR", // 1 | ||
293 | "SERVFAIL", // 2 | ||
294 | "NXDOMAIN", // 3 | ||
295 | "NOTIMP", // 4 | ||
296 | "REFUSED", // 5 | ||
297 | "YXDOMAIN", // 6 | ||
298 | "YXRRSET", // 7 | ||
299 | "NXRRSET", // 8 | ||
300 | "NOTAUTH", // 9 | ||
301 | "NOTZONE", // 10 | ||
302 | "11", // 11 not assigned | ||
303 | "12", // 12 not assigned | ||
304 | "13", // 13 not assigned | ||
305 | "14", // 14 not assigned | ||
306 | "15", // 15 not assigned | ||
307 | }; | ||
308 | |||
309 | #if ENABLE_FEATURE_IPV6 | ||
310 | static const char v4_mapped[12] = { 0,0,0,0, 0,0,0,0, 0,0,0xff,0xff }; | ||
311 | #endif | ||
312 | |||
313 | struct globals { | ||
314 | unsigned default_port; | ||
315 | unsigned default_retry; | ||
316 | unsigned default_timeout; | ||
317 | unsigned query_count; | ||
318 | unsigned serv_count; | ||
319 | struct ns *server; | ||
320 | struct query *query; | ||
321 | } FIX_ALIASING; | ||
322 | #define G (*(struct globals*)bb_common_bufsiz1) | ||
323 | #define INIT_G() do { \ | ||
324 | setup_common_bufsiz(); \ | ||
325 | G.default_port = 53; \ | ||
326 | G.default_retry = 2; \ | ||
327 | G.default_timeout = 5; \ | ||
328 | } while (0) | ||
329 | |||
330 | enum { | ||
331 | OPT_debug = (1 << 0), | ||
332 | }; | ||
333 | |||
334 | static int parse_reply(const unsigned char *msg, size_t len) | ||
335 | { | ||
336 | HEADER *header; | ||
337 | |||
338 | ns_msg handle; | ||
339 | ns_rr rr; | ||
340 | int i, n, rdlen; | ||
341 | const char *format = NULL; | ||
342 | char astr[INET6_ADDRSTRLEN], dname[MAXDNAME]; | ||
343 | const unsigned char *cp; | ||
344 | |||
345 | header = (HEADER *)msg; | ||
346 | if (!header->aa) | ||
347 | printf("Non-authoritative answer:\n"); | ||
348 | |||
349 | if (ns_initparse(msg, len, &handle) != 0) { | ||
350 | //printf("Unable to parse reply: %s\n", strerror(errno)); | ||
351 | return -1; | ||
352 | } | ||
353 | |||
354 | for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) { | ||
355 | if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) { | ||
356 | //printf("Unable to parse resource record: %s\n", strerror(errno)); | ||
357 | return -1; | ||
358 | } | ||
359 | |||
360 | rdlen = ns_rr_rdlen(rr); | ||
361 | |||
362 | switch (ns_rr_type(rr)) | ||
363 | { | ||
364 | case ns_t_a: | ||
365 | if (rdlen != 4) { | ||
366 | dbg("unexpected A record length %d\n", rdlen); | ||
367 | return -1; | ||
368 | } | ||
369 | inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr)); | ||
370 | printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr); | ||
371 | break; | ||
372 | |||
373 | #if ENABLE_FEATURE_IPV6 | ||
374 | case ns_t_aaaa: | ||
375 | if (rdlen != 16) { | ||
376 | dbg("unexpected AAAA record length %d\n", rdlen); | ||
377 | return -1; | ||
378 | } | ||
379 | inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr)); | ||
380 | /* bind-utils-9.11.3 uses the same format for A and AAAA answers */ | ||
381 | printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr); | ||
382 | break; | ||
383 | #endif | ||
384 | |||
385 | case ns_t_ns: | ||
386 | if (!format) | ||
387 | format = "%s\tnameserver = %s\n"; | ||
388 | /* fall through */ | ||
389 | |||
390 | case ns_t_cname: | ||
391 | if (!format) | ||
392 | format = "%s\tcanonical name = %s\n"; | ||
393 | /* fall through */ | ||
394 | |||
395 | case ns_t_ptr: | ||
396 | if (!format) | ||
397 | format = "%s\tname = %s\n"; | ||
398 | if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||
399 | ns_rr_rdata(rr), dname, sizeof(dname)) < 0 | ||
400 | ) { | ||
401 | //printf("Unable to uncompress domain: %s\n", strerror(errno)); | ||
402 | return -1; | ||
403 | } | ||
404 | printf(format, ns_rr_name(rr), dname); | ||
405 | break; | ||
406 | |||
407 | case ns_t_mx: | ||
408 | if (rdlen < 2) { | ||
409 | printf("MX record too short\n"); | ||
410 | return -1; | ||
411 | } | ||
412 | n = ns_get16(ns_rr_rdata(rr)); | ||
413 | if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||
414 | ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0 | ||
415 | ) { | ||
416 | //printf("Cannot uncompress MX domain: %s\n", strerror(errno)); | ||
417 | return -1; | ||
418 | } | ||
419 | printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname); | ||
420 | break; | ||
421 | |||
422 | case ns_t_txt: | ||
423 | if (rdlen < 1) { | ||
424 | //printf("TXT record too short\n"); | ||
425 | return -1; | ||
426 | } | ||
427 | n = *(unsigned char *)ns_rr_rdata(rr); | ||
428 | if (n > 0) { | ||
429 | memset(dname, 0, sizeof(dname)); | ||
430 | memcpy(dname, ns_rr_rdata(rr) + 1, n); | ||
431 | printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname); | ||
432 | } | ||
433 | break; | ||
434 | |||
435 | case ns_t_soa: | ||
436 | if (rdlen < 20) { | ||
437 | dbg("SOA record too short:%d\n", rdlen); | ||
438 | return -1; | ||
439 | } | ||
440 | |||
441 | printf("%s\n", ns_rr_name(rr)); | ||
442 | |||
443 | cp = ns_rr_rdata(rr); | ||
444 | n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||
445 | cp, dname, sizeof(dname)); | ||
446 | if (n < 0) { | ||
447 | //printf("Unable to uncompress domain: %s\n", strerror(errno)); | ||
448 | return -1; | ||
449 | } | ||
450 | |||
451 | printf("\torigin = %s\n", dname); | ||
452 | cp += n; | ||
453 | |||
454 | n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), | ||
455 | cp, dname, sizeof(dname)); | ||
456 | if (n < 0) { | ||
457 | //printf("Unable to uncompress domain: %s\n", strerror(errno)); | ||
458 | return -1; | ||
459 | } | ||
460 | |||
461 | printf("\tmail addr = %s\n", dname); | ||
462 | cp += n; | ||
463 | |||
464 | printf("\tserial = %lu\n", ns_get32(cp)); | ||
465 | cp += 4; | ||
466 | |||
467 | printf("\trefresh = %lu\n", ns_get32(cp)); | ||
468 | cp += 4; | ||
469 | |||
470 | printf("\tretry = %lu\n", ns_get32(cp)); | ||
471 | cp += 4; | ||
472 | |||
473 | printf("\texpire = %lu\n", ns_get32(cp)); | ||
474 | cp += 4; | ||
475 | |||
476 | printf("\tminimum = %lu\n", ns_get32(cp)); | ||
477 | break; | ||
478 | |||
479 | default: | ||
480 | break; | ||
481 | } | ||
482 | } | ||
483 | |||
484 | return i; | ||
485 | } | ||
486 | |||
487 | /* | ||
488 | * Function logic borrowed & modified from musl libc, res_msend.c | ||
489 | * G.query_count is always > 0. | ||
490 | */ | ||
491 | static int send_queries(struct ns *ns) | ||
492 | { | ||
493 | unsigned char reply[512]; | ||
494 | uint8_t rcode; | ||
495 | len_and_sockaddr *local_lsa; | ||
496 | struct pollfd pfd; | ||
497 | int servfail_retry = 0; | ||
498 | int n_replies = 0; | ||
499 | // int save_idx = 0; | ||
500 | unsigned retry_interval; | ||
501 | unsigned timeout = G.default_timeout * 1000; | ||
502 | unsigned tstart, tsent, tcur; | ||
503 | |||
504 | pfd.events = POLLIN; | ||
505 | pfd.fd = xsocket_type(&local_lsa, ns->lsa->u.sa.sa_family, SOCK_DGRAM); | ||
506 | /* | ||
507 | * local_lsa has "null" address and port 0 now. | ||
508 | * bind() ensures we have a *particular port* selected by kernel | ||
509 | * and remembered in fd, thus later recv(fd) | ||
510 | * receives only packets sent to this port. | ||
511 | */ | ||
512 | xbind(pfd.fd, &local_lsa->u.sa, local_lsa->len); | ||
513 | free(local_lsa); | ||
514 | /* Make read/writes know the destination */ | ||
515 | xconnect(pfd.fd, &ns->lsa->u.sa, ns->lsa->len); | ||
516 | ndelay_on(pfd.fd); | ||
517 | |||
518 | retry_interval = timeout / G.default_retry; | ||
519 | tstart = tcur = monotonic_ms(); | ||
520 | goto send; | ||
521 | |||
522 | while (tcur - tstart < timeout) { | ||
523 | int qn; | ||
524 | int recvlen; | ||
525 | |||
526 | if (tcur - tsent >= retry_interval) { | ||
527 | send: | ||
528 | for (qn = 0; qn < G.query_count; qn++) { | ||
529 | if (G.query[qn].qlen == 0) | ||
530 | continue; /* this one was replied already */ | ||
531 | |||
532 | if (write(pfd.fd, G.query[qn].query, G.query[qn].qlen) < 0) { | ||
533 | bb_perror_msg("write to '%s'", ns->name); | ||
534 | n_replies = -1; /* "no go, try next server" */ | ||
535 | goto ret; | ||
536 | } | ||
537 | dbg("query %u sent\n", qn); | ||
538 | } | ||
539 | tsent = tcur; | ||
540 | servfail_retry = 2 * G.query_count; | ||
541 | } | ||
542 | |||
543 | /* Wait for a response, or until time to retry */ | ||
544 | if (poll(&pfd, 1, retry_interval - (tcur - tsent)) <= 0) | ||
545 | goto next; | ||
546 | |||
547 | recvlen = read(pfd.fd, reply, sizeof(reply)); | ||
548 | if (recvlen < 0) { | ||
549 | bb_perror_msg("read"); | ||
550 | next: | ||
551 | tcur = monotonic_ms(); | ||
552 | continue; | ||
553 | } | ||
554 | |||
555 | if (ns->replies++ == 0) { | ||
556 | printf("Server:\t\t%s\n", ns->name); | ||
557 | printf("Address:\t%s\n\n", | ||
558 | auto_string(xmalloc_sockaddr2dotted(&ns->lsa->u.sa)) | ||
559 | ); | ||
560 | /* In "Address", bind-utils-9.11.3 show port after a hash: "1.2.3.4#53" */ | ||
561 | /* Should we do the same? */ | ||
562 | } | ||
563 | |||
564 | /* Non-identifiable packet */ | ||
565 | if (recvlen < 4) { | ||
566 | dbg("read is too short:%d\n", recvlen); | ||
567 | goto next; | ||
568 | } | ||
569 | |||
570 | /* Find which query this answer goes with, if any */ | ||
571 | // qn = save_idx; | ||
572 | qn = 0; | ||
573 | for (;;) { | ||
574 | if (memcmp(reply, G.query[qn].query, 2) == 0) { | ||
575 | dbg("response matches query %u\n", qn); | ||
576 | break; | ||
577 | } | ||
578 | if (++qn >= G.query_count) { | ||
579 | dbg("response does not match any query\n"); | ||
580 | goto next; | ||
581 | } | ||
582 | } | ||
583 | |||
584 | if (G.query[qn].qlen == 0) { | ||
585 | dbg("dropped duplicate response to query %u\n", qn); | ||
586 | goto next; | ||
587 | } | ||
588 | |||
589 | rcode = reply[3] & 0x0f; | ||
590 | dbg("query %u rcode:%s\n", qn, rcodes[rcode]); | ||
591 | |||
592 | /* Retry immediately on SERVFAIL */ | ||
593 | if (rcode == 2) { | ||
594 | ns->failures++; | ||
595 | if (servfail_retry) { | ||
596 | servfail_retry--; | ||
597 | write(pfd.fd, G.query[qn].query, G.query[qn].qlen); | ||
598 | dbg("query %u resent\n", qn); | ||
599 | goto next; | ||
600 | } | ||
601 | } | ||
602 | |||
603 | /* Process reply */ | ||
604 | G.query[qn].qlen = 0; /* flag: "reply received" */ | ||
605 | tcur = monotonic_ms(); | ||
606 | #if 1 | ||
607 | if (option_mask32 & OPT_debug) { | ||
608 | printf("Query #%d completed in %ums:\n", qn, tcur - tstart); | ||
609 | } | ||
610 | if (rcode != 0) { | ||
611 | printf("** server can't find %s: %s\n", | ||
612 | G.query[qn].name, rcodes[rcode]); | ||
613 | } else { | ||
614 | if (parse_reply(reply, recvlen) < 0) | ||
615 | printf("*** Can't find %s: Parse error\n", G.query[qn].name); | ||
616 | } | ||
617 | bb_putchar('\n'); | ||
618 | n_replies++; | ||
619 | if (n_replies >= G.query_count) | ||
620 | goto ret; | ||
621 | #else | ||
622 | //used to store replies and process them later | ||
623 | G.query[qn].latency = tcur - tstart; | ||
624 | n_replies++; | ||
625 | if (qn != save_idx) { | ||
626 | /* "wrong" receive buffer, move to correct one */ | ||
627 | memcpy(G.query[qn].reply, G.query[save_idx].reply, recvlen); | ||
628 | continue; | ||
629 | } | ||
630 | /* G.query[0..save_idx] have replies, move to next one, if exists */ | ||
631 | for (;;) { | ||
632 | save_idx++; | ||
633 | if (save_idx >= G.query_count) | ||
634 | goto ret; /* all are full: we have all results */ | ||
635 | if (!G.query[save_idx].rlen) | ||
636 | break; /* this one is empty */ | ||
637 | } | ||
638 | #endif | ||
639 | } /* while() */ | ||
640 | |||
641 | ret: | ||
642 | close(pfd.fd); | ||
643 | |||
644 | return n_replies; | ||
645 | } | ||
646 | |||
647 | static void add_ns(const char *addr) | ||
648 | { | ||
649 | struct ns *ns; | ||
650 | unsigned count; | ||
651 | |||
652 | dbg("%s: addr:'%s'\n", __func__, addr); | ||
653 | |||
654 | count = G.serv_count++; | ||
655 | |||
656 | G.server = xrealloc_vector(G.server, /*8=2^3:*/ 3, count); | ||
657 | ns = &G.server[count]; | ||
658 | ns->name = addr; | ||
659 | ns->lsa = xhost2sockaddr(addr, G.default_port); | ||
660 | /*ns->replies = 0; - already is */ | ||
661 | /*ns->failures = 0; - already is */ | ||
662 | } | ||
663 | |||
664 | static void parse_resolvconf(void) | ||
665 | { | ||
666 | FILE *resolv; | ||
667 | |||
668 | resolv = fopen("/etc/resolv.conf", "r"); | ||
669 | if (resolv) { | ||
670 | char line[128], *p; | ||
671 | |||
672 | while (fgets(line, sizeof(line), resolv)) { | ||
673 | p = strtok(line, " \t\n"); | ||
674 | |||
675 | if (!p || strcmp(p, "nameserver") != 0) | ||
676 | continue; | ||
677 | |||
678 | p = strtok(NULL, " \t\n"); | ||
679 | |||
680 | if (!p) | ||
681 | continue; | ||
682 | |||
683 | add_ns(xstrdup(p)); | ||
684 | } | ||
685 | |||
686 | fclose(resolv); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | static void add_query(int type, const char *dname) | ||
691 | { | ||
692 | struct query *new_q; | ||
693 | unsigned count; | ||
694 | ssize_t qlen; | ||
695 | |||
696 | count = G.query_count++; | ||
697 | |||
698 | G.query = xrealloc_vector(G.query, /*2=2^1:*/ 1, count); | ||
699 | new_q = &G.query[count]; | ||
700 | |||
701 | dbg("new query#%u type %u for '%s'\n", count, type, dname); | ||
702 | new_q->name = dname; | ||
703 | |||
704 | qlen = res_mkquery(QUERY, dname, C_IN, type, | ||
705 | /*data:*/ NULL, /*datalen:*/ 0, | ||
706 | /*newrr:*/ NULL, | ||
707 | new_q->query, sizeof(new_q->query) | ||
708 | ); | ||
709 | new_q->qlen = qlen; | ||
710 | } | ||
711 | |||
712 | static char *make_ptr(const char *addrstr) | ||
713 | { | ||
714 | unsigned char addr[16]; | ||
715 | int i; | ||
716 | |||
717 | #if ENABLE_FEATURE_IPV6 | ||
718 | if (inet_pton(AF_INET6, addrstr, addr)) { | ||
719 | if (memcmp(addr, v4_mapped, 12) != 0) { | ||
720 | char resbuf[80]; | ||
721 | char *ptr = resbuf; | ||
722 | for (i = 0; i < 16; i++) { | ||
723 | *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] & 0xf]; | ||
724 | *ptr++ = '.'; | ||
725 | *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] >> 4]; | ||
726 | *ptr++ = '.'; | ||
727 | } | ||
728 | strcpy(ptr, "ip6.arpa"); | ||
729 | return xstrdup(resbuf); | ||
730 | } | ||
731 | return xasprintf("%u.%u.%u.%u.in-addr.arpa", | ||
732 | addr[15], addr[14], addr[13], addr[12]); | ||
733 | } | ||
734 | #endif | ||
735 | |||
736 | if (inet_pton(AF_INET, addrstr, addr)) { | ||
737 | return xasprintf("%u.%u.%u.%u.in-addr.arpa", | ||
738 | addr[3], addr[2], addr[1], addr[0]); | ||
739 | } | ||
740 | |||
741 | return NULL; | ||
742 | } | ||
743 | |||
744 | int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
745 | int nslookup_main(int argc UNUSED_PARAM, char **argv) | ||
746 | { | ||
747 | unsigned types; | ||
748 | int rc; | ||
749 | int err; | ||
750 | |||
751 | INIT_G(); | ||
752 | |||
753 | /* manpage: "Options can also be specified on the command line | ||
754 | * if they precede the arguments and are prefixed with a hyphen." | ||
755 | */ | ||
756 | types = 0; | ||
757 | argv++; | ||
758 | for (;;) { | ||
759 | const char *options = | ||
760 | // bind-utils-9.11.3 accept these: | ||
761 | // class= cl= | ||
762 | // type= ty= querytype= query= qu= q= | ||
763 | // domain= do= | ||
764 | // port= po= | ||
765 | // timeout= t= | ||
766 | // retry= ret= | ||
767 | // ndots= | ||
768 | // recurse | ||
769 | // norecurse | ||
770 | // defname | ||
771 | // nodefname | ||
772 | // vc | ||
773 | // novc | ||
774 | // debug | ||
775 | // nodebug | ||
776 | // d2 | ||
777 | // nod2 | ||
778 | // search | ||
779 | // nosearch | ||
780 | // sil | ||
781 | // fail | ||
782 | // nofail | ||
783 | // ver (prints version and exits) | ||
784 | "type\0" /* 0 */ | ||
785 | "querytype\0" /* 1 */ | ||
786 | "port\0" /* 2 */ | ||
787 | "retry\0" /* 3 */ | ||
788 | "debug\0" /* 4 */ | ||
789 | "t\0" /* disambiguate with "type": else -t=2 fails */ | ||
790 | "timeout\0" /* 6 */ | ||
791 | ""; | ||
792 | int i; | ||
793 | char *arg; | ||
794 | char *val; | ||
795 | |||
796 | if (!*argv) | ||
797 | bb_show_usage(); | ||
798 | if (argv[0][0] != '-') | ||
799 | break; | ||
800 | |||
801 | /* Separate out "=val" part */ | ||
802 | arg = (*argv++) + 1; | ||
803 | val = strchrnul(arg, '='); | ||
804 | if (*val) | ||
805 | *val++ = '\0'; | ||
806 | |||
807 | i = index_in_substrings(options, arg); | ||
808 | //bb_error_msg("i:%d arg:'%s' val:'%s'", i, arg, val); | ||
809 | if (i < 0) | ||
810 | bb_show_usage(); | ||
811 | |||
812 | if (i <= 1) { | ||
813 | for (i = 0;; i++) { | ||
814 | if (i == ARRAY_SIZE(qtypes)) | ||
815 | bb_error_msg_and_die("invalid query type \"%s\"", val); | ||
816 | if (strcasecmp(qtypes[i].name, val) == 0) | ||
817 | break; | ||
818 | } | ||
819 | types |= (1 << i); | ||
820 | continue; | ||
821 | } | ||
822 | if (i == 2) { | ||
823 | G.default_port = xatou_range(val, 1, 0xffff); | ||
824 | } | ||
825 | if (i == 3) { | ||
826 | G.default_retry = xatou_range(val, 1, INT_MAX); | ||
827 | } | ||
828 | if (i == 4) { | ||
829 | option_mask32 |= OPT_debug; | ||
830 | } | ||
831 | if (i > 4) { | ||
832 | G.default_timeout = xatou_range(val, 1, INT_MAX / 1000); | ||
833 | } | ||
834 | } | ||
835 | |||
836 | if (types == 0) { | ||
837 | /* No explicit type given, guess query type. | ||
838 | * If we can convert the domain argument into a ptr (means that | ||
839 | * inet_pton() could read it) we assume a PTR request, else | ||
840 | * we issue A+AAAA queries and switch to an output format | ||
841 | * mimicking the one of the traditional nslookup applet. | ||
842 | */ | ||
843 | char *ptr; | ||
844 | |||
845 | ptr = make_ptr(argv[0]); | ||
846 | if (ptr) { | ||
847 | add_query(T_PTR, ptr); | ||
848 | } else { | ||
849 | add_query(T_A, argv[0]); | ||
850 | #if ENABLE_FEATURE_IPV6 | ||
851 | add_query(T_AAAA, argv[0]); | ||
852 | #endif | ||
853 | } | ||
854 | } else { | ||
855 | int c; | ||
856 | for (c = 0; c < ARRAY_SIZE(qtypes); c++) { | ||
857 | if (types & (1 << c)) | ||
858 | add_query(qtypes[c].type, argv[0]); | ||
859 | } | ||
860 | } | ||
861 | |||
862 | /* Use given DNS server if present */ | ||
863 | if (argv[1]) { | ||
864 | if (argv[2]) | ||
865 | bb_show_usage(); | ||
866 | add_ns(argv[1]); | ||
867 | } else { | ||
868 | parse_resolvconf(); | ||
869 | /* Fall back to localhost if we could not find NS in resolv.conf */ | ||
870 | if (G.serv_count == 0) | ||
871 | add_ns("127.0.0.1"); | ||
872 | } | ||
873 | |||
874 | for (rc = 0; rc < G.serv_count;) { | ||
875 | int c; | ||
876 | |||
877 | c = send_queries(&G.server[rc]); | ||
878 | if (c > 0) { | ||
879 | /* more than zero replies received */ | ||
880 | #if 0 /* which version does this? */ | ||
881 | if (option_mask32 & OPT_debug) { | ||
882 | printf("Replies:\t%d\n", G.server[rc].replies); | ||
883 | printf("Failures:\t%d\n\n", G.server[rc].failures); | ||
884 | } | ||
885 | #endif | ||
886 | break; | ||
887 | //FIXME: we "break" even though some queries may still be not answered, and other servers may know them? | ||
888 | } | ||
889 | /* c = 0: timed out waiting for replies */ | ||
890 | /* c < 0: error (message already printed) */ | ||
891 | rc++; | ||
892 | if (rc >= G.serv_count) { | ||
893 | // | ||
894 | // NB: bind-utils-9.11.3 behavior (all to stdout, not stderr): | ||
895 | // | ||
896 | // $ nslookup gmail.com 8.8.8.8 | ||
897 | // ;; connection timed out; no servers could be reached | ||
898 | // | ||
899 | // Using TCP mode: | ||
900 | // $ nslookup -vc gmail.com 8.8.8.8; echo EXITCODE:$? | ||
901 | // <~10 sec> | ||
902 | // ;; Connection to 8.8.8.8#53(8.8.8.8) for gmail.com failed: timed out. | ||
903 | // <~10 sec> | ||
904 | // ;; Connection to 8.8.8.8#53(8.8.8.8) for gmail.com failed: timed out. | ||
905 | // <~10 sec> | ||
906 | // ;; connection timed out; no servers could be reached | ||
907 | // ;; Connection to 8.8.8.8#53(8.8.8.8) for gmail.com failed: timed out. | ||
908 | // <empty line> | ||
909 | // EXITCODE:1 | ||
910 | // $ _ | ||
911 | printf(";; connection timed out; no servers could be reached\n\n"); | ||
912 | return EXIT_FAILURE; | ||
913 | } | ||
914 | } | ||
915 | |||
916 | err = 0; | ||
917 | for (rc = 0; rc < G.query_count; rc++) { | ||
918 | if (G.query[rc].qlen) { | ||
919 | printf("*** Can't find %s: No answer\n", G.query[rc].name); | ||
920 | err = 1; | ||
921 | } | ||
922 | } | ||
923 | if (err) /* should this affect exicode too? */ | ||
924 | bb_putchar('\n'); | ||
925 | |||
926 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
927 | free(G.server); | ||
928 | free(G.query); | ||
929 | } | ||
930 | |||
931 | return EXIT_SUCCESS; | ||
932 | } | ||
933 | |||
934 | #endif | ||