diff --git a/package/utils/busybox/Config-defaults.in b/package/utils/busybox/Config-defaults.in index 168c73b24c..f9028620a1 100644 --- a/package/utils/busybox/Config-defaults.in +++ b/package/utils/busybox/Config-defaults.in @@ -2459,17 +2459,11 @@ config BUSYBOX_DEFAULT_FEATURE_NETSTAT_PRG default y config BUSYBOX_DEFAULT_NSLOOKUP bool - default n + default y config BUSYBOX_DEFAULT_FEATURE_NSLOOKUP_BIG - bool - default n -config BUSYBOX_DEFAULT_FEATURE_NSLOOKUP_LONG_OPTIONS - bool - default n -config BUSYBOX_DEFAULT_NSLOOKUP_OPENWRT bool default y -config BUSYBOX_DEFAULT_FEATURE_NSLOOKUP_OPENWRT_LONG_OPTIONS +config BUSYBOX_DEFAULT_FEATURE_NSLOOKUP_LONG_OPTIONS bool default n config BUSYBOX_DEFAULT_NTPD diff --git a/package/utils/busybox/Makefile b/package/utils/busybox/Makefile index e4e176870c..c2f1c1be45 100644 --- a/package/utils/busybox/Makefile +++ b/package/utils/busybox/Makefile @@ -86,9 +86,6 @@ LDLIBS:=m crypt endif LDLIBS += $(call BUSYBOX_IF_ENABLED,PAM,pam pam_misc pthread) -ifeq ($(CONFIG_USE_GLIBC),y) - LDLIBS += $(call BUSYBOX_IF_ENABLED,NSLOOKUP_OPENWRT,resolv) -endif ifeq ($(BUILD_VARIANT),selinux) LDLIBS += selinux sepol diff --git a/package/utils/busybox/config/networking/Config.in b/package/utils/busybox/config/networking/Config.in index 6608f5899d..3e1c4c4c04 100644 --- a/package/utils/busybox/config/networking/Config.in +++ b/package/utils/busybox/config/networking/Config.in @@ -774,19 +774,7 @@ config BUSYBOX_CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS bool "Enable long options" default BUSYBOX_DEFAULT_FEATURE_NSLOOKUP_LONG_OPTIONS depends on BUSYBOX_CONFIG_FEATURE_NSLOOKUP_BIG && BUSYBOX_CONFIG_LONG_OPTS -config BUSYBOX_CONFIG_NSLOOKUP_OPENWRT - bool "nslookup_openwrt" - depends on !BUSYBOX_CONFIG_NSLOOKUP - default BUSYBOX_DEFAULT_NSLOOKUP_OPENWRT - help - nslookup is a tool to query Internet name servers (LEDE flavor). -config BUSYBOX_CONFIG_FEATURE_NSLOOKUP_OPENWRT_LONG_OPTIONS - bool "Enable long options" - default BUSYBOX_DEFAULT_FEATURE_NSLOOKUP_OPENWRT_LONG_OPTIONS - depends on BUSYBOX_CONFIG_NSLOOKUP_OPENWRT && BUSYBOX_CONFIG_LONG_OPTS - help - Support long options for the nslookup applet. config BUSYBOX_CONFIG_NTPD bool "ntpd (22 kb)" default BUSYBOX_DEFAULT_NTPD diff --git a/package/utils/busybox/patches/230-add_nslookup_lede.patch b/package/utils/busybox/patches/230-add_nslookup_lede.patch deleted file mode 100644 index 280d4e238e..0000000000 --- a/package/utils/busybox/patches/230-add_nslookup_lede.patch +++ /dev/null @@ -1,971 +0,0 @@ -From ab0f8bb80527928f513297ab93e3ec8c8b48dd50 Mon Sep 17 00:00:00 2001 -From: Jo-Philipp Wich -Date: Tue, 14 Mar 2017 22:21:34 +0100 -Subject: [PATCH] networking: add LEDE nslookup applet - -Add a new LEDE nslookup applet which is compatible with musl libc -and providing more features like ability to specify query type. - -In contrast to busybox' builtin nslookup applet, this variant does -not rely on libc resolver internals but uses explicit send logic -and the libresolv primitives to parse received DNS responses. - -Signed-off-by: Jo-Philipp Wich ---- - Makefile.flags | 6 + - networking/nslookup_lede.c | 915 +++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 921 insertions(+) - create mode 100644 networking/nslookup_lede.c - ---- a/Makefile.flags -+++ b/Makefile.flags -@@ -158,6 +158,12 @@ endif - # libm may be needed for dc, awk, ntpd - # librt may be needed for clock_gettime() - -+# nslookup_lede might need the resolv library -+RESOLV_AVAILABLE := $(shell echo 'int main(void){res_init();return 0;}' >resolvtest.c; $(CC) $(CFLAGS) -include resolv.h -lresolv -o /dev/null resolvtest.c >/dev/null 2>&1 && echo "y"; rm resolvtest.c) -+ifeq ($(RESOLV_AVAILABLE),y) -+LDLIBS += resolv -+endif -+ - # libpam may use libpthread, libdl and/or libaudit. - # On some platforms that requires an explicit -lpthread, -ldl, -laudit. - # However, on *other platforms* it fails when some of those flags ---- /dev/null -+++ b/networking/nslookup_lede.c -@@ -0,0 +1,934 @@ -+/* -+ * nslookup_lede - musl compatible replacement for busybox nslookup -+ * -+ * Copyright (C) 2017 Jo-Philipp Wich -+ * -+ * Permission to use, copy, modify, and/or distribute this software for any -+ * purpose with or without fee is hereby granted, provided that the above -+ * copyright notice and this permission notice appear in all copies. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ */ -+ -+//config:config NSLOOKUP_OPENWRT -+//config: bool "nslookup_openwrt" -+//config: depends on !NSLOOKUP -+//config: default y -+//config: help -+//config: nslookup is a tool to query Internet name servers (LEDE flavor). -+//config: -+//config:config FEATURE_NSLOOKUP_OPENWRT_LONG_OPTIONS -+//config: bool "Enable long options" -+//config: default y -+//config: depends on NSLOOKUP_OPENWRT && LONG_OPTS -+//config: help -+//config: Support long options for the nslookup applet. -+ -+//applet:IF_NSLOOKUP_OPENWRT(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP)) -+ -+//kbuild:lib-$(CONFIG_NSLOOKUP_OPENWRT) += nslookup_lede.o -+ -+//usage:#define nslookup_lede_trivial_usage -+//usage: "[HOST] [SERVER]" -+//usage:#define nslookup_lede_full_usage "\n\n" -+//usage: "Query the nameserver for the IP address of the given HOST\n" -+//usage: "optionally using a specified DNS server" -+//usage: -+//usage:#define nslookup_lede_example_usage -+//usage: "$ nslookup localhost\n" -+//usage: "Server: default\n" -+//usage: "Address: default\n" -+//usage: "\n" -+//usage: "Name: debian\n" -+//usage: "Address: 127.0.0.1\n" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "libbb.h" -+ -+struct ns { -+ const char *name; -+ len_and_sockaddr addr; -+ int failures; -+ int replies; -+}; -+ -+struct query { -+ const char *name; -+ size_t qlen, rlen; -+ unsigned char query[512], reply[512]; -+ unsigned long latency; -+ int rcode, n_ns; -+}; -+ -+static struct { -+ int type; -+ const char *name; -+} qtypes[] = { -+ { ns_t_soa, "SOA" }, -+ { ns_t_ns, "NS" }, -+ { ns_t_a, "A" }, -+#if ENABLE_FEATURE_IPV6 -+ { ns_t_aaaa, "AAAA" }, -+#endif -+ { ns_t_cname, "CNAME" }, -+ { ns_t_mx, "MX" }, -+ { ns_t_txt, "TXT" }, -+ { ns_t_srv, "SRV" }, -+ { ns_t_ptr, "PTR" }, -+ { ns_t_any, "ANY" }, -+ { } -+}; -+ -+static const char *rcodes[] = { -+ "NOERROR", -+ "FORMERR", -+ "SERVFAIL", -+ "NXDOMAIN", -+ "NOTIMP", -+ "REFUSED", -+ "YXDOMAIN", -+ "YXRRSET", -+ "NXRRSET", -+ "NOTAUTH", -+ "NOTZONE", -+ "RESERVED11", -+ "RESERVED12", -+ "RESERVED13", -+ "RESERVED14", -+ "RESERVED15", -+ "BADVERS" -+}; -+ -+static unsigned int default_port = 53; -+static unsigned int default_retry = 2; -+static unsigned int default_timeout = 5; -+ -+ -+static int parse_reply(const unsigned char *msg, size_t len, int *bb_style_counter) -+{ -+ ns_msg handle; -+ ns_rr rr; -+ int i, n, rdlen; -+ const char *format = NULL; -+ char astr[INET6_ADDRSTRLEN], dname[MAXDNAME]; -+ const unsigned char *cp; -+ -+ if (ns_initparse(msg, len, &handle) != 0) { -+ //fprintf(stderr, "Unable to parse reply: %s\n", strerror(errno)); -+ return -1; -+ } -+ -+ for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) { -+ if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) { -+ //fprintf(stderr, "Unable to parse resource record: %s\n", strerror(errno)); -+ return -1; -+ } -+ -+ if (bb_style_counter && *bb_style_counter == 1) -+ printf("Name: %s\n", ns_rr_name(rr)); -+ -+ rdlen = ns_rr_rdlen(rr); -+ -+ switch (ns_rr_type(rr)) -+ { -+ case ns_t_a: -+ if (rdlen != 4) { -+ //fprintf(stderr, "Unexpected A record length\n"); -+ return -1; -+ } -+ inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr)); -+ if (bb_style_counter) -+ printf("Address %d: %s\n", (*bb_style_counter)++, astr); -+ else -+ printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr); -+ break; -+ -+#if ENABLE_FEATURE_IPV6 -+ case ns_t_aaaa: -+ if (rdlen != 16) { -+ //fprintf(stderr, "Unexpected AAAA record length\n"); -+ return -1; -+ } -+ inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr)); -+ if (bb_style_counter) -+ printf("Address %d: %s\n", (*bb_style_counter)++, astr); -+ else -+ printf("%s\thas AAAA address %s\n", ns_rr_name(rr), astr); -+ break; -+#endif -+ -+ case ns_t_ns: -+ if (!format) -+ format = "%s\tnameserver = %s\n"; -+ /* fall through */ -+ -+ case ns_t_cname: -+ if (!format) -+ format = "%s\tcanonical name = %s\n"; -+ /* fall through */ -+ -+ case ns_t_ptr: -+ if (!format) -+ format = "%s\tname = %s\n"; -+ if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), -+ ns_rr_rdata(rr), dname, sizeof(dname)) < 0) { -+ //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); -+ return -1; -+ } -+ printf(format, ns_rr_name(rr), dname); -+ break; -+ -+ case ns_t_mx: -+ if (rdlen < 2) { -+ fprintf(stderr, "MX record too short\n"); -+ return -1; -+ } -+ n = ns_get16(ns_rr_rdata(rr)); -+ if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), -+ ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0) { -+ //fprintf(stderr, "Cannot uncompress MX domain: %s\n", strerror(errno)); -+ return -1; -+ } -+ printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname); -+ break; -+ -+ case ns_t_txt: -+ if (rdlen < 1) { -+ //fprintf(stderr, "TXT record too short\n"); -+ return -1; -+ } -+ n = *(unsigned char *)ns_rr_rdata(rr); -+ if (n > 0) { -+ memset(dname, 0, sizeof(dname)); -+ memcpy(dname, ns_rr_rdata(rr) + 1, n); -+ printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname); -+ } -+ break; -+ -+ case ns_t_srv: -+ if (rdlen < 6) { -+ //printf("SRV record too short\n"); -+ return -1; -+ } -+ -+ cp = ns_rr_rdata(rr); -+ n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), -+ cp + 6, dname, sizeof(dname)); -+ -+ if (n < 0) { -+ //printf("Unable to uncompress domain: %s\n", strerror(errno)); -+ return -1; -+ } -+ -+ printf("%s\tservice = %hu %hu %hu %s\n", ns_rr_name(rr), -+ ns_get16(cp), ns_get16(cp + 2), ns_get16(cp + 4), dname); -+ break; -+ -+ case ns_t_soa: -+ if (rdlen < 20) { -+ //fprintf(stderr, "SOA record too short\n"); -+ return -1; -+ } -+ -+ printf("%s\n", ns_rr_name(rr)); -+ -+ cp = ns_rr_rdata(rr); -+ n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), -+ cp, dname, sizeof(dname)); -+ -+ if (n < 0) { -+ //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); -+ return -1; -+ } -+ -+ printf("\torigin = %s\n", dname); -+ cp += n; -+ -+ n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), -+ cp, dname, sizeof(dname)); -+ -+ if (n < 0) { -+ //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); -+ return -1; -+ } -+ -+ printf("\tmail addr = %s\n", dname); -+ cp += n; -+ -+ printf("\tserial = %lu\n", ns_get32(cp)); -+ cp += 4; -+ -+ printf("\trefresh = %lu\n", ns_get32(cp)); -+ cp += 4; -+ -+ printf("\tretry = %lu\n", ns_get32(cp)); -+ cp += 4; -+ -+ printf("\texpire = %lu\n", ns_get32(cp)); -+ cp += 4; -+ -+ printf("\tminimum = %lu\n", ns_get32(cp)); -+ break; -+ -+ default: -+ break; -+ } -+ } -+ -+ return i; -+} -+ -+static int parse_nsaddr(const char *addrstr, len_and_sockaddr *lsa) -+{ -+ char *eptr, *hash, ifname[IFNAMSIZ]; -+ unsigned int port = default_port; -+ unsigned int scope = 0; -+ -+ hash = strchr(addrstr, '#'); -+ -+ if (hash) { -+ *hash++ = '\0'; -+ port = strtoul(hash, &eptr, 10); -+ -+ if (eptr == hash || *eptr != '\0' || port > 65535) { -+ errno = EINVAL; -+ return -1; -+ } -+ } -+ -+ hash = strchr(addrstr, '%'); -+ -+ if (hash) { -+ for (eptr = ++hash; *eptr != '\0' && *eptr != '#'; eptr++) { -+ if ((eptr - hash) >= IFNAMSIZ) { -+ errno = ENODEV; -+ return -1; -+ } -+ -+ ifname[eptr - hash] = *eptr; -+ } -+ -+ ifname[eptr - hash] = '\0'; -+ scope = if_nametoindex(ifname); -+ -+ if (scope == 0) { -+ errno = ENODEV; -+ return -1; -+ } -+ } -+ -+#if ENABLE_FEATURE_IPV6 -+ if (inet_pton(AF_INET6, addrstr, &lsa->u.sin6.sin6_addr)) { -+ lsa->u.sin6.sin6_family = AF_INET6; -+ lsa->u.sin6.sin6_port = htons(port); -+ lsa->u.sin6.sin6_scope_id = scope; -+ lsa->len = sizeof(lsa->u.sin6); -+ return 0; -+ } -+#endif -+ -+ if (!scope && inet_pton(AF_INET, addrstr, &lsa->u.sin.sin_addr)) { -+ lsa->u.sin.sin_family = AF_INET; -+ lsa->u.sin.sin_port = htons(port); -+ lsa->len = sizeof(lsa->u.sin); -+ return 0; -+ } -+ -+ errno = EINVAL; -+ return -1; -+} -+ -+static char *make_ptr(const char *addrstr) -+{ -+ const char *hexdigit = "0123456789abcdef"; -+ static char ptrstr[73]; -+ unsigned char addr[16]; -+ char *ptr = ptrstr; -+ int i; -+ -+ if (inet_pton(AF_INET6, addrstr, addr)) { -+ if (memcmp(addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12) != 0) { -+ for (i = 0; i < 16; i++) { -+ *ptr++ = hexdigit[(unsigned char)addr[15 - i] & 0xf]; -+ *ptr++ = '.'; -+ *ptr++ = hexdigit[(unsigned char)addr[15 - i] >> 4]; -+ *ptr++ = '.'; -+ } -+ strcpy(ptr, "ip6.arpa"); -+ } -+ else { -+ sprintf(ptr, "%u.%u.%u.%u.in-addr.arpa", -+ addr[15], addr[14], addr[13], addr[12]); -+ } -+ -+ return ptrstr; -+ } -+ -+ if (inet_pton(AF_INET, addrstr, addr)) { -+ sprintf(ptr, "%u.%u.%u.%u.in-addr.arpa", -+ addr[3], addr[2], addr[1], addr[0]); -+ return ptrstr; -+ } -+ -+ return NULL; -+} -+ -+static unsigned long mtime(void) -+{ -+ struct timespec ts; -+ clock_gettime(CLOCK_REALTIME, &ts); -+ return (unsigned long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; -+} -+ -+#if ENABLE_FEATURE_IPV6 -+static void to_v4_mapped(len_and_sockaddr *a) -+{ -+ if (a->u.sa.sa_family != AF_INET) -+ return; -+ -+ memcpy(a->u.sin6.sin6_addr.s6_addr + 12, -+ &a->u.sin.sin_addr, 4); -+ -+ memcpy(a->u.sin6.sin6_addr.s6_addr, -+ "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); -+ -+ a->u.sin6.sin6_family = AF_INET6; -+ a->u.sin6.sin6_flowinfo = 0; -+ a->u.sin6.sin6_scope_id = 0; -+ a->len = sizeof(a->u.sin6); -+} -+#endif -+ -+ -+/* -+ * Function logic borrowed & modified from musl libc, res_msend.c -+ */ -+ -+static int send_queries(struct ns *ns, int n_ns, struct query *queries, int n_queries) -+{ -+ int fd; -+ int timeout = default_timeout * 1000, retry_interval, servfail_retry = 0; -+ len_and_sockaddr from = { }; -+#if ENABLE_FEATURE_IPV6 -+ int one = 1; -+#endif -+ int recvlen = 0; -+ int n_replies = 0; -+ struct pollfd pfd; -+ unsigned long t0, t1, t2; -+ int nn, qn, next_query = 0; -+ -+ from.u.sa.sa_family = AF_INET; -+ from.len = sizeof(from.u.sin); -+ -+#if ENABLE_FEATURE_IPV6 -+ for (nn = 0; nn < n_ns; nn++) { -+ if (ns[nn].addr.u.sa.sa_family == AF_INET6) { -+ from.u.sa.sa_family = AF_INET6; -+ from.len = sizeof(from.u.sin6); -+ break; -+ } -+ } -+#endif -+ -+ /* Get local address and open/bind a socket */ -+ fd = socket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); -+ -+#if ENABLE_FEATURE_IPV6 -+ /* Handle case where system lacks IPv6 support */ -+ if (fd < 0 && from.u.sa.sa_family == AF_INET6 && errno == EAFNOSUPPORT) { -+ fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); -+ from.u.sa.sa_family = AF_INET; -+ } -+#endif -+ -+ if (fd < 0) -+ return -1; -+ -+ if (bind(fd, &from.u.sa, from.len) < 0) { -+ close(fd); -+ return -1; -+ } -+ -+#if ENABLE_FEATURE_IPV6 -+ /* Convert any IPv4 addresses in a mixed environment to v4-mapped */ -+ if (from.u.sa.sa_family == AF_INET6) { -+ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); -+ -+ for (nn = 0; nn < n_ns; nn++) -+ to_v4_mapped(&ns[nn].addr); -+ } -+#endif -+ -+ pfd.fd = fd; -+ pfd.events = POLLIN; -+ retry_interval = timeout / default_retry; -+ t0 = t2 = mtime(); -+ t1 = t2 - retry_interval; -+ -+ for (; t2 - t0 < timeout; t2 = mtime()) { -+ if (t2 - t1 >= retry_interval) { -+ for (qn = 0; qn < n_queries; qn++) { -+ if (queries[qn].rlen) -+ continue; -+ -+ for (nn = 0; nn < n_ns; nn++) { -+ sendto(fd, queries[qn].query, queries[qn].qlen, -+ MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len); -+ } -+ } -+ -+ t1 = t2; -+ servfail_retry = 2 * n_queries; -+ } -+ -+ /* Wait for a response, or until time to retry */ -+ if (poll(&pfd, 1, t1+retry_interval-t2) <= 0) -+ continue; -+ -+ while (1) { -+ recvlen = recvfrom(fd, queries[next_query].reply, -+ sizeof(queries[next_query].reply), 0, -+ &from.u.sa, &from.len); -+ -+ /* read error */ -+ if (recvlen < 0) -+ break; -+ -+ /* Ignore non-identifiable packets */ -+ if (recvlen < 4) -+ continue; -+ -+ /* Ignore replies from addresses we didn't send to */ -+ for (nn = 0; nn < n_ns; nn++) -+ if (memcmp(&from.u.sa, &ns[nn].addr.u.sa, from.len) == 0) -+ break; -+ -+ if (nn >= n_ns) -+ continue; -+ -+ /* Find which query this answer goes with, if any */ -+ for (qn = next_query; qn < n_queries; qn++) -+ if (!memcmp(queries[next_query].reply, queries[qn].query, 2)) -+ break; -+ -+ if (qn >= n_queries || queries[qn].rlen) -+ continue; -+ -+ queries[qn].rcode = queries[next_query].reply[3] & 15; -+ queries[qn].latency = mtime() - t0; -+ queries[qn].n_ns = nn; -+ -+ ns[nn].replies++; -+ -+ /* Only accept positive or negative responses; -+ * retry immediately on server failure, and ignore -+ * all other codes such as refusal. */ -+ switch (queries[qn].rcode) { -+ case 0: -+ case 3: -+ break; -+ -+ case 2: -+ if (servfail_retry && servfail_retry--) { -+ ns[nn].failures++; -+ sendto(fd, queries[qn].query, queries[qn].qlen, -+ MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len); -+ } -+ /* fall through */ -+ -+ default: -+ continue; -+ } -+ -+ /* Store answer */ -+ n_replies++; -+ -+ queries[qn].rlen = recvlen; -+ -+ if (qn == next_query) { -+ while (next_query < n_queries) { -+ if (!queries[next_query].rlen) -+ break; -+ -+ next_query++; -+ } -+ } -+ else { -+ memcpy(queries[qn].reply, queries[next_query].reply, recvlen); -+ } -+ -+ if (next_query >= n_queries) -+ return n_replies; -+ } -+ } -+ -+ return n_replies; -+} -+ -+static struct ns *add_ns(struct ns **ns, int *n_ns, const char *addr) -+{ -+ char portstr[sizeof("65535")], *p; -+ len_and_sockaddr a = { }; -+ struct ns *tmp; -+ struct addrinfo *ai, *aip, hints = { -+ .ai_flags = AI_NUMERICSERV, -+ .ai_socktype = SOCK_DGRAM -+ }; -+ -+ if (parse_nsaddr(addr, &a)) { -+ /* Maybe we got a domain name, attempt to resolve it using the standard -+ * resolver routines */ -+ -+ p = strchr(addr, '#'); -+ snprintf(portstr, sizeof(portstr), "%hu", -+ (unsigned short)(p ? strtoul(p, NULL, 10) : default_port)); -+ -+ if (!getaddrinfo(addr, portstr, &hints, &ai)) { -+ for (aip = ai; aip; aip = aip->ai_next) { -+ if (aip->ai_addr->sa_family != AF_INET && -+ aip->ai_addr->sa_family != AF_INET6) -+ continue; -+ -+#if ! ENABLE_FEATURE_IPV6 -+ if (aip->ai_addr->sa_family != AF_INET) -+ continue; -+#endif -+ -+ tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1)); -+ -+ if (!tmp) -+ return NULL; -+ -+ *ns = tmp; -+ -+ (*ns)[*n_ns].name = addr; -+ (*ns)[*n_ns].replies = 0; -+ (*ns)[*n_ns].failures = 0; -+ (*ns)[*n_ns].addr.len = aip->ai_addrlen; -+ -+ memcpy(&(*ns)[*n_ns].addr.u.sa, aip->ai_addr, aip->ai_addrlen); -+ -+ (*n_ns)++; -+ } -+ -+ freeaddrinfo(ai); -+ -+ return &(*ns)[*n_ns]; -+ } -+ -+ return NULL; -+ } -+ -+ tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1)); -+ -+ if (!tmp) -+ return NULL; -+ -+ *ns = tmp; -+ -+ (*ns)[*n_ns].addr = a; -+ (*ns)[*n_ns].name = addr; -+ (*ns)[*n_ns].replies = 0; -+ (*ns)[*n_ns].failures = 0; -+ -+ return &(*ns)[(*n_ns)++]; -+} -+ -+static int parse_resolvconf(struct ns **ns, int *n_ns) -+{ -+ int prev_n_ns = *n_ns; -+ char line[128], *p; -+ FILE *resolv; -+ -+ if ((resolv = fopen("/etc/resolv.conf", "r")) != NULL) { -+ while (fgets(line, sizeof(line), resolv)) { -+ p = strtok(line, " \t\n"); -+ -+ if (!p || strcmp(p, "nameserver")) -+ continue; -+ -+ p = strtok(NULL, " \t\n"); -+ -+ if (!p) -+ continue; -+ -+ if (!add_ns(ns, n_ns, strdup(p))) { -+ free(p); -+ break; -+ } -+ } -+ -+ fclose(resolv); -+ } -+ -+ return *n_ns - prev_n_ns; -+} -+ -+static struct query *add_query(struct query **queries, int *n_queries, -+ int type, const char *dname) -+{ -+ struct query *tmp; -+ ssize_t qlen; -+ -+ tmp = realloc(*queries, sizeof(**queries) * (*n_queries + 1)); -+ -+ if (!tmp) -+ return NULL; -+ -+ memset(&tmp[*n_queries], 0, sizeof(*tmp)); -+ -+ qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL, -+ tmp[*n_queries].query, sizeof(tmp[*n_queries].query)); -+ -+ tmp[*n_queries].qlen = qlen; -+ tmp[*n_queries].name = dname; -+ *queries = tmp; -+ -+ return &tmp[(*n_queries)++]; -+} -+ -+static char *sal2str(len_and_sockaddr *a) -+{ -+ static char buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1 + 5 + 1]; -+ char *p = buf; -+ -+#if ENABLE_FEATURE_IPV6 -+ if (a->u.sa.sa_family == AF_INET6) { -+ inet_ntop(AF_INET6, &a->u.sin6.sin6_addr, buf, sizeof(buf)); -+ p += strlen(p); -+ -+ if (a->u.sin6.sin6_scope_id) { -+ if (if_indextoname(a->u.sin6.sin6_scope_id, p + 1)) { -+ *p++ = '%'; -+ p += strlen(p); -+ } -+ } -+ } -+ else -+#endif -+ { -+ inet_ntop(AF_INET, &a->u.sin.sin_addr, buf, sizeof(buf)); -+ p += strlen(p); -+ } -+ -+ sprintf(p, "#%hu", ntohs(a->u.sin.sin_port)); -+ -+ return buf; -+} -+ -+int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -+int nslookup_main(int argc, char **argv) -+{ -+ int rc = 1; -+ char *ptr, *chr; -+ struct ns *ns = NULL; -+ struct query *queries = NULL; -+ llist_t *type_strings = NULL; -+ int n_ns = 0, n_queries = 0; -+ int c, opts, option_index = 0; -+ int stats = 0, bb_style_counter = 0; -+ unsigned int types = 0; -+ HEADER *header; -+ -+#if ENABLE_FEATURE_NSLOOKUP_OPENWRT_LONG_OPTIONS -+ static const char nslookup_longopts[] ALIGN1 = -+ "type\0" Required_argument "q" -+ "querytype\0" Required_argument "q" -+ "port\0" Required_argument "p" -+ "retry\0" Required_argument "r" -+ "timeout\0" Required_argument "t" -+ "stats\0" No_argument "s" -+ ; -+ -+ opts = getopt32long(argv, "^" "+q:*p:+r:+t:+s" "\0" "q::", -+ nslookup_longopts, -+ &type_strings, &default_port, -+ &default_retry, &default_timeout); -+#else -+ opts = getopt32(argv, "^" "+q:*p:+r:+t:+s" "\0" "q::", -+ &type_strings, &default_port, -+ &default_retry, &default_timeout); -+#endif -+ -+ while (type_strings) { -+ ptr = llist_pop(&type_strings); -+ -+ /* skip leading text, e.g. when invoked with -querytype=AAAA */ -+ if ((chr = strchr(ptr, '=')) != NULL) -+ ptr = chr + 1; -+ -+ for (c = 0; qtypes[c].name; c++) -+ if (!strcmp(qtypes[c].name, ptr)) -+ break; -+ -+ if (!qtypes[c].name) { -+ fprintf(stderr, "Invalid query type \"%s\"\n", ptr); -+ goto out; -+ } -+ -+ types |= (1 << c); -+ } -+ -+ if (default_port > 65535) { -+ fprintf(stderr, "Invalid server port\n"); -+ goto out; -+ } -+ -+ if (!default_retry) { -+ fprintf(stderr, "Invalid retry value\n"); -+ goto out; -+ } -+ -+ if (!default_timeout) { -+ fprintf(stderr, "Invalid timeout value\n"); -+ goto out; -+ } -+ -+ stats = (opts & 16); -+ -+ if (optind >= argc) -+ bb_show_usage(); -+ -+ for (option_index = optind; -+ option_index < ((argc - optind) > 1 ? argc - 1 : argc); -+ option_index++) { -+ -+ /* No explicit type given, guess query type. -+ * If we can convert the domain argument into a ptr (means that -+ * inet_pton() could read it) we assume a PTR request, else -+ * we issue A+AAAA queries and switch to an output format -+ * mimicking the one of the traditional nslookup applet. */ -+ if (types == 0) { -+ ptr = make_ptr(argv[option_index]); -+ -+ if (ptr) { -+ add_query(&queries, &n_queries, T_PTR, ptr); -+ } -+ else { -+ bb_style_counter = 1; -+ add_query(&queries, &n_queries, T_A, argv[option_index]); -+#if ENABLE_FEATURE_IPV6 -+ add_query(&queries, &n_queries, T_AAAA, argv[option_index]); -+#endif -+ } -+ } -+ else { -+ for (c = 0; qtypes[c].name; c++) -+ if (types & (1 << c)) -+ add_query(&queries, &n_queries, qtypes[c].type, -+ argv[option_index]); -+ } -+ } -+ -+ /* Use given DNS server if present */ -+ if (option_index < argc) { -+ if (!add_ns(&ns, &n_ns, argv[option_index])) { -+ fprintf(stderr, "Invalid NS server address \"%s\": %s\n", -+ argv[option_index], strerror(errno)); -+ goto out; -+ } -+ } -+ else { -+ parse_resolvconf(&ns, &n_ns); -+ } -+ -+ /* Fall back to localhost if we could not find NS in resolv.conf */ -+ if (n_ns == 0) { -+ add_ns(&ns, &n_ns, "127.0.0.1"); -+ } -+ -+ for (c = 0; c < n_ns; c++) { -+ rc = send_queries(&ns[c], 1, queries, n_queries); -+ -+ if (rc < 0) { -+ fprintf(stderr, "Failed to send queries: %s\n", strerror(errno)); -+ goto out; -+ } else if (rc > 0) { -+ break; -+ } -+ } -+ -+ if (c >= n_ns) { -+ fprintf(stderr, -+ ";; connection timed out; no servers could be reached\n\n"); -+ -+ return 1; -+ } -+ -+ printf("Server:\t\t%s\n", ns[c].name); -+ printf("Address:\t%s\n", sal2str(&ns[c].addr)); -+ -+ if (stats) { -+ printf("Replies:\t%d\n", ns[c].replies); -+ printf("Failures:\t%d\n", ns[c].failures); -+ } -+ -+ printf("\n"); -+ -+ for (rc = 0; rc < n_queries; rc++) { -+ if (stats) { -+ printf("Query #%d completed in %lums:\n", rc, queries[rc].latency); -+ } -+ -+ if (queries[rc].rcode != 0) { -+ printf("** server can't find %s: %s\n", queries[rc].name, -+ rcodes[queries[rc].rcode]); -+ continue; -+ } -+ -+ c = 0; -+ -+ if (queries[rc].rlen) { -+ if (!bb_style_counter) { -+ header = (HEADER *)queries[rc].reply; -+ -+ if (!header->aa) -+ printf("Non-authoritative answer:\n"); -+ -+ c = parse_reply(queries[rc].reply, queries[rc].rlen, NULL); -+ } -+ else { -+ c = parse_reply(queries[rc].reply, queries[rc].rlen, -+ &bb_style_counter); -+ } -+ } -+ -+ if (c == 0) -+ printf("*** Can't find %s: No answer\n", queries[rc].name); -+ else if (c < 0) -+ printf("*** Can't find %s: Parse error\n", queries[rc].name); -+ -+ if (!bb_style_counter) -+ printf("\n"); -+ } -+ -+ rc = 0; -+ -+out: -+ if (n_ns) -+ free(ns); -+ -+ if (n_queries) -+ free(queries); -+ -+ return rc; -+}