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.

Change-Id: I851ec8ab8ce3112626ad2a729078b91d013f32fd
bug:4815099
bug:5465296
diff --git a/libc/docs/OVERVIEW.TXT b/libc/docs/OVERVIEW.TXT
index 753e48a..a0a3493 100644
--- a/libc/docs/OVERVIEW.TXT
+++ b/libc/docs/OVERVIEW.TXT
@@ -282,16 +282,9 @@
 
      - read /system/etc/resolv.conf instead of /etc/resolv.conf
 
-     - read the list of servers from system properties. the code looks for
-       'net.dns1', 'net.dns2', etc.. Each property should contain the IP
-       address of a DNS server.
-
-       these properties are set/modified by other parts of the Android system
-       (e.g. the dhcpd daemon).
-
-       the implementation also supports per-process DNS server list, using the
-       properties 'net.dns1.<pid>', 'net.dns2.<pid>', etc... Where <pid> stands
-       for the numerical ID of the current process.
+     - get the list of servers and the search domains for this process's
+       current interface from the dns cache.  This information is sent
+       from the framework via cache functions.
 
      - when performing a query, use a properly randomized Query ID (instead of
        a incremented one), for increased security.
diff --git a/libc/include/netdb.h b/libc/include/netdb.h
index ead5954..3ea512c 100644
--- a/libc/include/netdb.h
+++ b/libc/include/netdb.h
@@ -207,11 +207,13 @@
 void endservent(void);
 void freehostent(struct hostent *);
 struct hostent	*gethostbyaddr(const void *, socklen_t, int);
+struct hostent	*android_gethostbyaddrforiface(const void *, socklen_t, int, const char*);
 int gethostbyaddr_r(const void *, int, int, struct hostent *, char *, size_t, struct hostent **, int *);
 struct hostent	*gethostbyname(const char *);
 int gethostbyname_r(const char *, struct hostent *, char *, size_t, struct hostent **, int *);
 struct hostent	*gethostbyname2(const char *, int);
 int gethostbyname2_r(const char *, int, struct hostent *, char *, size_t, struct hostent **, int *);
+struct hostent	*android_gethostbynameforiface(const char *, int, const char *);
 struct hostent	*gethostent(void);
 int gethostent_r(struct hostent *, char *, size_t, struct hostent **, int *);
 struct hostent	*getipnodebyaddr(const void *, size_t, int, int *);
@@ -239,7 +241,9 @@
 void setnetent(int);
 void setprotoent(int);
 int getaddrinfo(const char *, const char *, const struct addrinfo *, struct addrinfo **);
+int android_getaddrinfoforiface(const char *, const char *, const struct addrinfo *, const char *, struct addrinfo **);
 int getnameinfo(const struct sockaddr *, socklen_t, char *, size_t, char *, size_t, int);
+int android_getnameinfoforiface(const struct sockaddr *, socklen_t, char *, size_t, char *, size_t, int, const char *);
 void freeaddrinfo(struct addrinfo *);
 const char	*gai_strerror(int);
 void setnetgrent(const char *);
diff --git a/libc/netbsd/gethnamaddr.c b/libc/netbsd/gethnamaddr.c
index 9a9f6e2..c59d1f1 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,145 @@
 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 struct hostent *gethostbyname_internal_real(const char *name, int af, res_state res);
+
+// very similar in proxy-ness to android_getaddrinfo_proxy
 static struct hostent *
-gethostbyname_internal(const char *name, int af, res_state res)
+gethostbyname_internal(const char *name, int af, res_state res, const char *iface)
+{
+	int sock;
+	const int one = 1;
+	struct sockaddr_un proxy_addr;
+	const char *cache_mode = getenv("ANDROID_DNS_MODE");
+	FILE* proxy = NULL;
+
+	if (cache_mode != NULL && strcmp(cache_mode, "local") == 0) {
+		res_setiface(res, iface);
+		return gethostbyname_internal_real(name, af, res);
+	}
+
+	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;
+	}
+
+	proxy = fdopen(sock, "r+");
+	/* This is writing to system/netd/DnsProxyListener.cpp and changes
+	 * here need to be matched there */
+	if (fprintf(proxy, "gethostbyname %d %s %d",
+		    getpid(),
+		    iface == NULL ? "^" : iface,
+		    name == NULL ? "^" : name,
+		    af) < 0) {
+		goto exit;
+	}
+
+	if (fputc(0, proxy) == EOF || fflush(proxy) != 0) {
+		goto exit;
+	}
+
+	uint32_t size;
+	char *buf = (char *)&size;
+	if (fread(buf, 1, sizeof(size), proxy) != 1) {
+		goto exit;
+	}
+
+	/* This is reading serialized data from system/netd/DnsProxyListener.cpp
+	 * and changes here need to be matched there */
+	int result_code = (int)strtol(buf, NULL, 10);
+	if (result_code != DnsProxyQueryResult) goto exit;
+
+	if (fread(buf, 1, sizeof(size), proxy) != 1) goto exit;
+	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) != 1) goto exit;
+	ptr += size;
+	rs->host.h_name = rs->hostbuf;
+
+	char **aliases = rs->host_aliases;
+	rs->host.h_aliases = rs->host_aliases;
+	while (1) {
+		if (fread(buf, 1, sizeof(size), proxy) != 1) goto exit;
+		size = ntohl(size);
+		if (fread(ptr, 1, size, proxy) != 1) goto exit;
+		if (size == 1) {
+			*aliases = NULL;
+			break;
+		}
+		*aliases++ = ptr;
+		ptr += size;
+	}
+
+	if (fread(buf, 1, sizeof(size), proxy) != 1) goto exit;
+	rs->host.h_addrtype = ntohl(size);
+
+	if (fread(buf, 1, sizeof(size), proxy) != 1) goto exit;
+	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(buf, 1, sizeof(size), proxy) != 1) goto exit;
+		size = ntohl(size);
+		if (fread(ptr, 1, size, proxy) != 1) goto exit;
+		if (size == 1) {
+		    *addrs = NULL;
+		    break;
+		}
+		*addrs++ = ptr;
+		ptr += size;
+	}
+
+	fclose(proxy);
+	return &rs->host;
+
+exit:
+	if (proxy != NULL) {
+		fclose(proxy);
+	}
+	return NULL;
+}
+
+
+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)
@@ -641,6 +767,13 @@
 gethostbyaddr(const void *addr,
     socklen_t len, int af)
 {
+	return android_gethostbyaddrforiface(addr, len, af, NULL);
+}
+
+struct hostent *
+android_gethostbyaddrforiface(const void *addr,
+    socklen_t len, int af, const char* iface)
+{
 	const u_char *uaddr = (const u_char *)addr;
 	socklen_t size;
 	struct hostent *hp;
@@ -687,7 +820,7 @@
 	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;
@@ -1124,6 +1257,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 +1265,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 +1307,8 @@
 		free(buf);
 		return NS_NOTFOUND;
 	}
