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, &params, 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, &params, 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);