Merge "DNS proxy: the start.  proxies getaddrinfo calls."
diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c
index e7564c4..4fa92b7 100644
--- a/libc/netbsd/net/getaddrinfo.c
+++ b/libc/netbsd/net/getaddrinfo.c
@@ -77,10 +77,13 @@
  *	  friends.
  */
 
+#include <fcntl.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/param.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 #include <net/if.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -371,6 +374,180 @@
 	return have_ipv6;
 }
 
+// Returns 0 on success, else returns non-zero on error (in which case
+// getaddrinfo should continue as normal)
+static int
+android_getaddrinfo_proxy(
+    const char *hostname, const char *servname,
+    const struct addrinfo *hints, struct addrinfo **res)
+{
+	int sock;
+	const int one = 1;
+	struct sockaddr_un proxy_addr;
+	const char* cache_mode = getenv("ANDROID_DNS_MODE");
+	FILE* proxy = NULL;
+	int success = 0;
+
+	// Clear this at start, as we use its non-NULLness later (in the
+	// error path) to decide if we have to free up any memory we
+	// allocated in the process (before failing).
+	*res = NULL;
+
+	if (cache_mode != NULL && strcmp(cache_mode, "local") == 0) {
+		// Don't use the proxy in local mode.  This is used by the
+		// proxy itself.
+		return -1;
+	}
+
+	// Bogus things we can't serialize.  Don't use the proxy.
+	if ((hostname != NULL &&
+	     strcspn(hostname, " \n\r\t^'\"") != strlen(hostname)) ||
+	    (servname != NULL &&
+	     strcspn(servname, " \n\r\t^'\"") != strlen(servname))) {
+		return -1;
+	}
+
+	sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) {
+		return -1;
+	}
+
+	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+	memset(&proxy_addr, 0, sizeof(proxy_addr));
+	proxy_addr.sun_family = AF_UNIX;
+	strlcpy(proxy_addr.sun_path, "/dev/socket/dnsproxyd",
+		sizeof(proxy_addr.sun_path));
+	if (TEMP_FAILURE_RETRY(connect(sock,
+				       (const struct sockaddr*) &proxy_addr,
+				       sizeof(proxy_addr))) != 0) {
+		close(sock);
+		return -1;
+	}
+
+	// Send the request.
+	proxy = fdopen(sock, "r+");
+	if (fprintf(proxy, "getaddrinfo %s %s %d %d %d %d",
+		    hostname == NULL ? "^" : hostname,
+		    servname == NULL ? "^" : servname,
+		    hints == NULL ? -1 : hints->ai_flags,
+		    hints == NULL ? -1 : hints->ai_family,
+		    hints == NULL ? -1 : hints->ai_socktype,
+		    hints == NULL ? -1 : hints->ai_protocol) < 0) {
+		goto exit;
+	}
+	// literal NULL byte at end, required by FrameworkListener
+	if (fputc(0, proxy) == EOF ||
+	    fflush(proxy) != 0) {
+		goto exit;
+	}
+
+	int remote_rv;
+	if (fread(&remote_rv, sizeof(int), 1, proxy) != 1) {
+		goto exit;
+	}
+
+	if (remote_rv != 0) {
+		goto exit;
+	}
+
+	struct addrinfo* ai = NULL;
+	struct addrinfo** nextres = res;
+	while (1) {
+		uint32_t addrinfo_len;
+		if (fread(&addrinfo_len, sizeof(addrinfo_len),
+			  1, proxy) != 1) {
+			break;
+		}
+		addrinfo_len = ntohl(addrinfo_len);
+		if (addrinfo_len == 0) {
+			success = 1;
+			break;
+		}
+
+		if (addrinfo_len < sizeof(struct addrinfo)) {
+			break;
+		}
+		struct addrinfo* ai = calloc(1, addrinfo_len +
+					     sizeof(struct sockaddr_storage));
+		if (ai == NULL) {
+			break;
+		}
+
+		if (fread(ai, addrinfo_len, 1, proxy) != 1) {
+			// Error; fall through.
+			break;
+		}
+
+		// Zero out the pointer fields we copied which aren't
+		// valid in this address space.
+		ai->ai_addr = NULL;
+		ai->ai_canonname = NULL;
+		ai->ai_next = NULL;
+
+		// struct sockaddr
+		uint32_t addr_len;
+		if (fread(&addr_len, sizeof(addr_len), 1, proxy) != 1) {
+			break;
+		}
+		addr_len = ntohl(addr_len);
+		if (addr_len != 0) {
+			if (addr_len > sizeof(struct sockaddr_storage)) {
+				// Bogus; too big.
+				break;
+			}
+			struct sockaddr* addr = (struct sockaddr*)(ai + 1);
+			if (fread(addr, addr_len, 1, proxy) != 1) {
+				break;
+			}
+			ai->ai_addr = addr;
+		}
+
+		// cannonname
+		uint32_t name_len;
+		if (fread(&name_len, sizeof(name_len), 1, proxy) != 1) {
+			break;
+		}
+		if (name_len != 0) {
+			ai->ai_canonname = (char*) malloc(name_len);
+			if (fread(ai->ai_canonname, name_len, 1, proxy) != 1) {
+				break;
+			}
+			if (ai->ai_canonname[name_len - 1] != '\0') {
+				// The proxy should be returning this
+				// NULL-terminated.
+				break;
+			}
+		}
+
+		*nextres = ai;
+		nextres = &ai->ai_next;
+		ai = NULL;
+	}
+
+	if (ai != NULL) {
+		// Clean up partially-built addrinfo that we never ended up
+		// attaching to the response.
+		freeaddrinfo(ai);
+	}
+exit:
+	if (proxy != NULL) {
+		fclose(proxy);
+	}
+
+	if (success) {
+		return 0;
+	}
+
+	// Proxy failed; fall through to local
+	// resolver case.  But first clean up any
+	// memory we might've allocated.
+	if (*res) {
+		freeaddrinfo(*res);
+		*res = NULL;
+	}
+	return -1;
+}
+
 int
 getaddrinfo(const char *hostname, const char *servname,
     const struct addrinfo *hints, struct addrinfo **res)
@@ -517,6 +694,13 @@
 	if (pai->ai_flags & AI_NUMERICHOST)
 		ERR(EAI_NONAME);
 
+        /*
+         * BEGIN ANDROID CHANGES; proxying to the cache
+         */
+        if (android_getaddrinfo_proxy(hostname, servname, hints, res) == 0) {
+            return 0;
+        }
+
 	/*
 	 * hostname as alphabetical name.
 	 * we would like to prefer AF_INET6 than AF_INET, so we'll make a