+	res_setiface(res, iface);
+	_resolv_populate_res_for_iface(res);
 	n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, sizeof(buf->buf));
 	if (n < 0) {
 		free(buf);
diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c
index 326b09c..4e23132 100644
--- a/libc/netbsd/net/getaddrinfo.c
+++ b/libc/netbsd/net/getaddrinfo.c
@@ -214,7 +214,7 @@
 
 static int str2number(const char *);
 static int explore_fqdn(const struct addrinfo *, const char *,
-	const char *, struct addrinfo **);
+	const char *, struct addrinfo **, const char *iface);
 static int explore_null(const struct addrinfo *,
 	const char *, struct addrinfo **);
 static int explore_numeric(const struct addrinfo *, const char *,
@@ -402,17 +402,15 @@
         return _test_connect(PF_INET, &addr.generic, sizeof(addr.in));
 }
 
-// Returns 0 on success, else returns non-zero on error (in which case
-// getaddrinfo should continue as normal)
+// Returns 0 on success, else returns on error.
 static int
 android_getaddrinfo_proxy(
     const char *hostname, const char *servname,
-    const struct addrinfo *hints, struct addrinfo **res)
+    const struct addrinfo *hints, struct addrinfo **res, const char *iface)
 {
 	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;
 
@@ -421,33 +419,17 @@
 	// 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;
-	}
-
-	// Temporary cautious hack to disable the DNS proxy for processes
-	// requesting special treatment.  Ideally the DNS proxy should
-	// accomodate these apps, though.
-	char propname[PROP_NAME_MAX];
-	char propvalue[PROP_VALUE_MAX];
-	snprintf(propname, sizeof(propname), "net.dns1.%d", getpid());
-	if (__system_property_get(propname, propvalue) > 0) {
-		return -1;
-	}
-
-	// Bogus things we can't serialize.  Don't use the proxy.
+	// Bogus things we can't serialize.  Don't use the proxy.  These will fail - let them.
 	if ((hostname != NULL &&
 	     strcspn(hostname, " \n\r\t^'\"") != strlen(hostname)) ||
 	    (servname != NULL &&
 	     strcspn(servname, " \n\r\t^'\"") != strlen(servname))) {
-		return -1;
+		return EAI_NODATA;
 	}
 
 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
 	if (sock < 0) {
-		return -1;
+		return EAI_NODATA;
 	}
 
 	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
@@ -459,18 +441,20 @@
 				       (const struct sockaddr*) &proxy_addr,
 				       sizeof(proxy_addr))) != 0) {
 		close(sock);
-		return -1;
+		return EAI_NODATA;
 	}
 
 	// Send the request.
 	proxy = fdopen(sock, "r+");
-	if (fprintf(proxy, "getaddrinfo %s %s %d %d %d %d",
+	if (fprintf(proxy, "getaddrinfo %s %s %d %d %d %d %s %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) {
+		    hints == NULL ? -1 : hints->ai_protocol,
+		    iface == NULL ? "^" : iface,
+		    getpid()) < 0) {
 		goto exit;
 	}
 	// literal NULL byte at end, required by FrameworkListener
@@ -580,20 +564,26 @@
 		return 0;
 	}
 
-	// Proxy failed; fall through to local
-	// resolver case.  But first clean up any
-	// memory we might've allocated.
+	// Proxy failed;
+	// clean up memory we might've allocated.
 	if (*res) {
 		freeaddrinfo(*res);
 		*res = NULL;
 	}
-	return -1;
+	return EAI_NODATA;
 }
 
 int
 getaddrinfo(const char *hostname, const char *servname,
     const struct addrinfo *hints, struct addrinfo **res)
 {
+	return android_getaddrinfoforiface(hostname, servname, hints, NULL, res);
+}
+
+int
+android_getaddrinfoforiface(const char *hostname, const char *servname,
+    const struct addrinfo *hints, const char *iface, struct addrinfo **res)
+{
 	struct addrinfo sentinel;
 	struct addrinfo *cur;
 	int error = 0;
@@ -601,12 +591,12 @@
 	struct addrinfo ai0;
 	struct addrinfo *pai;
 	const struct explore *ex;
+	const char* cache_mode = getenv("ANDROID_DNS_MODE");
 
 	/* hostname is allowed to be NULL */
 	/* servname is allowed to be NULL */
 	/* hints is allowed to be NULL */
 	assert(res != NULL);
-
 	memset(&sentinel, 0, sizeof(sentinel));
 	cur = &sentinel;
 	pai = &ai;
@@ -739,9 +729,10 @@
         /*
          * BEGIN ANDROID CHANGES; proxying to the cache
          */
-        if (android_getaddrinfo_proxy(hostname, servname, hints, res) == 0) {
-            return 0;
-        }
+	if (cache_mode == NULL || strcmp(cache_mode, "local") != 0) {
+		// we're not the proxy - pass the request to them
+		return android_getaddrinfo_proxy(hostname, servname, hints, res, iface);
+	}
 
 	/*
 	 * hostname as alphabetical name.
@@ -770,7 +761,7 @@
 			pai->ai_protocol = ex->e_protocol;
 
 		error = explore_fqdn(pai, hostname, servname,
-			&cur->ai_next);
+			&cur->ai_next, iface);
 
 		while (cur && cur->ai_next)
 			cur = cur->ai_next;
@@ -803,7 +794,7 @@
  */
 static int
 explore_fqdn(const struct addrinfo *pai, const char *hostname,
-    const char *servname, struct addrinfo **res)
+    const char *servname, struct addrinfo **res, const char *iface)
 {
 	struct addrinfo *result;
 	struct addrinfo *cur;
@@ -829,7 +820,7 @@
 		return 0;
 
 	switch (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo",
-			default_dns_files, hostname, pai)) {
+			default_dns_files, hostname, pai, iface)) {
 	case NS_TRYAGAIN:
 		error = EAI_AGAIN;
 		goto free;
@@ -1897,9 +1888,11 @@
 	struct addrinfo sentinel, *cur;
 	struct res_target q, q2;
 	res_state res;
+	const char* iface;
 
 	name = va_arg(ap, char *);
 	pai = va_arg(ap, const struct addrinfo *);
+	iface = va_arg(ap, char *);
 	//fprintf(stderr, "_dns_getaddrinfo() name = '%s'\n", name);
 
 	memset(&q, 0, sizeof(q));
@@ -1981,6 +1974,12 @@
 		return NS_NOTFOUND;
 	}
 
