busybox: remove nslookup_lede/openwrt.patch
authorNick Hainke <vincent@systemli.org>
Wed, 12 May 2021 18:36:58 +0000 (20:36 +0200)
committerPaul Spooren <mail@aparcar.org>
Tue, 18 May 2021 23:08:23 +0000 (01:08 +0200)
The nslookup_lede/openwrt applet was introduced in de5b8e5. It was
introduced because:

  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.

In busybox this applet is added in 0dd3be8. In particular, this commit
introduces the variable NSLOOKUP_BIG. We set the default to true and
so nothing changes.

Signed-off-by: Nick Hainke <vincent@systemli.org>
package/utils/busybox/Config-defaults.in
package/utils/busybox/Makefile
package/utils/busybox/config/networking/Config.in
package/utils/busybox/patches/230-add_nslookup_lede.patch [deleted file]

index 168c73b24c61056d28e2d0c30fd52a6785a3227f..f9028620a18d7197b9f8aab17c067e4cdcc18827 100644 (file)
@@ -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
index e4e176870c7adda3c594f2804be57fe15b643e73..c2f1c1be4541d6ae15f53f50f813effb9697afad 100644 (file)
@@ -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
index 6608f5899d538ee2c308141f1b78e3a79ea9f8f2..3e1c4c4c04b8c71b5a909fd5d3a5fb535fac99c7 100644 (file)
@@ -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 (file)
index 280d4e2..0000000
+++ /dev/null
@@ -1,971 +0,0 @@
-From ab0f8bb80527928f513297ab93e3ec8c8b48dd50 Mon Sep 17 00:00:00 2001
-From: Jo-Philipp Wich <jo@mein.io>
-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 <jo@mein.io>
----
- 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 <jo@mein.io>
-+ *
-+ * 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 <stdio.h>
-+#include <resolv.h>
-+#include <string.h>
-+#include <errno.h>
-+#include <time.h>
-+#include <poll.h>
-+#include <unistd.h>
-+#include <stdlib.h>
-+#include <sys/socket.h>
-+#include <arpa/inet.h>
-+#include <net/if.h>
-+#include <netdb.h>
-+
-+#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;
-+}