struct ns {
const char *name;
- len_and_sockaddr addr;
+ len_and_sockaddr *lsa;
int failures;
int replies;
};
size_t qlen, rlen;
unsigned char query[512], reply[512];
unsigned long latency;
- int rcode, n_ns;
+ int rcode;
};
static const struct {
unsigned default_port;
unsigned default_retry;
unsigned default_timeout;
+ unsigned serv_count;
+ struct ns *server;
} FIX_ALIASING;
#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
return i;
}
-static int parse_nsaddr(const char *addrstr, len_and_sockaddr *lsa)
-{
- char *hash;
- unsigned port = G.default_port;
- IF_FEATURE_IPV6(unsigned scope;)
-
- dbg("%s: addrstr:'%s'\n", __func__, addrstr);
-
- hash = strchr(addrstr, '#');
-
- if (hash) {
- *hash++ = '\0';
- port = bb_strtou(hash, NULL, 10);
- if (errno || port > 65535) {
- errno = EINVAL;
- return -1;
- }
- }
-
-#if ENABLE_FEATURE_IPV6
- scope = 0;
- hash = strchr(addrstr, '%');
- if (hash) {
- char ifname[IFNAMSIZ];
- int i;
-
- hash++;
- for (i = 0; hash[i] != '\0' && hash[i] != '#'; i++) {
- if (i >= IFNAMSIZ) {
- errno = ENODEV;
- return -1;
- }
- ifname[i] = hash[i];
- }
- ifname[i] = '\0';
-
- scope = if_nametoindex(ifname);
- if (scope == 0) {
- errno = ENODEV;
- return -1;
- }
- }
-
- 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 (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(char resbuf[80], const char *addrstr)
{
unsigned char addr[16];
return NULL;
}
-#if ENABLE_FEATURE_IPV6
-static void to_v4_mapped(len_and_sockaddr *a)
-{
- if (a->u.sa.sa_family != AF_INET)
- return;
-
- /* Order is important */
-//FIXME: port?
- memcpy(a->u.sin6.sin6_addr.s6_addr + 12, &a->u.sin.sin_addr, 4);
- memcpy(a->u.sin6.sin6_addr.s6_addr, v4_mapped, 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)
+static int send_queries(struct ns *ns, struct query *queries, int n_queries)
{
- int fd;
- int timeout = G.default_timeout * 1000, retry_interval, servfail_retry = 0;
- len_and_sockaddr from = { };
- 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 */
-#if ENABLE_FEATURE_IPV6
- fd = socket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- /* Handle case where system lacks IPv6 support */
- if (fd < 0) {
- if (from.u.sa.sa_family != AF_INET6 || errno != EAFNOSUPPORT)
- bb_perror_msg_and_die("socket");
- fd = xsocket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- from.u.sa.sa_family = AF_INET;
- }
-#else
- fd = xsocket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-#endif
- xbind(fd, &from.u.sa, from.len);
-
-#if ENABLE_FEATURE_IPV6
- /* Convert any IPv4 addresses in a mixed environment to v4-mapped */
- if (from.u.sa.sa_family == AF_INET6) {
- setsockopt_1(fd, IPPROTO_IPV6, IPV6_V6ONLY);
- for (nn = 0; nn < n_ns; nn++)
- to_v4_mapped(&ns[nn].addr);
- }
-#endif
+ int servfail_retry = 0;
+ int n_replies = 0;
+ int next_query = 0;
+ unsigned retry_interval;
+ unsigned timeout = G.default_timeout * 1000;
+ unsigned t0, t1, t2;
- pfd.fd = fd;
+ pfd.fd = -1;
pfd.events = POLLIN;
+
retry_interval = timeout / G.default_retry;
t0 = t2 = monotonic_ms();
t1 = t2 - retry_interval;
for (; t2 - t0 < timeout; t2 = monotonic_ms()) {
if (t2 - t1 >= retry_interval) {
+ int qn;
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);
+ if (pfd.fd < 0) {
+ len_and_sockaddr *local_lsa;
+ pfd.fd = xsocket_type(&local_lsa, ns->lsa->u.sa.sa_family, SOCK_DGRAM);
+ /*
+ * local_lsa has "null" address and port 0 now.
+ * bind() ensures we have a *particular port* selected by kernel
+ * and remembered in fd, thus later recv(fd)
+ * receives only packets sent to this port.
+ */
+ xbind(pfd.fd, &local_lsa->u.sa, local_lsa->len);
+ free(local_lsa);
+ /* Make read/writes know the destination */
+ xconnect(pfd.fd, &ns->lsa->u.sa, ns->lsa->len);
+ ndelay_on(pfd.fd);
}
+ write(pfd.fd, queries[qn].query, queries[qn].qlen);
}
t1 = t2;
servfail_retry = 2 * n_queries;
}
-
+ poll_more:
/* 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
+ int qn;
+ int recvlen;
+
+ recvlen = read(pfd.fd,
+ queries[next_query].reply,
+ sizeof(queries[next_query].reply)
);
/* read error */
/* 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;
+ goto poll_more;
/* Find which query this answer goes with, if any */
for (qn = next_query; qn < n_queries; qn++)
break;
if (qn >= n_queries || queries[qn].rlen)
- continue;
+ goto poll_more;
queries[qn].rcode = queries[next_query].reply[3] & 15;
queries[qn].latency = monotonic_ms() - t0;
- queries[qn].n_ns = nn;
- ns[nn].replies++;
+ ns->replies++;
/* Only accept positive or negative responses;
* retry immediately on server failure, and ignore
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);
+ ns->failures++;
+ write(pfd.fd, queries[qn].query, queries[qn].qlen);
}
/* fall through */
while (next_query < n_queries) {
if (!queries[next_query].rlen)
break;
-
next_query++;
}
- }
- else {
+ } else {
memcpy(queries[qn].reply, queries[next_query].reply, recvlen);
}
if (next_query >= n_queries)
- return n_replies;
+ goto ret;
}
}
+ ret:
+ if (pfd.fd >= 0)
+ close(pfd.fd);
return n_replies;
}
-///FIXME: use standard lsa = xhost2sockaddr(host, port) instead
-
-static struct ns *add_ns(struct ns **ns, int *n_ns, const char *addr)
+static void add_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
- };
+ struct ns *ns;
+ unsigned count;
dbg("%s: addr:'%s'\n", __func__, addr);
- 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) : G.default_port));
+ count = G.serv_count++;
- 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 = xrealloc(*ns, sizeof(**ns) * (*n_ns + 1));
- *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)++];
+ G.server = xrealloc_vector(G.server, /*8=2^3:*/ 3, count);
+ ns = &G.server[count];
+ ns->name = addr;
+ ns->lsa = xhost2sockaddr(addr, 53);
+ /*ns->replies = 0; - already is */
+ /*ns->failures = 0; - already is */
}
-static int parse_resolvconf(struct ns **ns, int *n_ns)
+static void parse_resolvconf(void)
{
- int prev_n_ns = *n_ns;
FILE *resolv;
resolv = fopen("/etc/resolv.conf", "r");
if (!p)
continue;
- if (!add_ns(ns, n_ns, xstrdup(p))) {
- free(p);
- break;
- }
+ add_ns(xstrdup(p));
}
fclose(resolv);
}
-
- return *n_ns - prev_n_ns;
}
static struct query *add_query(struct query **queries, int *n_queries,
struct ns *ns;
struct query *queries;
llist_t *type_strings;
- int n_ns, n_queries;
+ int n_queries;
int bb_style_counter = 0;
unsigned types;
int rc;
} while (argv[0] && argv[1]);
/* Use given DNS server if present */
- n_ns = 0;
- ns = NULL;
if (argv[0]) {
- if (!add_ns(&ns, &n_ns, argv[0]))
- bb_error_msg_and_die("invalid NS server address \"%s\"", argv[0]);
+ add_ns(argv[0]);
} else {
- parse_resolvconf(&ns, &n_ns);
+ parse_resolvconf();
/* 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");
+ if (G.serv_count == 0)
+ add_ns("127.0.0.1");
}
- for (rc = 0; rc < n_ns; rc++) {
- int c = send_queries(&ns[rc], 1, queries, n_queries);
+ for (rc = 0; rc < G.serv_count; rc++) {
+ int c = send_queries(&G.server[rc], queries, n_queries);
if (c < 0)
bb_perror_msg_and_die("can't send queries");
if (c > 0)
break;
rc++;
- if (rc >= n_ns) {
+ if (rc >= G.serv_count) {
fprintf(stderr,
";; connection timed out; no servers could be reached\n\n");
return EXIT_FAILURE;
}
}
- printf("Server:\t\t%s\n", ns[rc].name);
+ printf("Server:\t\t%s\n", G.server[rc].name);
{
char buf[SIZEOF_SAL2STR_BUF];
- printf("Address:\t%s\n", sal2str(buf, &ns[rc].addr));
+ printf("Address:\t%s\n", sal2str(buf, G.server[rc].lsa));
}
if (opts & OPT_stats) {
- printf("Replies:\t%d\n", ns[rc].replies);
- printf("Failures:\t%d\n", ns[rc].failures);
+ printf("Replies:\t%d\n", G.server[rc].replies);
+ printf("Failures:\t%d\n", G.server[rc].failures);
}
printf("\n");