dns cache per interface iteration 2

name server addresses are read from the dns
cache associated wih the interface on which
the request shall be done.

processes which has requested to issue dns request
using specific interface are now proxied to netd.

added methods to attach/detach a process to a specific
dns cache/interface.

added getaddrinfoforinface method which takes an
interface as an argument.

bug:4815099
bug:5465296
Change-Id: I7a8fe1980cdf99d4d296ddc5c6411f0c72162263
diff --git a/libc/netbsd/gethnamaddr.c b/libc/netbsd/gethnamaddr.c
index 9a9f6e2..1ab987e 100644
--- a/libc/netbsd/gethnamaddr.c
+++ b/libc/netbsd/gethnamaddr.c
@@ -56,6 +56,7 @@
 
 #include <sys/param.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include "arpa_nameser.h"
@@ -69,6 +70,7 @@
 #include <stdio.h>
 #include <strings.h>
 #include <syslog.h>
+#include <unistd.h>
 
 #ifndef LOG_AUTH
 # define LOG_AUTH 0
@@ -80,6 +82,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+// This should be synchronized to ResponseCode.h
+static const int DnsProxyQueryResult = 222;
+
 static const char const AskedForGot[] =
 			  "gethostby*.getanswer: asked for \"%s\", got \"%s\"";
 
@@ -121,7 +126,7 @@
 static int _dns_gethtbyaddr(void *, void *, va_list);
 static int _dns_gethtbyname(void *, void *, va_list);
 
-static struct hostent *gethostbyname_internal(const char *, int, res_state);
+static struct hostent *gethostbyname_internal(const char *, int, res_state, const char *);
 
 static const ns_src default_dns_files[] = {
 	{ NSSRC_FILES, 	NS_SUCCESS },
@@ -490,14 +495,15 @@
 
 	assert(name != NULL);
 
+	/* try IPv6 first - if that fails do IPv4 */
 	if (res->options & RES_USE_INET6) {
-		hp = gethostbyname_internal(name, AF_INET6, res);
+		hp = gethostbyname_internal(name, AF_INET6, res, NULL);
 		if (hp) {
 			__res_put_state(res);
 			return hp;
 		}
 	}
-	hp = gethostbyname_internal(name, AF_INET, res);
+	hp = gethostbyname_internal(name, AF_INET, res, NULL);
 	__res_put_state(res);
 	return hp;
 }
@@ -505,25 +511,120 @@
 struct hostent *
 gethostbyname2(const char *name, int af)
 {
+	return android_gethostbynameforiface(name, af, NULL);
+}
+
+struct hostent *
+android_gethostbynameforiface(const char *name, int af, const char *iface)
+{
 	struct hostent *hp;
 	res_state res = __res_get_state();
 
 	if (res == NULL)
 		return NULL;
-	hp = gethostbyname_internal(name, af, res);
+	hp = gethostbyname_internal(name, af, res, iface);
 	__res_put_state(res);
 	return hp;
 }
 
+
+static FILE* android_open_proxy()
+{
+	int sock;
+	const int one = 1;
+	struct sockaddr_un proxy_addr;
+
+	sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) {
+		return NULL;
+	}
+
+	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 NULL;
+	}
+
+	return fdopen(sock, "r+");
+}
+
 static struct hostent *
-gethostbyname_internal(const char *name, int af, res_state res)
+android_read_hostent(FILE* proxy)
+{
+	uint32_t size;
+	char buf[4];
+	if (fread(buf, 1, sizeof(buf), proxy) != sizeof(buf)) return NULL;
+
+	/* This is reading serialized data from system/netd/DnsProxyListener.cpp
+	 * and changes here need to be matched there */
+	int result_code = strtol(buf, NULL, 10);
+	if (result_code != DnsProxyQueryResult) {
+		fread(&size, 1, sizeof(size), proxy);
+		return NULL;
+	}
+
+	if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL;
+	size = ntohl(size);
+	res_static rs = __res_get_static();
+	memset(&rs->host, 0, sizeof(rs->host));
+	char *ptr = rs->hostbuf;
+
+	if (fread(ptr, 1, size, proxy) != size) return NULL;
+	ptr += size;
+	rs->host.h_name = rs->hostbuf;
+
+	char **aliases = rs->host_aliases;
+	rs->host.h_aliases = rs->host_aliases;
+	while (1) {
+		if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL;
+		size = ntohl(size);
+
+		if (size == 0) {
+			*aliases = NULL;
+			break;
+		}
+		if (fread(ptr, 1, size, proxy) != size) return NULL;
+		*aliases++ = ptr;
+		ptr += size;
+	}
+
+	if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL;
+	rs->host.h_addrtype = ntohl(size);
+
+	if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL;
+	rs->host.h_length = ntohl(size);
+
+	char **addrs = rs->h_addr_ptrs;
+	rs->host.h_addr_list = rs->h_addr_ptrs;
+	while (1) {
+		if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return NULL;
+		size = ntohl(size);
+		if (size == 0) {
+			*addrs = NULL;
+			break;
+		}
+		if (fread(ptr, 1, size, proxy) != size) return NULL;
+		*addrs++ = ptr;
+		ptr += size;
+	}
+
+	return &rs->host;
+}
+
+
+static struct hostent *
+gethostbyname_internal_real(const char *name, int af, res_state res)
 {
 	const char *cp;
 	char *bp, *ep;
 	int size;
 	struct hostent *hp;
-        struct resolv_cache*  cache;
-        res_static  rs = __res_get_static();
+	res_static rs = __res_get_static();
 
 	static const ns_dtab dtab[] = {
 		NS_FILES_CB(_gethtbyname, NULL)
@@ -632,14 +733,84 @@
 	if (nsdispatch(&hp, dtab, NSDB_HOSTS, "gethostbyname",
 	    default_dns_files, name, strlen(name), af) != NS_SUCCESS) {
 		return NULL;
-        }
+	}
 	h_errno = NETDB_SUCCESS;
 	return hp;
 }
 