+	/* this just sets our iface val in the thread private data so we don't have to
+	 * modify the api's all the way down to res_send.c's res_nsend.  We could
+	 * fully populate the thread private data here, but if we get down there
+	 * and have a cache hit that would be wasted, so we do the rest there on miss
+	 */
+	res_setiface(res, iface);
 	if (res_searchN(name, &q, res) < 0) {
 		__res_put_state(res);
 		free(buf);
diff --git a/libc/netbsd/net/getnameinfo.c b/libc/netbsd/net/getnameinfo.c
index d8ac037..7ab48e6 100644
--- a/libc/netbsd/net/getnameinfo.c
+++ b/libc/netbsd/net/getnameinfo.c
@@ -99,7 +99,7 @@
 };
 
 static int getnameinfo_inet __P((const struct sockaddr *, socklen_t, char *,
-    socklen_t, char *, socklen_t, int));
+    socklen_t, char *, socklen_t, int, const char*));
 #ifdef INET6
 static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
 				 socklen_t, int));
@@ -122,28 +122,33 @@
  */
 int getnameinfo(const struct sockaddr* sa, socklen_t salen, char* host, size_t hostlen, char* serv, size_t servlen, int flags)
 {
+	return android_getnameinfoforiface(sa, salen, host, hostlen, serv, servlen, flags, NULL);
+}
+
+int android_getnameinfoforiface(const struct sockaddr* sa, socklen_t salen, char* host, size_t hostlen, char* serv, size_t servlen, int flags, const char* iface)
+{
 	switch (sa->sa_family) {
 	case AF_INET:
 	case AF_INET6:
 		return getnameinfo_inet(sa, salen, host, hostlen,
-		    serv, servlen, flags);
+				serv, servlen, flags, iface);
 #if defined(ANDROID_CHANGES) && defined(AF_LINK)
 	case AF_LINK:
 		return getnameinfo_link(sa, salen, host, hostlen,
-		    serv, servlen, flags);
+				serv, servlen, flags);
 #endif
 	default:
 		return EAI_FAMILY;
 	}
 }
-
 #ifdef ANDROID_CHANGES
 /* On success length of the host name is returned. A return
  * value of 0 means there's no host name associated with
  * the address. On failure -1 is returned in which case
  * normal execution flow shall continue. */
 static int
