diff options
-rw-r--r-- | networking/nslookup.c | 241 |
1 files changed, 123 insertions, 118 deletions
diff --git a/networking/nslookup.c b/networking/nslookup.c index 2e6569497..23ab97585 100644 --- a/networking/nslookup.c +++ b/networking/nslookup.c @@ -263,10 +263,10 @@ struct ns { 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 { @@ -288,23 +288,22 @@ 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 @@ -518,131 +517,136 @@ static char *make_ptr(char resbuf[80], const char *addrstr) /* * 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; } @@ -791,12 +795,13 @@ int nslookup_main(int argc UNUSED_PARAM, char **argv) 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]; |