+
+// very similar in proxy-ness to android_getaddrinfo_proxy
+static struct hostent *
+gethostbyname_internal(const char *name, int af, res_state res, const char *iface)
+{
+	const char *cache_mode = getenv("ANDROID_DNS_MODE");
+	FILE* proxy = NULL;
+	struct hostent *result = NULL;
+
+	if (cache_mode != NULL && strcmp(cache_mode, "local") == 0) {
+		res_setiface(res, iface);
+		return gethostbyname_internal_real(name, af, res);
+	}
+
+	proxy = android_open_proxy();
+
+	/* This is writing to system/netd/DnsProxyListener.cpp and changes
+	 * here need to be matched there */
+	if (fprintf(proxy, "gethostbyname %d %s %s %d",
+			getpid(),
+			iface == NULL ? "^" : iface,
+			name == NULL ? "^" : name,
+			af) < 0) {
+		goto exit;
+	}
+
+	if (fputc(0, proxy) == EOF || fflush(proxy) != 0) {
+		goto exit;
+	}
+
+	result = android_read_hostent(proxy);
+
+exit:
+	if (proxy != NULL) {
+		fclose(proxy);
+	}
+	return result;
+}
+
+
 struct hostent *
-gethostbyaddr(const void *addr,
-    socklen_t len, int af)
+android_gethostbyaddrforiface_proxy(const void *addr,
+    socklen_t len, int af, const char* iface)
+{
+	struct hostent *result = NULL;
+	FILE* proxy = android_open_proxy();
+
+	if (proxy == NULL) goto exit;
+
+	char buf[INET6_ADDRSTRLEN];  //big enough for IPv4 and IPv6
+	const char * addrStr = inet_ntop(af, addr, buf, sizeof(buf));
+	if (addrStr == NULL) goto exit;
+
+	if (fprintf(proxy, "gethostbyaddr %s %d %d %d %s",
+			addrStr, len, af, getpid(), iface == NULL ? "^" : iface) < 0) {
+		goto exit;
+	}
+
+	if (fputc(0, proxy) == EOF || fflush(proxy) != 0) {
+		goto exit;
+	}
+
+	result = android_read_hostent(proxy);
+exit:
+	if (proxy != NULL) {
+		fclose(proxy);
+	}
+	return result;
+}
+
+struct hostent *
+android_gethostbyaddrforiface_real(const void *addr,
+    socklen_t len, int af, const char* iface)
 {
 	const u_char *uaddr = (const u_char *)addr;
 	socklen_t size;
@@ -687,12 +858,31 @@
 	hp = NULL;
 	h_errno = NETDB_INTERNAL;
 	if (nsdispatch(&hp, dtab, NSDB_HOSTS, "gethostbyaddr",
-	    default_dns_files, uaddr, len, af) != NS_SUCCESS)
+	    default_dns_files, uaddr, len, af, iface) != NS_SUCCESS)
 		return NULL;
 	h_errno = NETDB_SUCCESS;
 	return hp;
 }
 
+struct hostent *
+android_gethostbyaddrforiface(const void *addr, socklen_t len, int af, const char* iface)
+{
+	const char *cache_mode = getenv("ANDROID_DNS_MODE");
+
+	if (cache_mode == NULL || strcmp(cache_mode, "local") != 0) {
+		return android_gethostbyaddrforiface_proxy(addr, len, af, iface);
+	} else {
+		return android_gethostbyaddrforiface_real(addr,len, af,iface);
+	}
+}
+
+struct hostent *
+gethostbyaddr(const void *addr, socklen_t len, int af)
+{
+	return android_gethostbyaddrforiface(addr, len, af, NULL);
+}
+
+
 static void
 _sethtent(int f)
 {
@@ -1124,6 +1314,7 @@
 	const unsigned char *uaddr;
 	int len, af, advance;
 	res_state res;
+	const char* iface;
 	res_static rs = __res_get_static();
 
 	assert(rv != NULL);
@@ -1131,6 +1322,7 @@
 	uaddr = va_arg(ap, unsigned char *);
 	len = va_arg(ap, int);
 	af = va_arg(ap, int);
+	iface = va_arg(ap, char *);
 
 	switch (af) {
 	case AF_INET:
@@ -1172,6 +1364,7 @@
 		free(buf);
 		return NS_NOTFOUND;
 	}
+	res_setiface(res, iface);
 	n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, sizeof(buf->buf));
 	if (n < 0) {
 		free(buf);