aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-04-14 14:05:45 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2018-04-14 14:05:45 +0200
commit0dd3be8c0933d5649152b6019b975876e804e22a (patch)
tree04a196f434fc9bf1b0a56243c4ffb02d8711868d
parent43dd0062229170747dcbee0a2a87b8e5ee2f09d6 (diff)
downloadbusybox-w32-0dd3be8c0933d5649152b6019b975876e804e22a.tar.gz
busybox-w32-0dd3be8c0933d5649152b6019b975876e804e22a.tar.bz2
busybox-w32-0dd3be8c0933d5649152b6019b975876e804e22a.zip
nslookup: add openwrt / lede version
Needs work on size reduction function old new delta nslookup_main 114 2363 +2249 parse_reply - 1022 +1022 add_ns - 663 +663 ns_parserr - 486 +486 ns_initparse - 184 +184 ns_skiprr - 117 +117 add_query - 95 +95 qtypes - 80 +80 rcodes - 68 +68 dn_skipname - 58 +58 ns_name_uncompress - 56 +56 ns_get16 - 13 +13 v4_mapped - 12 +12 ns_get32 - 9 +9 res_init 3 - -3 __res_state 6 - -6 xmalloc_sockaddr2hostonly_noport 10 - -10 gai_strerror 47 - -47 set_default_dns 95 - -95 print_host 199 - -199 static.res 512 - -512 ------------------------------------------------------------------------------ (add/remove: 15/10 grow/shrink: 1/0 up/down: 5112/-872) Total: 4240 bytes text data bss dec hex filename 921944 555 6252 928751 e2bef busybox_old 927375 555 5740 933670 e3f26 busybox_unstripped Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--networking/nslookup.c919
1 files changed, 908 insertions, 11 deletions
diff --git a/networking/nslookup.c b/networking/nslookup.c
index 45c218e6c..6f8f02fc1 100644
--- a/networking/nslookup.c
+++ b/networking/nslookup.c
@@ -1,20 +1,20 @@
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 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 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
@@ -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_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,881 @@ 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
257struct ns {
258 const char *name;
259 len_and_sockaddr addr;
260 int failures;
261 int replies;
262};
263
264struct query {
265 const char *name;
266 size_t qlen, rlen;
267 unsigned char query[512], reply[512];
268 unsigned long latency;
269 int rcode, n_ns;
270};
271
272static const struct {
273 int type;
274 const char *name;
275} qtypes[] = {
276 { ns_t_soa, "SOA" },
277 { ns_t_ns, "NS" },
278 { ns_t_a, "A" },
279#if ENABLE_FEATURE_IPV6
280 { ns_t_aaaa, "AAAA" },
281#endif
282 { ns_t_cname, "CNAME" },
283 { ns_t_mx, "MX" },
284 { ns_t_txt, "TXT" },
285 { ns_t_ptr, "PTR" },
286 { ns_t_any, "ANY" },
287 { }
288};
289
290static const char *const rcodes[] = {
291 "NOERROR",
292 "FORMERR",
293 "SERVFAIL",
294 "NXDOMAIN",
295 "NOTIMP",
296 "REFUSED",
297 "YXDOMAIN",
298 "YXRRSET",
299 "NXRRSET",
300 "NOTAUTH",
301 "NOTZONE",
302 "RESERVED11",
303 "RESERVED12",
304 "RESERVED13",
305 "RESERVED14",
306 "RESERVED15",
307 "BADVERS"
308};
309
310#if ENABLE_FEATURE_IPV6
311static const char v4_mapped[] = { 0,0,0,0,0,0,0,0,0,0,0xff,0xff };
312#endif
313
314struct globals {
315 unsigned default_port;
316 unsigned default_retry;
317 unsigned default_timeout;
318} FIX_ALIASING;
319#define G (*(struct globals*)bb_common_bufsiz1)
320#define INIT_G() do { \
321 setup_common_bufsiz(); \
322 G.default_port = 53; \
323 G.default_retry = 2; \
324 G.default_timeout = 5; \
325} while (0)
326
327static int
328parse_reply(const unsigned char *msg, size_t len, int *bb_style_counter)
329{
330 ns_msg handle;
331 ns_rr rr;
332 int i, n, rdlen;
333 const char *format = NULL;
334 char astr[INET6_ADDRSTRLEN], dname[MAXDNAME];
335 const unsigned char *cp;
336
337 if (ns_initparse(msg, len, &handle) != 0) {
338 //fprintf(stderr, "Unable to parse reply: %s\n", strerror(errno));
339 return -1;
340 }
341
342 for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
343 if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) {
344 //fprintf(stderr, "Unable to parse resource record: %s\n", strerror(errno));
345 return -1;
346 }
347
348 if (bb_style_counter && *bb_style_counter == 1)
349 printf("Name: %s\n", ns_rr_name(rr));
350
351 rdlen = ns_rr_rdlen(rr);
352
353 switch (ns_rr_type(rr))
354 {
355 case ns_t_a:
356 if (rdlen != 4) {
357 //fprintf(stderr, "Unexpected A record length\n");
358 return -1;
359 }
360 inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr));
361 if (bb_style_counter)
362 printf("Address %d: %s\n", (*bb_style_counter)++, astr);
363 else
364 printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr);
365 break;
366
367#if ENABLE_FEATURE_IPV6
368 case ns_t_aaaa:
369 if (rdlen != 16) {
370 //fprintf(stderr, "Unexpected AAAA record length\n");
371 return -1;
372 }
373 inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr));
374 if (bb_style_counter)
375 printf("Address %d: %s\n", (*bb_style_counter)++, astr);
376 else
377 printf("%s\thas AAAA address %s\n", ns_rr_name(rr), astr);
378 break;
379#endif
380
381 case ns_t_ns:
382 if (!format)
383 format = "%s\tnameserver = %s\n";
384 /* fall through */
385
386 case ns_t_cname:
387 if (!format)
388 format = "%s\tcanonical name = %s\n";
389 /* fall through */
390
391 case ns_t_ptr:
392 if (!format)
393 format = "%s\tname = %s\n";
394 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
395 ns_rr_rdata(rr), dname, sizeof(dname)) < 0) {
396 //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
397 return -1;
398 }
399 printf(format, ns_rr_name(rr), dname);
400 break;
401
402 case ns_t_mx:
403 if (rdlen < 2) {
404 fprintf(stderr, "MX record too short\n");
405 return -1;
406 }
407 n = ns_get16(ns_rr_rdata(rr));
408 if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
409 ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0) {
410 //fprintf(stderr, "Cannot uncompress MX domain: %s\n", strerror(errno));
411 return -1;
412 }
413 printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname);
414 break;
415
416 case ns_t_txt:
417 if (rdlen < 1) {
418 //fprintf(stderr, "TXT record too short\n");
419 return -1;
420 }
421 n = *(unsigned char *)ns_rr_rdata(rr);
422 if (n > 0) {
423 memset(dname, 0, sizeof(dname));
424 memcpy(dname, ns_rr_rdata(rr) + 1, n);
425 printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname);
426 }
427 break;
428
429 case ns_t_soa:
430 if (rdlen < 20) {
431 //fprintf(stderr, "SOA record too short\n");
432 return -1;
433 }
434
435 printf("%s\n", ns_rr_name(rr));
436
437 cp = ns_rr_rdata(rr);
438 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
439 cp, dname, sizeof(dname));
440
441 if (n < 0) {
442 //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
443 return -1;
444 }
445
446 printf("\torigin = %s\n", dname);
447 cp += n;
448
449 n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
450 cp, dname, sizeof(dname));
451
452 if (n < 0) {
453 //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno));
454 return -1;
455 }
456
457 printf("\tmail addr = %s\n", dname);
458 cp += n;
459
460 printf("\tserial = %lu\n", ns_get32(cp));
461 cp += 4;
462
463 printf("\trefresh = %lu\n", ns_get32(cp));
464 cp += 4;
465
466 printf("\tretry = %lu\n", ns_get32(cp));
467 cp += 4;
468
469 printf("\texpire = %lu\n", ns_get32(cp));
470 cp += 4;
471
472 printf("\tminimum = %lu\n", ns_get32(cp));
473 break;
474
475 default:
476 break;
477 }
478 }
479
480 return i;
481}
482
483static int parse_nsaddr(const char *addrstr, len_and_sockaddr *lsa)
484{
485 char *hash;
486 unsigned port = G.default_port;
487 IF_FEATURE_IPV6(unsigned scope;)
488
489 dbg("%s: addrstr:'%s'\n", __func__, addrstr);
490
491 hash = strchr(addrstr, '#');
492
493 if (hash) {
494 *hash++ = '\0';
495 port = bb_strtou(hash, NULL, 10);
496 if (errno || port > 65535) {
497 errno = EINVAL;
498 return -1;
499 }
500 }
501
502#if ENABLE_FEATURE_IPV6
503 scope = 0;
504 hash = strchr(addrstr, '%');
505 if (hash) {
506 char ifname[IFNAMSIZ];
507 int i;
508
509 hash++;
510 for (i = 0; hash[i] != '\0' && hash[i] != '#'; i++) {
511 if (i >= IFNAMSIZ) {
512 errno = ENODEV;
513 return -1;
514 }
515 ifname[i] = hash[i];
516 }
517 ifname[i] = '\0';
518
519 scope = if_nametoindex(ifname);
520 if (scope == 0) {
521 errno = ENODEV;
522 return -1;
523 }
524 }
525
526 if (inet_pton(AF_INET6, addrstr, &lsa->u.sin6.sin6_addr)) {
527 lsa->u.sin6.sin6_family = AF_INET6;
528 lsa->u.sin6.sin6_port = htons(port);
529 lsa->u.sin6.sin6_scope_id = scope;
530 lsa->len = sizeof(lsa->u.sin6);
531 return 0;
532 }
533#endif
534
535 if (inet_pton(AF_INET, addrstr, &lsa->u.sin.sin_addr)) {
536 lsa->u.sin.sin_family = AF_INET;
537 lsa->u.sin.sin_port = htons(port);
538 lsa->len = sizeof(lsa->u.sin);
539 return 0;
540 }
541
542 errno = EINVAL;
543 return -1;
544}
545
546static char *make_ptr(char resbuf[80], const char *addrstr)
547{
548 unsigned char addr[16];
549 int i;
550
551#if ENABLE_FEATURE_IPV6
552 if (inet_pton(AF_INET6, addrstr, addr)) {
553 if (memcmp(addr, v4_mapped, 12) != 0) {
554 char *ptr = resbuf;
555 for (i = 0; i < 16; i++) {
556 *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] & 0xf];
557 *ptr++ = '.';
558 *ptr++ = 0x20 | bb_hexdigits_upcase[(unsigned char)addr[15 - i] >> 4];
559 *ptr++ = '.';
560 }
561 strcpy(ptr, "ip6.arpa");
562 }
563 else {
564 sprintf(resbuf, "%u.%u.%u.%u.in-addr.arpa",
565 addr[15], addr[14], addr[13], addr[12]);
566 }
567 return resbuf;
568 }
569#endif
570
571 if (inet_pton(AF_INET, addrstr, addr)) {
572 sprintf(resbuf, "%u.%u.%u.%u.in-addr.arpa",
573 addr[3], addr[2], addr[1], addr[0]);
574 return resbuf;
575 }
576
577 return NULL;
578}
579
580#if ENABLE_FEATURE_IPV6
581static void to_v4_mapped(len_and_sockaddr *a)
582{
583 if (a->u.sa.sa_family != AF_INET)
584 return;
585
586 /* Order is important */
587//FIXME: port?
588 memcpy(a->u.sin6.sin6_addr.s6_addr + 12, &a->u.sin.sin_addr, 4);
589 memcpy(a->u.sin6.sin6_addr.s6_addr, v4_mapped, 12);
590
591 a->u.sin6.sin6_family = AF_INET6;
592 a->u.sin6.sin6_flowinfo = 0;
593 a->u.sin6.sin6_scope_id = 0;
594 a->len = sizeof(a->u.sin6);
595}
596#endif
597
598/*
599 * Function logic borrowed & modified from musl libc, res_msend.c
600 */
601static int send_queries(struct ns *ns, int n_ns, struct query *queries, int n_queries)
602{
603 int fd;
604 int timeout = G.default_timeout * 1000, retry_interval, servfail_retry = 0;
605 len_and_sockaddr from = { };
606 int recvlen = 0;
607 int n_replies = 0;
608 struct pollfd pfd;
609 unsigned long t0, t1, t2;
610 int nn, qn, next_query = 0;
611
612 from.u.sa.sa_family = AF_INET;
613 from.len = sizeof(from.u.sin);
614#if ENABLE_FEATURE_IPV6
615 for (nn = 0; nn < n_ns; nn++) {
616 if (ns[nn].addr.u.sa.sa_family == AF_INET6) {
617 from.u.sa.sa_family = AF_INET6;
618 from.len = sizeof(from.u.sin6);
619 break;
620 }
621 }
622#endif
623
624 /* Get local address and open/bind a socket */
625#if ENABLE_FEATURE_IPV6
626 fd = socket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
627 /* Handle case where system lacks IPv6 support */
628 if (fd < 0) {
629 if (from.u.sa.sa_family != AF_INET6 || errno != EAFNOSUPPORT)
630 bb_perror_msg_and_die("socket");
631 fd = xsocket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
632 from.u.sa.sa_family = AF_INET;
633 }
634#else
635 fd = xsocket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
636#endif
637 xbind(fd, &from.u.sa, from.len);
638
639#if ENABLE_FEATURE_IPV6
640 /* Convert any IPv4 addresses in a mixed environment to v4-mapped */
641 if (from.u.sa.sa_family == AF_INET6) {
642 setsockopt_1(fd, IPPROTO_IPV6, IPV6_V6ONLY);
643 for (nn = 0; nn < n_ns; nn++)
644 to_v4_mapped(&ns[nn].addr);
645 }
646#endif
647
648 pfd.fd = fd;
649 pfd.events = POLLIN;
650 retry_interval = timeout / G.default_retry;
651 t0 = t2 = monotonic_ms();
652 t1 = t2 - retry_interval;
653
654 for (; t2 - t0 < timeout; t2 = monotonic_ms()) {
655 if (t2 - t1 >= retry_interval) {
656 for (qn = 0; qn < n_queries; qn++) {
657 if (queries[qn].rlen)
658 continue;
659
660 for (nn = 0; nn < n_ns; nn++) {
661 sendto(fd, queries[qn].query, queries[qn].qlen,
662 MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len);
663 }
664 }
665
666 t1 = t2;
667 servfail_retry = 2 * n_queries;
668 }
669
670 /* Wait for a response, or until time to retry */
671 if (poll(&pfd, 1, t1 + retry_interval - t2) <= 0)
672 continue;
673
674 while (1) {
675 recvlen = recvfrom(fd, queries[next_query].reply,
676 sizeof(queries[next_query].reply), 0,
677 &from.u.sa, &from.len
678 );
679
680 /* read error */
681 if (recvlen < 0)
682 break;
683
684 /* Ignore non-identifiable packets */
685 if (recvlen < 4)
686 continue;
687
688 /* Ignore replies from addresses we didn't send to */
689 for (nn = 0; nn < n_ns; nn++)
690 if (memcmp(&from.u.sa, &ns[nn].addr.u.sa, from.len) == 0)
691 break;
692
693 if (nn >= n_ns)
694 continue;
695
696 /* Find which query this answer goes with, if any */
697 for (qn = next_query; qn < n_queries; qn++)
698 if (memcmp(queries[next_query].reply, queries[qn].query, 2) == 0)
699 break;
700
701 if (qn >= n_queries || queries[qn].rlen)
702 continue;
703
704 queries[qn].rcode = queries[next_query].reply[3] & 15;
705 queries[qn].latency = monotonic_ms() - t0;
706 queries[qn].n_ns = nn;
707
708 ns[nn].replies++;
709
710 /* Only accept positive or negative responses;
711 * retry immediately on server failure, and ignore
712 * all other codes such as refusal. */
713 switch (queries[qn].rcode) {
714 case 0:
715 case 3:
716 break;
717
718 case 2:
719 if (servfail_retry && servfail_retry--) {
720 ns[nn].failures++;
721 sendto(fd, queries[qn].query, queries[qn].qlen,
722 MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len);
723 }
724 /* fall through */
725
726 default:
727 continue;
728 }
729
730 /* Store answer */
731 n_replies++;
732
733 queries[qn].rlen = recvlen;
734
735 if (qn == next_query) {
736 while (next_query < n_queries) {
737 if (!queries[next_query].rlen)
738 break;
739
740 next_query++;
741 }
742 }
743 else {
744 memcpy(queries[qn].reply, queries[next_query].reply, recvlen);
745 }
746
747 if (next_query >= n_queries)
748 return n_replies;
749 }
750 }
751
752 return n_replies;
753}
754
755///FIXME: use standard lsa = xhost2sockaddr(host, port) instead
756
757static struct ns *add_ns(struct ns **ns, int *n_ns, const char *addr)
758{
759 char portstr[sizeof("65535")], *p;
760 len_and_sockaddr a = { };
761 struct ns *tmp;
762 struct addrinfo *ai, *aip, hints = {
763 .ai_flags = AI_NUMERICSERV,
764 .ai_socktype = SOCK_DGRAM
765 };
766
767 dbg("%s: addr:'%s'\n", __func__, addr);
768
769 if (parse_nsaddr(addr, &a)) {
770 /* Maybe we got a domain name, attempt to resolve it using the standard
771 * resolver routines */
772
773 p = strchr(addr, '#');
774 snprintf(portstr, sizeof(portstr), "%hu",
775 (unsigned short)(p ? strtoul(p, NULL, 10) : G.default_port));
776
777 if (!getaddrinfo(addr, portstr, &hints, &ai)) {
778 for (aip = ai; aip; aip = aip->ai_next) {
779 if (aip->ai_addr->sa_family != AF_INET &&
780 aip->ai_addr->sa_family != AF_INET6)
781 continue;
782
783#if ! ENABLE_FEATURE_IPV6
784 if (aip->ai_addr->sa_family != AF_INET)
785 continue;
786#endif
787
788 tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1));
789
790 if (!tmp)
791 return NULL;
792
793 *ns = tmp;
794
795 (*ns)[*n_ns].name = addr;
796 (*ns)[*n_ns].replies = 0;
797 (*ns)[*n_ns].failures = 0;
798 (*ns)[*n_ns].addr.len = aip->ai_addrlen;
799
800 memcpy(&(*ns)[*n_ns].addr.u.sa, aip->ai_addr, aip->ai_addrlen);
801
802 (*n_ns)++;
803 }
804
805 freeaddrinfo(ai);
806
807 return &(*ns)[*n_ns];
808 }
809
810 return NULL;
811 }
812
813 tmp = xrealloc(*ns, sizeof(**ns) * (*n_ns + 1));
814 *ns = tmp;
815
816 (*ns)[*n_ns].addr = a;
817 (*ns)[*n_ns].name = addr;
818 (*ns)[*n_ns].replies = 0;
819 (*ns)[*n_ns].failures = 0;
820
821 return &(*ns)[(*n_ns)++];
822}
823
824static int parse_resolvconf(struct ns **ns, int *n_ns)
825{
826 int prev_n_ns = *n_ns;
827 FILE *resolv;
828
829 resolv = fopen("/etc/resolv.conf", "r");
830 if (resolv) {
831 char line[128], *p;
832
833 while (fgets(line, sizeof(line), resolv)) {
834 p = strtok(line, " \t\n");
835
836 if (!p || strcmp(p, "nameserver") != 0)
837 continue;
838
839 p = strtok(NULL, " \t\n");
840
841 if (!p)
842 continue;
843
844 if (!add_ns(ns, n_ns, xstrdup(p))) {
845 free(p);
846 break;
847 }
848 }
849
850 fclose(resolv);
851 }
852
853 return *n_ns - prev_n_ns;
854}
855
856static struct query *add_query(struct query **queries, int *n_queries,
857 int type, const char *dname)
858{
859 struct query *new_q;
860 int pos;
861 ssize_t qlen;
862
863 pos = *n_queries;
864 *n_queries = pos + 1;
865 *queries = new_q = xrealloc(*queries, sizeof(**queries) * (pos + 1));
866
867 dbg("new query#%u type %u for '%s'\n", pos, type, dname);
868 new_q += pos;
869 memset(new_q, 0, sizeof(*new_q));
870
871 qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL,
872 new_q->query, sizeof(new_q->query));
873
874 new_q->qlen = qlen;
875 new_q->name = dname;
876
877 return new_q;
878}
879
880//FIXME: use xmalloc_sockaddr2dotted[_noport]() instead of sal2str()
881
882#define SIZEOF_SAL2STR_BUF (INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1 + 5 + 1)
883static char *sal2str(char buf[SIZEOF_SAL2STR_BUF], len_and_sockaddr *a)
884{
885 char *p = buf;
886
887#if ENABLE_FEATURE_IPV6
888 if (a->u.sa.sa_family == AF_INET6) {
889 inet_ntop(AF_INET6, &a->u.sin6.sin6_addr, buf, SIZEOF_SAL2STR_BUF);
890 p += strlen(p);
891
892 if (a->u.sin6.sin6_scope_id) {
893 if (if_indextoname(a->u.sin6.sin6_scope_id, p + 1)) {
894 *p++ = '%';
895 p += strlen(p);
896 }
897 }
898 } else
899#endif
900 {
901 inet_ntop(AF_INET, &a->u.sin.sin_addr, buf, SIZEOF_SAL2STR_BUF);
902 p += strlen(p);
903 }
904
905 sprintf(p, "#%hu", ntohs(a->u.sin.sin_port));
906
907 return buf;
908}
909
910int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
911int nslookup_main(int argc UNUSED_PARAM, char **argv)
912{
913 struct ns *ns;
914 struct query *queries;
915 llist_t *type_strings;
916 int n_ns, n_queries;
917 int bb_style_counter = 0;
918 unsigned types;
919 int rc;
920 int opts;
921 enum {
922 OPT_stats = (1 << 4),
923 };
924
925 INIT_G();
926
927 type_strings = NULL;
928#if ENABLE_FEATURE_NSLOOKUP_LONG_OPTIONS
929 opts = getopt32long(argv, "^"
930 "+" /* '+': stop at first non-option (why?) */
931 "q:*p:+r:+t:+s"
932 "\0"
933 "-1:q::", /* minimum 1 arg, -q is a list */
934 "type\0" Required_argument "q"
935 "querytype\0" Required_argument "q"
936 "port\0" Required_argument "p"
937 "retry\0" Required_argument "r"
938 "timeout\0" Required_argument "t"
939 "stats\0" No_argument "s",
940 &type_strings, &G.default_port,
941 &G.default_retry, &G.default_timeout
942 );
943#else
944 opts = getopt32(argv, "^"
945 "+" /* '+': stop at first non-option (why?) */
946 "q:*p:+r:+t:+s"
947 "\0"
948 "-1:q::", /* minimum 1 arg, -q is a list */
949 &type_strings, &G.default_port,
950 &G.default_retry, &G.default_timeout
951 );
952#endif
953 if (G.default_port > 65535)
954 bb_error_msg_and_die("invalid server port");
955 if (G.default_retry == 0)
956 bb_error_msg_and_die("invalid retry value");
957 if (G.default_timeout == 0)
958 bb_error_msg_and_die("invalid timeout value");
959
960 types = 0;
961 while (type_strings) {
962 int c;
963 char *ptr, *chr;
964
965 ptr = llist_pop(&type_strings);
966
967 /* skip leading text, e.g. when invoked with -querytype=AAAA */
968 chr = strchr(ptr, '=');
969 if (chr)
970 ptr = chr + 1;
971
972 for (c = 0;; c++) {
973 if (!qtypes[c].name)
974 bb_error_msg_and_die("invalid query type \"%s\"", ptr);
975 if (strcmp(qtypes[c].name, ptr) == 0)
976 break;
977 }
978
979 types |= (1 << c);
980 }
981
982 argv += optind;
983
984 n_queries = 0;
985 queries = NULL;
986 do {
987 /* No explicit type given, guess query type.
988 * If we can convert the domain argument into a ptr (means that
989 * inet_pton() could read it) we assume a PTR request, else
990 * we issue A+AAAA queries and switch to an output format
991 * mimicking the one of the traditional nslookup applet. */
992 if (types == 0) {
993 char *ptr;
994 char buf80[80];
995
996 ptr = make_ptr(buf80, *argv);
997
998 if (ptr) {
999 add_query(&queries, &n_queries, T_PTR, ptr);
1000 }
1001 else {
1002 bb_style_counter = 1;
1003 add_query(&queries, &n_queries, T_A, *argv);
1004#if ENABLE_FEATURE_IPV6
1005 add_query(&queries, &n_queries, T_AAAA, *argv);
1006#endif
1007 }
1008 }
1009 else {
1010 int c;
1011 for (c = 0; qtypes[c].name; c++) {
1012 if (types & (1 << c))
1013 add_query(&queries, &n_queries, qtypes[c].type,
1014 *argv);
1015 }
1016 }
1017 argv++;
1018 } while (argv[0] && argv[1]);
1019
1020 /* Use given DNS server if present */
1021 n_ns = 0;
1022 ns = NULL;
1023 if (argv[0]) {
1024 if (!add_ns(&ns, &n_ns, argv[0]))
1025 bb_error_msg_and_die("invalid NS server address \"%s\"", argv[0]);
1026 } else {
1027 parse_resolvconf(&ns, &n_ns);
1028 /* Fall back to localhost if we could not find NS in resolv.conf */
1029 if (n_ns == 0)
1030 add_ns(&ns, &n_ns, "127.0.0.1");
1031 }
1032
1033 for (rc = 0; rc < n_ns; rc++) {
1034 int c = send_queries(&ns[rc], 1, queries, n_queries);
1035 if (c < 0)
1036 bb_perror_msg_and_die("can't send queries");
1037 if (c > 0)
1038 break;
1039 rc++;
1040 if (rc >= n_ns) {
1041 fprintf(stderr,
1042 ";; connection timed out; no servers could be reached\n\n");
1043 return EXIT_FAILURE;
1044 }
1045 }
1046
1047 printf("Server:\t\t%s\n", ns[rc].name);
1048 {
1049 char buf[SIZEOF_SAL2STR_BUF];
1050 printf("Address:\t%s\n", sal2str(buf, &ns[rc].addr));
1051 }
1052 if (opts & OPT_stats) {
1053 printf("Replies:\t%d\n", ns[rc].replies);
1054 printf("Failures:\t%d\n", ns[rc].failures);
1055 }
1056 printf("\n");
1057
1058 for (rc = 0; rc < n_queries; rc++) {
1059 int c;
1060
1061 if (opts & OPT_stats) {
1062 printf("Query #%d completed in %lums:\n", rc, queries[rc].latency);
1063 }
1064
1065 if (queries[rc].rcode != 0) {
1066 printf("** server can't find %s: %s\n", queries[rc].name,
1067 rcodes[queries[rc].rcode]);
1068 continue;
1069 }
1070
1071 c = 0;
1072
1073 if (queries[rc].rlen) {
1074 HEADER *header;
1075
1076 if (!bb_style_counter) {
1077 header = (HEADER *)queries[rc].reply;
1078 if (!header->aa)
1079 printf("Non-authoritative answer:\n");
1080 c = parse_reply(queries[rc].reply, queries[rc].rlen, NULL);
1081 }
1082 else {
1083 c = parse_reply(queries[rc].reply, queries[rc].rlen,
1084 &bb_style_counter);
1085 }
1086 }
1087
1088 if (c == 0)
1089 printf("*** Can't find %s: No answer\n", queries[rc].name);
1090 else if (c < 0)
1091 printf("*** Can't find %s: Parse error\n", queries[rc].name);
1092
1093 if (!bb_style_counter)
1094 printf("\n");
1095 }
1096
1097 if (ENABLE_FEATURE_CLEAN_UP) {
1098 free(ns);
1099 if (n_queries)
1100 free(queries);
1101 }
1102
1103 return EXIT_SUCCESS;
1104}
1105
1106#endif