-android_gethostbyaddr_proxy(char* nameBuf, size_t nameBufLen, const void *addr, socklen_t addrLen, int addrFamily) {
+android_gethostbyaddr_proxy(char* nameBuf, size_t nameBufLen, const void *addr, socklen_t addrLen, int addrFamily, const char* iface)
+{
 
 	int sock;
 	const int one = 1;
@@ -158,15 +163,6 @@
 		return -1;
 	}
 
-	// Temporary cautious hack to disable the DNS proxy for processes
-	// requesting special treatment.  Ideally the DNS proxy should
-	// accomodate these apps, though.
-	char propname[PROP_NAME_MAX];
-	char propvalue[PROP_VALUE_MAX];
-	snprintf(propname, sizeof(propname), "net.dns1.%d", getpid());
-	if (__system_property_get(propname, propvalue) > 0) {
-		return -1;
-	}
 	// create socket
 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
 	if (sock < 0) {
@@ -195,7 +191,8 @@
 	if (addrStr == NULL) {
 		goto exit;
 	}
-	if (fprintf(proxy, "gethostbyaddr %s %d %d", addrStr, addrLen, addrFamily) < 0) {
+	if (fprintf(proxy, "gethostbyaddr %s %d %d %d %s",
+			addrStr, addrLen, addrFamily, getpid(), iface == NULL ? "^" : iface) < 0) {
 		goto exit;
 	}
 
@@ -246,7 +243,7 @@
  * Format an IPv4 or IPv6 sockaddr into a printable string.
  */
 static int
-getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags)
+getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags, iface)
 	const struct sockaddr *sa;
 	socklen_t salen;
 	char *host;
@@ -254,6 +251,7 @@
 	char *serv;
 	socklen_t servlen;
 	int flags;
+	const char* iface;
 {
 	const struct afd *afd;
 	struct servent *sp;
@@ -398,14 +396,14 @@
 		char android_proxy_buf[MAXDNAME];
 
 		int hostnamelen = android_gethostbyaddr_proxy(android_proxy_buf,
-				MAXDNAME, addr, afd->a_addrlen, afd->a_af);
+				MAXDNAME, addr, afd->a_addrlen, afd->a_af, iface);
 		if (hostnamelen > 0) {
 			hp = &android_proxy_hostent;
 			hp->h_name = android_proxy_buf;
 		} else if (!hostnamelen) {
 			hp = NULL;
 		} else {
-			hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
+			hp = android_gethostbyaddrforiface(addr, afd->a_addrlen, afd->a_af, iface);
 		}
 #else
 		hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
diff --git a/libc/netbsd/resolv/res_cache.c b/libc/netbsd/resolv/res_cache.c
index afc9a36..23f5c24 100644
--- a/libc/netbsd/resolv/res_cache.c
+++ b/libc/netbsd/resolv/res_cache.c
@@ -43,6 +43,7 @@
 #include <arpa/inet.h>
 #include "resolv_private.h"
 #include "resolv_iface.h"
+#include "res_private.h"
 
 /* This code implements a small and *simple* DNS resolver cache.
  *
@@ -1249,9 +1250,16 @@
     struct resolv_cache_info*   next;
     char*                       nameservers[MAXNS +1];
     struct addrinfo*            nsaddrinfo[MAXNS + 1];
-    char*                       domains;
+    char                        defdname[256];
+    int                         dnsrch_offset[MAXDNSRCH+1];  // offsets into defdname
 } CacheInfo;
 
+typedef struct resolv_pidiface_info {
+    int                             pid;
+    char                            ifname[IF_NAMESIZE + 1];
+    struct resolv_pidiface_info*    next;
+} PidIfaceInfo;
+
 #define  HTABLE_VALID(x)  ((x) != NULL && (x) != HTABLE_DELETED)
 
 static void
@@ -1304,6 +1312,7 @@
             }
         } else {
             struct timespec ts = {0,0};
+            XLOG("Waiting for previous request");
             ts.tv_sec = _time_now() + PENDING_REQUEST_TIMEOUT;
             pthread_cond_timedwait(&ri->cond, &cache->lock, &ts);
         }
@@ -1399,9 +1408,8 @@
     if (cache_mode == NULL || strcmp(cache_mode, "local") != 0) {
         // Don't use the cache in local mode.  This is used by the
         // proxy itself.
-        // TODO - change this to 0 when all dns stuff uses proxy (5918973)
-        XLOG("setup cache for non-cache process. size=1");
-        return 1;
+        XLOG("setup cache for non-cache process. size=0, %s", cache_mode);
+        return 0;
     }
 
     if (__system_property_get(DNS_CACHE_SIZE_PROP_NAME, cache_size) > 0) {
@@ -1540,7 +1548,7 @@
 
         pnode = &node->hlink;
     }
-    return pnode; 
+    return pnode;
 }
 
 /* Add a new entry to the hash table. 'lookup' must be the
@@ -1781,20 +1789,28 @@
 /****************************************************************************/
 /****************************************************************************/
 
-static pthread_once_t        _res_cache_once;
+static pthread_once_t        _res_cache_once = PTHREAD_ONCE_INIT;
 
 // Head of the list of caches.  Protected by _res_cache_list_lock.
 static struct resolv_cache_info _res_cache_list;
 
+// List of pid iface pairs
+static struct resolv_pidiface_info _res_pidiface_list;
+
 // name of the current default inteface
 static char            _res_default_ifname[IF_NAMESIZE + 1];
 
 // lock protecting everything in the _resolve_cache_info structs (next ptr, etc)
 static pthread_mutex_t _res_cache_list_lock;
 
+// lock protecting the _res_pid_iface_list
+static pthread_mutex_t _res_pidiface_list_lock;
 
 /* lookup the default interface name */
 static char *_get_default_iface_locked();
+/* find the first cache that has an associated interface and return the name of the interface */
+static char* _find_any_iface_name_locked( void );
+
 /* insert resolv_cache_info into the list of resolv_cache_infos */
 static void _insert_cache_info_locked(struct resolv_cache_info* cache_info);
 /* creates a resolv_cache_info */
@@ -1815,8 +1831,14 @@
 static struct addrinfo* _get_nameserver_addr_locked(const char* ifname, int n);
 /* lookup the inteface's address */
 static struct in_addr* _get_addr_locked(const char * ifname);
-
-
+/* return 1 if the provided list of name servers differs from the list of name servers
+ * currently attached to the provided cache_info */
+static int _resolv_is_nameservers_equal_locked(struct resolv_cache_info* cache_info,
+        char** servers, int numservers);
+/* remove a resolv_pidiface_info structure from _res_pidiface_list */
+static void _remove_pidiface_info_locked(int pid);
+/* get a resolv_pidiface_info structure from _res_pidiface_list with a certain pid */
+static struct resolv_pidiface_info* _get_pid_iface_info_locked(int pid);
 
 static void
 _res_cache_init(void)
@@ -1830,37 +1852,36 @@
 
     memset(&_res_default_ifname, 0, sizeof(_res_default_ifname));
     memset(&_res_cache_list, 0, sizeof(_res_cache_list));
+    memset(&_res_pidiface_list, 0, sizeof(_res_pidiface_list));
     pthread_mutex_init(&_res_cache_list_lock, NULL);
+    pthread_mutex_init(&_res_pidiface_list_lock, NULL);
 }
 
 struct resolv_cache*
