Make the resolver retry timeout configurable via __res_params
The primary motivation is speeding up tests for non-responsive
nameservers. It's also base infrastructure for future work to compute
retry timeouts dynamically.
Using default timeouts (RES_TIMEOUT = 5 seconds):
ResolverTest.GetAddrInfoV6_nonresponsive (45141 ms)
With base_timeout_msec=100:
ResolverTest.GetAddrInfoV6_nonresponsive (1264 ms)
Test: executed unsubmitted test from aosp/713993
Change-Id: Id902089ca69ca8d28032180ba51e8937262ef490
diff --git a/libc/dns/include/resolv_params.h b/libc/dns/include/resolv_params.h
index 3c07d8a..ecc1cc3 100644
--- a/libc/dns/include/resolv_params.h
+++ b/libc/dns/include/resolv_params.h
@@ -45,6 +45,7 @@
uint8_t success_threshold; // 0: disable, value / 100 otherwise
uint8_t min_samples; // min # samples needed for statistics to be considered meaningful
uint8_t max_samples; // max # samples taken into account for statistics
+ int base_timeout_msec; // base query retry timeout (if 0, use RES_TIMEOUT)
};
typedef enum { res_goahead, res_nextns, res_modified, res_done, res_error }
diff --git a/libc/dns/resolv/res_send.c b/libc/dns/resolv/res_send.c
index cc09238..a645a6b 100644
--- a/libc/dns/resolv/res_send.c
+++ b/libc/dns/resolv/res_send.c
@@ -138,20 +138,17 @@
static int get_salen __P((const struct sockaddr *));
static struct sockaddr * get_nsaddr __P((res_state, size_t));
-static int send_vc(res_state, const u_char *, int,
- u_char *, int, int *, int,
- time_t *, int *, int *);
-static int send_dg(res_state, const u_char *, int,
- u_char *, int, int *, int,
- int *, int *,
- time_t *, int *, int *);
+static int send_vc(res_state, struct __res_params *params, const u_char *, int,
+ u_char *, int, int *, int, time_t *, int *, int *);
+static int send_dg(res_state, struct __res_params *params, const u_char *, int,
+ u_char *, int, int *, int, int *, int *, time_t *, int *, int *);
static void Aerror(const res_state, FILE *, const char *, int,
const struct sockaddr *, int);
static void Perror(const res_state, FILE *, const char *, int);
static int sock_eq(struct sockaddr *, struct sockaddr *);
void res_pquery(const res_state, const u_char *, int, FILE *);
static int connect_with_timeout(int sock, const struct sockaddr *nsap,
- socklen_t salen, int sec);
+ socklen_t salen, const struct timespec timeout);
static int retrying_poll(const int sock, short events, const struct timespec* finish);
/* BIONIC-BEGIN: implement source port randomization */
@@ -546,7 +543,7 @@
/* Use VC; at most one attempt per server. */
try = statp->retry;
- n = send_vc(statp, buf, buflen, ans, anssiz, &terrno,
+ n = send_vc(statp, ¶ms, buf, buflen, ans, anssiz, &terrno,
ns, &now, &rcode, &delay);
/*
@@ -577,7 +574,7 @@
async_safe_format_log(ANDROID_LOG_DEBUG, "libc", "using send_dg\n");
}
- n = send_dg(statp, buf, buflen, ans, anssiz, &terrno,
+ n = send_dg(statp, ¶ms, buf, buflen, ans, anssiz, &terrno,
ns, &v_circuit, &gotsomewhere, &now, &rcode, &delay);
/* Only record stats the first time we try a query. See above. */
@@ -727,24 +724,36 @@
}
}
-static int get_timeout(const res_state statp, const int ns)
+static struct timespec get_timeout(const res_state statp, const struct __res_params* params, const int ns)
{
- int timeout = (statp->retrans << ns);
- if (ns > 0) {
- timeout /= statp->nscount;
- }
- if (timeout <= 0) {
- timeout = 1;
+ int msec;
+ if (params->base_timeout_msec != 0) {
+ // TODO: scale the timeout by retry attempt and maybe number of servers
+ msec = params->base_timeout_msec;
+ } else {
+ // Legacy algorithm which scales the timeout by nameserver number.
+ // For instance, with 4 nameservers: 5s, 2.5s, 5s, 10s
+ // This has no effect with 1 or 2 nameservers
+ msec = (statp->retrans * 1000) << ns;
+ if (ns > 0) {
+ msec /= statp->nscount;
+ }
+ if (msec < 1000) {
+ msec = 1000; // Use at least 100ms
+ }
}
if (DBG) {
- async_safe_format_log(ANDROID_LOG_DEBUG, "libc", "using timeout of %d sec\n", timeout);
+ async_safe_format_log(ANDROID_LOG_DEBUG, "libc", "using timeout of %d msec\n", msec);
}
- return timeout;
+ struct timespec result;
+ result.tv_sec = msec / 1000;
+ result.tv_nsec = (msec % 1000) * 1000000;
+ return result;
}
static int
-send_vc(res_state statp,
+send_vc(res_state statp, struct __res_params* params,
const u_char *buf, int buflen, u_char *ans, int anssiz,
int *terrno, int ns, time_t* at, int* rcode, int* delay)
{
@@ -828,7 +837,7 @@
return (0);
}
if (connect_with_timeout(statp->_vcsock, nsap, (socklen_t)nsaplen,
- get_timeout(statp, ns)) < 0) {
+ get_timeout(statp, params, ns)) < 0) {
*terrno = errno;
Aerror(statp, stderr, "connect/vc", errno, nsap,
nsaplen);
@@ -969,7 +978,8 @@
/* return -1 on error (errno set), 0 on success */
static int
-connect_with_timeout(int sock, const struct sockaddr *nsap, socklen_t salen, int sec)
+connect_with_timeout(int sock, const struct sockaddr *nsap, socklen_t salen,
+ const struct timespec timeout)
{
int res, origflags;
@@ -983,7 +993,6 @@
}
if (res != 0) {
struct timespec now = evNowTime();
- struct timespec timeout = evConsTime((long)sec, 0L);
struct timespec finish = evAddTime(now, timeout);
if (DBG) {
async_safe_format_log(ANDROID_LOG_DEBUG, "libc", " %d send_vc\n", sock);
@@ -998,7 +1007,7 @@
fcntl(sock, F_SETFL, origflags);
if (DBG) {
async_safe_format_log(ANDROID_LOG_DEBUG, "libc",
- " %d connect_with_timeout returning %d\n", sock, res);
+ " %d connect_with_const timeout returning %d\n", sock, res);
}
return res;
}
@@ -1021,7 +1030,7 @@
int n = ppoll(&fds, 1, &timeout, /*sigmask=*/NULL);
if (n == 0) {
if (DBG) {
- async_safe_format_log(ANDROID_LOG_DEBUG, " libc",
+ async_safe_format_log(ANDROID_LOG_DEBUG, "libc",
" %d retrying_poll timeout\n", sock);
}
errno = ETIMEDOUT;
@@ -1058,7 +1067,7 @@
}
static int
-send_dg(res_state statp,
+send_dg(res_state statp, struct __res_params* params,
const u_char *buf, int buflen, u_char *ans, int anssiz,
int *terrno, int ns, int *v_circuit, int *gotsomewhere,
time_t *at, int *rcode, int* delay)
@@ -1073,7 +1082,7 @@
struct timespec now, timeout, finish, done;
struct sockaddr_storage from;
socklen_t fromlen;
- int resplen, seconds, n, s;
+ int resplen, n, s;
nsap = get_nsaddr(statp, (size_t)ns);
nsaplen = get_salen(nsap);
@@ -1151,9 +1160,8 @@
/*
* Wait for reply.
*/
- seconds = get_timeout(statp, ns);
+ timeout = get_timeout(statp, params, ns);
now = evNowTime();
- timeout = evConsTime((long)seconds, 0L);
finish = evAddTime(now, timeout);
retry:
n = retrying_poll(s, POLLIN, &finish);