123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951 |
- 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
- @@ -134,6 +134,12 @@ else
- LDLIBS += m
- endif
-
- +# 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,914 @@
- +/*
- + * 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_LIBRECMC
- +//config: bool "nslookup_librecmc"
- +//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_LIBRECMC_LONG_OPTIONS
- +//config: bool "Enable long options"
- +//config: default y
- +//config: depends on NSLOOKUP_LIBRECMC && LONG_OPTS
- +//config: help
- +//config: Support long options for the nslookup applet.
- +
- +//applet:IF_NSLOOKUP_LIBRECMC(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP))
- +
- +//kbuild:lib-$(CONFIG_NSLOOKUP_LIBRECMC) += 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_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_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_LIBRECMC_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;
- +}
|