-__get_res_cache(void)
+__get_res_cache(const char* ifname)
 {
     struct resolv_cache *cache;
 
     pthread_once(&_res_cache_once, _res_cache_init);
-
     pthread_mutex_lock(&_res_cache_list_lock);
 
-    char* ifname = _get_default_iface_locked();
-
-    // if default interface not set then use the first cache
-    // associated with an interface as the default one.
-    if (ifname[0] == '\0') {
-        struct resolv_cache_info* cache_info = _res_cache_list.next;
-        while (cache_info) {
-            if (cache_info->ifname[0] != '\0') {
-                ifname = cache_info->ifname;
-                break;
+    char* iface;
+    if (ifname == NULL || ifname[0] == '\0') {
+        iface = _get_default_iface_locked();
+        if (iface[0] == '\0') {
+            char* tmp = _find_any_iface_name_locked();
+            if (tmp) {
+                iface = tmp;
             }
-
-            cache_info = cache_info->next;
         }
+    } else {
+        iface = (char *) ifname;
     }
-    cache = _get_res_cache_for_iface_locked(ifname);
+
+    cache = _get_res_cache_for_iface_locked(iface);
 
     pthread_mutex_unlock(&_res_cache_list_lock);
-    XLOG("_get_res_cache. default_ifname = %s\n", ifname);
+    XLOG("_get_res_cache: iface = %s, cache=%p\n", iface, cache);
     return cache;
 }
 
@@ -2016,11 +2037,29 @@
 static char*
 _get_default_iface_locked(void)
 {
+
     char* iface = _res_default_ifname;
 
     return iface;
 }
 
+static char*
+_find_any_iface_name_locked( void ) {
+    char* ifname = NULL;
+
+    struct resolv_cache_info* cache_info = _res_cache_list.next;
+    while (cache_info) {
+        if (cache_info->ifname[0] != '\0') {
+            ifname = cache_info->ifname;
+            break;
+        }
+
+        cache_info = cache_info->next;
+    }
+
+    return ifname;
+}
+
 void
 _resolv_set_default_iface(const char* ifname)
 {
@@ -2044,16 +2083,19 @@
     int i, rt, index;
     struct addrinfo hints;
     char sbuf[NI_MAXSERV];
+    register char *cp;
+    int *offset;
 
     pthread_once(&_res_cache_once, _res_cache_init);
-
     pthread_mutex_lock(&_res_cache_list_lock);
+
     // creates the cache if not created
     _get_res_cache_for_iface_locked(ifname);
 
     struct resolv_cache_info* cache_info = _find_cache_info_locked(ifname);
 
-    if (cache_info != NULL) {
+    if (cache_info != NULL &&
+            !_resolv_is_nameservers_equal_locked(cache_info, servers, numservers)) {
         // free current before adding new
         _free_nameservers_locked(cache_info);
 
@@ -2069,15 +2111,68 @@
             if (rt == 0) {
                 cache_info->nameservers[index] = strdup(servers[i]);
                 index++;
+                XLOG("_resolv_set_nameservers_for_iface: iface = %s, addr = %s\n",
+                        ifname, servers[i]);
             } else {
                 cache_info->nsaddrinfo[index] = NULL;
             }
         }
-        cache_info->domains = strdup(domains);
+
+        // code moved from res_init.c, load_domain_search_list
+        strlcpy(cache_info->defdname, domains, sizeof(cache_info->defdname));
+        if ((cp = strchr(cache_info->defdname, '\n')) != NULL)
+            *cp = '\0';
+        cp = cache_info->defdname;
+        offset = cache_info->dnsrch_offset;
+        while (offset < cache_info->dnsrch_offset + MAXDNSRCH) {
+            while (*cp == ' ' || *cp == '\t') /* skip leading white space */
+                cp++;
+            if (*cp == '\0') /* stop if nothing more to do */
+                break;
+            *offset++ = cp - cache_info->defdname; /* record this search domain */
+            while (*cp) { /* zero-terminate it */
+                if (*cp == ' '|| *cp == '\t') {
+                    *cp++ = '\0';
+                    break;
+                }
+                cp++;
+            }
+        }
+        *offset = -1; /* cache_info->dnsrch_offset has MAXDNSRCH+1 items */
+
+        // flush cache since new settings
+        _flush_cache_for_iface_locked(ifname);
+
     }
+
     pthread_mutex_unlock(&_res_cache_list_lock);
 }
 
+static int
+_resolv_is_nameservers_equal_locked(struct resolv_cache_info* cache_info,
+        char** servers, int numservers)
+{
+    int i;
+    char** ns;
+    int equal = 1;
+
+    // compare each name server against current name servers
+    if (numservers > MAXNS) numservers = MAXNS;
+    for (i = 0; i < numservers && equal; i++) {
+        ns = cache_info->nameservers;
+        equal = 0;
+        while(*ns) {
+            if (strcmp(*ns, servers[i]) == 0) {
+                equal = 1;
+                break;
+            }
+            ns++;
+        }
+    }
+
+    return equal;
+}
+
 static void
 _free_nameservers_locked(struct resolv_cache_info* cache_info)
 {
@@ -2220,3 +2315,192 @@
     }
     return NULL;
 }
+
+static void
+_remove_pidiface_info_locked(int pid) {
+    struct resolv_pidiface_info* result = &_res_pidiface_list;
+    struct resolv_pidiface_info* prev = NULL;
+
+    while (result != NULL && result->pid != pid) {
+        prev = result;
+        result = result->next;
+    }
+    if (prev != NULL && result != NULL) {
+        prev->next = result->next;
+        free(result);
+    }
+}
+
+static struct resolv_pidiface_info*
+_get_pid_iface_info_locked(int pid)
+{
+    struct resolv_pidiface_info* result = &_res_pidiface_list;
+    while (result != NULL && result->pid != pid) {
+        result = result->next;
+    }
+
+    return result;
+}
+
+void
+_resolv_set_iface_for_pid(const char* ifname, int pid)
+{
+    // make sure the pid iface list is created
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_pidiface_list_lock);
+
+    struct resolv_pidiface_info* pidiface_info = _get_pid_iface_info_locked(pid);
+    if (!pidiface_info) {
+        pidiface_info = calloc(sizeof(*pidiface_info), 1);
+        if (pidiface_info) {
+            pidiface_info->pid = pid;
+            int len = sizeof(pidiface_info->ifname);
+            strncpy(pidiface_info->ifname, ifname, len - 1);
+            pidiface_info->ifname[len - 1] = '\0';
+
+            pidiface_info->next = _res_pidiface_list.next;
+            _res_pidiface_list.next = pidiface_info;
+
+            XLOG("_resolv_set_iface_for_pid: pid %d , iface %s\n", pid, ifname);
+        } else {
+            XLOG("_resolv_set_iface_for_pid failing calloc");
+        }
+    }
+
+    pthread_mutex_unlock(&_res_pidiface_list_lock);
+}
+
+void
+_resolv_clear_iface_for_pid(int pid)
+{
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_pidiface_list_lock);
+
+    _remove_pidiface_info_locked(pid);
+
+    XLOG("_resolv_clear_iface_for_pid: pid %d\n", pid);
+
+    pthread_mutex_unlock(&_res_pidiface_list_lock);
+}
+
+int
+_resolv_get_pids_associated_interface(int pid, char* buff, int buffLen)
+{
+    int len = 0;
+
+    if (!buff) {
+        return -1;
+    }
+
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_pidiface_list_lock);
+
+    struct resolv_pidiface_info* pidiface_info = _get_pid_iface_info_locked(pid);
+    buff[0] = '\0';
+    if (pidiface_info) {
+        len = strlen(pidiface_info->ifname);
+        if (len < buffLen) {
+            strncpy(buff, pidiface_info->ifname, len);
+            buff[len] = '\0';
+        }
+    }
+
+    XLOG("_resolv_get_pids_associated_interface buff: %s\n", buff);
+
+    pthread_mutex_unlock(&_res_pidiface_list_lock);
+
+    return len;
+}
+
+int
+_resolv_get_default_iface(char* buff, int buffLen)
+{
+    char* ifname;
+    int len = 0;
+
+    if (!buff || buffLen == 0) {
+        return -1;
+    }
+
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
+
+    ifname = _get_default_iface_locked();
+
+    // if default interface not set. Get first cache with an interface
+    if (ifname[0] == '\0') {
+        ifname = _find_any_iface_name_locked();
+    }
+
+    if (ifname) {
+        len = strlen(ifname);
+        if (len < buffLen) {
+            strncpy(buff, ifname, len);
+            buff[len] = '\0';
+        }
+    } else {
+        buff[0] = '\0';
+    }
+
+    pthread_mutex_unlock(&_res_cache_list_lock);
+
+    return len;
+}
+
+int
+_resolv_populate_res_for_iface(res_state statp)
+{
+    int nserv;
+    struct resolv_cache_info* info = NULL;
+
+    if (statp) {
+        struct addrinfo* ai;
+
+        if (statp->iface[0] == '\0') { // no interface set assign default
+            _resolv_get_default_iface(statp->iface, sizeof(statp->iface));
+        }
+
+        pthread_once(&_res_cache_once, _res_cache_init);
+        pthread_mutex_lock(&_res_cache_list_lock);
+        info = _find_cache_info_locked(statp->iface);
+
+        if (info == NULL) return 0;
+
+        XLOG("_resolv_populate_res_for_iface: %s\n", statp->iface);
+        for (nserv = 0; nserv < MAXNS; nserv++) {
+            ai = info->nsaddrinfo[nserv];
+            if (ai == NULL) {
+                break;
+            }
+
+            if ((size_t) ai->ai_addrlen <= sizeof(statp->_u._ext.ext->nsaddrs[0])) {
+                if (statp->_u._ext.ext != NULL) {
+                    memcpy(&statp->_u._ext.ext->nsaddrs[nserv], ai->ai_addr, ai->ai_addrlen);
+                    statp->nsaddr_list[nserv].sin_family = AF_UNSPEC;
+                } else {
+                    if ((size_t) ai->ai_addrlen
+                            <= sizeof(statp->nsaddr_list[0])) {
+                        memcpy(&statp->nsaddr_list[nserv], ai->ai_addr,
+                                ai->ai_addrlen);
+                    } else {
+                        statp->nsaddr_list[nserv].sin_family = AF_UNSPEC;
+                    }
+                }
+            } else {
+                XLOG("_resolv_populate_res_for_iface found too long addrlen");
+            }
+        }
+        statp->nscount = nserv;
+        // now do search domains.  Note that we cache the offsets as this code runs alot
+        // but the setting/offset-computer only runs when set/changed
+        strlcpy(statp->defdname, info->defdname, sizeof(statp->defdname));
+        register char **pp = statp->dnsrch;
+        register int *p = info->dnsrch_offset;
+        while (pp < statp->dnsrch + MAXDNSRCH && *p != -1) {
+            *pp++ = &statp->defdname + *p++;
+        }
+
+        pthread_mutex_unlock(&_res_cache_list_lock);
+    }
+    return nserv;
+}
diff --git a/libc/netbsd/resolv/res_data.c b/libc/netbsd/resolv/res_data.c
index 014c99b..7e5a308 100644
--- a/libc/netbsd/resolv/res_data.c
+++ b/libc/netbsd/resolv/res_data.c
@@ -82,13 +82,7 @@
 
 int  res_ourserver_p(const res_state, const struct sockaddr *);
 
