struct query {
const char *name;
- size_t qlen, rlen;
+ unsigned qlen, rlen;
+ unsigned latency;
+ uint8_t rcode;
unsigned char query[512], reply[512];
- unsigned long latency;
- int rcode;
};
static const struct {
};
static const char *const rcodes[] = {
- "NOERROR",
- "FORMERR",
- "SERVFAIL",
- "NXDOMAIN",
- "NOTIMP",
- "REFUSED",
- "YXDOMAIN",
- "YXRRSET",
- "NXRRSET",
- "NOTAUTH",
- "NOTZONE",
- "RESERVED11",
- "RESERVED12",
- "RESERVED13",
- "RESERVED14",
- "RESERVED15",
- "BADVERS"
+ "NOERROR", // 0
+ "FORMERR", // 1
+ "SERVFAIL", // 2
+ "NXDOMAIN", // 3
+ "NOTIMP", // 4
+ "REFUSED", // 5
+ "YXDOMAIN", // 6
+ "YXRRSET", // 7
+ "NXRRSET", // 8
+ "NOTAUTH", // 9
+ "NOTZONE", // 10
+ "11", // 11 not assigned
+ "12", // 12 not assigned
+ "13", // 13 not assigned
+ "14", // 14 not assigned
+ "15", // 15 not assigned
};
#if ENABLE_FEATURE_IPV6
/*
* Function logic borrowed & modified from musl libc, res_msend.c
+ * n_queries is always > 0.
*/
-static int send_queries(struct ns *ns, struct query *queries, int n_queries)
+static int send_queries(struct ns *ns, struct query *query, int n_queries)
{
+ len_and_sockaddr *local_lsa;
struct pollfd pfd;
int servfail_retry = 0;
int n_replies = 0;
- int next_query = 0;
+ int save_idx = 0;
unsigned retry_interval;
unsigned timeout = G.default_timeout * 1000;
- unsigned t0, t1, t2;
+ unsigned tstart, tsent, tcur;
- pfd.fd = -1;
pfd.events = POLLIN;
+ 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);
retry_interval = timeout / G.default_retry;
- t0 = t2 = monotonic_ms();
- t1 = t2 - retry_interval;
+ tstart = tcur = monotonic_ms();
+ goto send;
+
+ while (tcur - tstart < timeout) {
+ int qn;
+ int recvlen;
- for (; t2 - t0 < timeout; t2 = monotonic_ms()) {
- if (t2 - t1 >= retry_interval) {
- int qn;
+ if (tcur - tsent >= retry_interval) {
+ send:
for (qn = 0; qn < n_queries; qn++) {
- if (queries[qn].rlen)
+ if (query[qn].rlen)
continue;
- 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);
- }
- if (write(pfd.fd, queries[qn].query, queries[qn].qlen) < 0) {
+ if (write(pfd.fd, query[qn].query, query[qn].qlen) < 0) {
bb_perror_msg("write to '%s'", ns->name);
- close(pfd.fd);
- return -1; /* "no go, try next server" */
+ n_replies = -1; /* "no go, try next server" */
+ goto ret;
}
+ dbg("query %u sent\n", qn);
}
-
- t1 = t2;
+ tsent = tcur;
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) {
- int qn;
- int recvlen;
-
- recvlen = read(pfd.fd,
- queries[next_query].reply,
- sizeof(queries[next_query].reply)
- );
- /* read error */
- if (recvlen < 0)
- break;
-
- /* Ignore non-identifiable packets */
- if (recvlen < 4)
- goto poll_more;
-
- /* 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) == 0)
- break;
-
- if (qn >= n_queries || queries[qn].rlen)
- goto poll_more;
+ /* Wait for a response, or until time to retry */
+ if (poll(&pfd, 1, retry_interval - (tcur - tsent)) <= 0)
+ goto next;
- queries[qn].rcode = queries[next_query].reply[3] & 15;
- queries[qn].latency = monotonic_ms() - t0;
+ recvlen = read(pfd.fd, query[save_idx].reply, sizeof(query[0].reply));
- ns->replies++;
+ /* Error/non-identifiable packet */
+ if (recvlen < 4) {
+ dbg("read is too short:%d\n", recvlen);
+ goto next;
+ }
- /* 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:
+ /* Find which query this answer goes with, if any */
+ qn = save_idx;
+ for (;;) {
+ if (memcmp(query[save_idx].reply, query[qn].query, 2) == 0) {
+ dbg("response matches query %u\n", qn);
break;
-
- case 2:
- if (servfail_retry && servfail_retry--) {
- ns->failures++;
- write(pfd.fd, queries[qn].query, queries[qn].qlen);
- }
- /* fall through */
-
- default:
- continue;
}
+ if (++qn >= n_queries) {
+ dbg("response does not match any query\n");
+ goto next;
+ }
+ }
- /* Store answer */
- n_replies++;
+ if (query[qn].rlen) {
+ dbg("dropped duplicate response to query %u\n", qn);
+ goto next;
+ }
- queries[qn].rlen = recvlen;
+ ns->replies++;
- 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);
+ query[qn].rcode = query[save_idx].reply[3] & 15;
+ dbg("query %u rcode:%s\n", qn, rcodes[query[qn].rcode]);
+
+ /* Only accept positive or negative responses;
+ * retry immediately on server failure, and ignore
+ * all other codes such as refusal.
+ */
+ switch (query[qn].rcode) {
+ case 0:
+ case 3:
+ break;
+ case 2:
+ if (servfail_retry) {
+ servfail_retry--;
+ ns->failures++;
+ write(pfd.fd, query[qn].query, query[qn].qlen);
+ dbg("query %u resent\n", qn);
}
+ /* fall through */
+ default:
+ next:
+ tcur = monotonic_ms();
+ continue;
+ }
- if (next_query >= n_queries)
- goto ret;
+ /* Store answer */
+ n_replies++;
+ query[qn].rlen = recvlen;
+ tcur = monotonic_ms();
+ query[qn].latency = tcur - tstart;
+ if (qn != save_idx) {
+ /* "wrong" receive buffer, move to correct one */
+ memcpy(query[qn].reply, query[save_idx].reply, recvlen);
+ continue;
}
- }
+ /* query[0..save_idx] have replies, move to next one, if exists */
+ for (;;) {
+ save_idx++;
+ if (save_idx >= n_queries)
+ goto ret; /* all are full: we have all results */
+ if (!query[save_idx].rlen)
+ break; /* this one is empty */
+ }
+ } /* while() */
+
ret:
- if (pfd.fd >= 0)
- close(pfd.fd);
+ close(pfd.fd);
return n_replies;
}
n_queries = 0;
queries = NULL;
do {
- /* 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) {
+ /* 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.
+ */
char *ptr;
char buf80[80];