-#ifdef ANDROID_CHANGES
-static int res_need_init() {
-	return ((_nres.options & RES_INIT) == 0U) || res_get_dns_changed();
-}
-#else
 #define res_need_init()   ((_nres.options & RES_INIT) == 0U)
-#endif
 
 int
 res_init(void) {
diff --git a/libc/netbsd/resolv/res_init.c b/libc/netbsd/resolv/res_init.c
index 56a25af..ff65299 100644
--- a/libc/netbsd/resolv/res_init.c
+++ b/libc/netbsd/resolv/res_init.c
@@ -111,13 +111,6 @@
 /* ensure that sockaddr_in6 and IN6ADDR_ANY_INIT are declared / defined */
 #ifdef ANDROID_CHANGES
 #include "resolv_private.h"
-#define MAX_DNS_PROPERTIES 8
-#define DNS_PROP_NAME_PREFIX "net.dns"
-#define DNS_CHANGE_PROP_NAME "net.dnschange"
-#define DNS_SEARCH_PROP_NAME "net.dns.search"
-static const prop_info *dns_change_prop;
-static int dns_last_change_counter;
-static int _get_dns_change_count();
 #else
 #include <resolv.h>
 #endif
@@ -171,41 +164,6 @@
 	return (__res_vinit(statp, 0));
 }
 
-#ifdef ANDROID_CHANGES
-static int load_domain_search_list(res_state statp) {
-	char propvalue[PROP_VALUE_MAX];
-	register char *cp, **pp;
-
-	if(__system_property_get(DNS_SEARCH_PROP_NAME, propvalue) >= 1) {
-		strlcpy(statp->defdname, propvalue, sizeof(statp->defdname));
-		if ((cp = strchr(statp->defdname, '\n')) != NULL)
-			*cp = '\0';
-		cp = statp->defdname;
-		pp = statp->dnsrch;
-		while ( pp < statp->dnsrch + MAXDNSRCH ) {
-			while (*cp == ' ' || *cp == '\t') /* skip leading white space */
-				cp++;
-			if (*cp == '\0')  /* stop if nothing more */
-				break;
-			*pp++ = cp;  /* record this search domain */
-			while (*cp) { /* zero-terminate it */
-				if (*cp == ' ' || *cp == '\t') {
-					*cp++ = '\0';
-					break;
-				}
-				cp++;
-			}
-		}
-		*pp = NULL; /* statp->dnsrch has MAXDNSRCH+1 items */
-		if (pp > statp->dnsrch)
-			return 1;
-	}
-	statp->defdname[0] = '\0';  /* no default domain name on Android */
-	statp->dnsrch[0] = NULL;
-	return 0;
-}
-#endif
-
 /* This function has to be reachable by res_data.c but not publicly. */
 int
 __res_vinit(res_state statp, int preinit) {
@@ -220,12 +178,6 @@
 	char *net;
 	int dots;
 	union res_sockaddr_union u[2];
-#ifdef ANDROID_CHANGES
-        pid_t mypid = getpid();
-        int use_proc_props = 0;
-        int found_prop;
-	char dnsProperty[PROP_VALUE_MAX];
-#endif
 
         if ((statp->options & RES_INIT) != 0U)
                 res_ndestroy(statp);
@@ -318,74 +270,8 @@
 	if (nserv > 0)
 		statp->nscount = nserv;
 #endif
-#ifdef ANDROID_CHANGES /* READ FROM SYSTEM PROPERTIES */
-	dns_last_change_counter = _get_dns_change_count();
 
-	nserv = 0;
-	for(n = 1; n <= MAX_DNS_PROPERTIES && nserv < MAXNS; n++) {
-		char propname[PROP_NAME_MAX];
-		char propvalue[PROP_VALUE_MAX];
-
-		struct addrinfo hints, *ai;
-		char sbuf[NI_MAXSERV];
-		const size_t minsiz = sizeof(statp->_u._ext.ext->nsaddrs[0]);
-
-		/*
-		 * Check first for process-specific properties, and if those don't
-		 * exist, try the generic properties.
-		 */
-		found_prop = 0;
-		if (n == 1 || use_proc_props) {
-			snprintf(propname, sizeof(propname), "%s%d.%d", DNS_PROP_NAME_PREFIX, n, mypid);
-			if(__system_property_get(propname, propvalue) < 1) {
-				if (use_proc_props) {
-					break;
-				}
-			} else {
-				found_prop = 1;
-				use_proc_props = 1;
-			}
-		}
-		if (!found_prop) {
-			snprintf(propname, sizeof(propname), "%s%d", DNS_PROP_NAME_PREFIX, n);
-			if(__system_property_get(propname, propvalue) < 1) {
-				break;
-			}
-		}
-
-		cp = propvalue;
-
-		while (*cp == ' ' || *cp == '\t')
-			cp++;
-		cp[strcspn(cp, ";# \t\n")] = '\0';
-		if ((*cp != '\0') && (*cp != '\n')) {
-			memset(&hints, 0, sizeof(hints));
-			hints.ai_family = PF_UNSPEC;
-			hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
-			hints.ai_flags = AI_NUMERICHOST;
-			sprintf(sbuf, "%u", NAMESERVER_PORT);
-			if (getaddrinfo(cp, sbuf, &hints, &ai) == 0 &&
-			    (size_t)ai->ai_addrlen <= minsiz) {
-				if (statp->_u._ext.ext != NULL) {
-					memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
-					       ai->ai_addr, ai->ai_addrlen);
-				}
-				if ((size_t)ai->ai_addrlen <=
-				    sizeof(statp->nsaddr_list[nserv])) {
-					memcpy(&statp->nsaddr_list[nserv],
-					       ai->ai_addr, ai->ai_addrlen);
-				} else {
-					statp->nsaddr_list[nserv].sin_family = 0;
-				}
-				freeaddrinfo(ai);
-				nserv++;
-			}
-		}
-	}
-
-	/* Add the domain search list */
-	havesearch = load_domain_search_list(statp);
-#else /* !ANDROID_CHANGES - IGNORE resolv.conf in Android */
+#ifndef ANDROID_CHANGES /* !ANDROID_CHANGES - IGNORE resolv.conf in Android */
 #define	MATCH(line, name) \
 	(!strncmp(line, name, sizeof(name) - 1) && \
 	(line[sizeof(name) - 1] == ' ' || \
@@ -907,32 +793,17 @@
 }
 
 #ifdef ANDROID_CHANGES
-static int _get_dns_change_count()
+void res_setiface(res_state statp, const char* iface)
 {
-	if (dns_change_prop == NULL) {
-		dns_change_prop = __system_property_find(DNS_CHANGE_PROP_NAME);
-	}
-	if (dns_change_prop != NULL) {
-		char propvalue[PROP_VALUE_MAX];
-		if (__system_property_read(dns_change_prop, NULL, propvalue) >= 1) {
-			return atoi(propvalue);
+	if (statp != NULL) {
+		// set interface
+		if (iface && iface[0] != '\0') {
+			int len = sizeof(statp->iface);
+			strncpy(statp->iface, iface, len - 1);
+			statp->iface[len - 1] = '\0';
+		} else {
+			statp->iface[0] = '\0';
 		}
 	}
-	return -1;
-}
-
-int res_get_dns_changed()
-{
-	int change_count;
-
-	change_count = _get_dns_change_count();
-	if (change_count != dns_last_change_counter) {
-		if (change_count != -1) {
-			dns_last_change_counter = change_count;
-		}
-		return 1;
-	} else {
-		return 0;
-	}
 }
 #endif /* ANDROID_CHANGES */
diff --git a/libc/netbsd/resolv/res_send.c b/libc/netbsd/resolv/res_send.c
index f3ee539..0be6e8e 100644
--- a/libc/netbsd/resolv/res_send.c
+++ b/libc/netbsd/resolv/res_send.c
@@ -370,10 +370,13 @@
         ResolvCacheStatus     cache_status = RESOLV_CACHE_UNSUPPORTED;
 #endif
 
+#if !USE_RESOLV_CACHE
 	if (statp->nscount == 0) {
 		errno = ESRCH;
 		return (-1);
 	}
+#endif
+
 	if (anssiz < HFIXEDSZ) {
 		errno = EINVAL;
 		return (-1);
@@ -385,7 +388,8 @@
 	terrno = ETIMEDOUT;
 
 #if USE_RESOLV_CACHE
-        cache = __get_res_cache();
+        // get the cache associated with the interface
+        cache = __get_res_cache(statp->iface);
         if (cache != NULL) {
             int  anslen = 0;
             cache_status = _resolv_cache_lookup(
@@ -394,8 +398,17 @@
 
             if (cache_status == RESOLV_CACHE_FOUND) {
                 return anslen;
+            } else {
+                // had a cache miss for a known interface, so populate the thread private
+                // data so the normal resolve path can do its thing
+                _resolv_populate_res_for_iface(statp);
             }
         }
+
+        if (statp->nscount == 0) {
+            errno = ESRCH;
+            return (-1);
+        }
 #endif
 
 	/*
diff --git a/libc/netbsd/resolv/res_state.c b/libc/netbsd/resolv/res_state.c
index e05846a..efaa519 100644
--- a/libc/netbsd/resolv/res_state.c
+++ b/libc/netbsd/resolv/res_state.c
@@ -50,7 +50,7 @@
 #endif
 
 static pthread_key_t   _res_key;
-static pthread_once_t  _res_once;
+static pthread_once_t  _res_once = PTHREAD_ONCE_INIT;
 
 typedef struct {
     int                  _h_errno;
diff --git a/libc/private/resolv_cache.h b/libc/private/resolv_cache.h
index 1dcc53f..4f32fb7 100644
--- a/libc/private/resolv_cache.h
+++ b/libc/private/resolv_cache.h
@@ -28,13 +28,13 @@
 #ifndef _RESOLV_CACHE_H_
 #define _RESOLV_CACHE_H_
 
+struct __res_state;
 struct resolv_cache;  /* forward */
 
-/* gets the cache for the default interface. Might be NULL*/
-extern struct resolv_cache*  __get_res_cache(void);
-
-/* get the cache for a specified interface. Can be NULL*/
-extern struct resolv_cache* __get_res_cache_for_iface(const char* ifname);
+/* gets the cache for an interface. Set ifname argument to NULL or
+ * empty buffer ('\0') to get cache for default interface.
+ * returned cache might be NULL*/
+extern struct resolv_cache*  __get_res_cache(const char* ifname);
 
 /* this gets called everytime we detect some changes in the DNS configuration
  * and will flush the cache */
@@ -67,8 +67,14 @@
 /* gets the address associated with the specified interface */
 extern struct in_addr* _resolv_get_addr_of_iface(const char* ifname);
 
-/* Get name of default interface */
-extern char* _resolv_get_default_iface();
+/* Copy the name of the default interface to provided buffer.
+ * Return length of buffer on success on failure -1 is returned */
+extern int _resolv_get_default_iface(char* buff, int buffLen);
+
+/* sets the name server addresses to the provided res_state structure. The
+ * name servers are retrieved from the cache which is associated
+ * with the interface to which the res_state structure is associated */
+extern int _resolv_populate_res_for_iface(struct __res_state* statp);
 
 typedef enum {
     RESOLV_CACHE_UNSUPPORTED,  /* the cache can't handle that kind of queries */
diff --git a/libc/private/resolv_iface.h b/libc/private/resolv_iface.h
index 3fe586e..bf5abad 100644
--- a/libc/private/resolv_iface.h
+++ b/libc/private/resolv_iface.h
@@ -60,6 +60,21 @@
 /* flush the cache associated with a certain interface */
 extern void _resolv_flush_cache_for_iface(const char* ifname);
 
+/* set a pid to use the name servers of the specified interface */
+extern void _resolv_set_iface_for_pid(const char* ifname, int pid);
+
+/* clear pid from being associated with an interface */
+extern void _resolv_clear_iface_for_pid(int pid);
+
+/** Gets the name of the interface to which the pid is attached.
+ *  On error, -1 is returned.
+ *  If no interface is found, 0 is returned and buff is set to empty ('\0').
+ *  If an interface is found, the name is copied to buff and the length of the name is returned.
+ *  Arguments:   pid The pid to find an interface for
+ *               buff A buffer to copy the result to
+ *               buffLen Length of buff. An interface is at most IF_NAMESIZE in length */
+extern int _resolv_get_pids_associated_interface(int pid, char* buff, int buffLen);
+
 #endif /* _BIONIC_RESOLV_IFACE_FUNCTIONS_DECLARED */
 
 __END_DECLS
diff --git a/libc/private/resolv_private.h b/libc/private/resolv_private.h
index 0f3c6c0..1c3c1a2 100644
--- a/libc/private/resolv_private.h
+++ b/libc/private/resolv_private.h
@@ -56,6 +56,7 @@
 
 #include <resolv.h>
 #include "resolv_static.h"
+#include <net/if.h>
 
 /*
  * Revision information.  This is the release date in YYYYMMDD format.
@@ -139,6 +140,7 @@
 struct __res_state_ext;
 
 struct __res_state {
+	char	iface[IF_NAMESIZE+1];
 	int	retrans;	 	/* retransmission time interval */
 	int	retry;			/* number of times to retransmit */
 #ifdef sun
@@ -491,7 +493,7 @@
 int		res_getservers(res_state,
 				    union res_sockaddr_union *, int);
 
-int res_get_dns_changed();
+void res_setiface();
 u_int  res_randomid(void);
 
 __END_DECLS