From c63e59039d28c352e3053bb81319e960c392dbd4 Mon Sep 17 00:00:00 2001 From: Mattias Falk Date: Tue, 23 Aug 2011 14:34:14 +0200 Subject: [PATCH] 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 --- libc/docs/OVERVIEW.TXT | 13 +- libc/include/netdb.h | 4 + libc/netbsd/gethnamaddr.c | 215 +++++++++++++++++++-- libc/netbsd/net/getaddrinfo.c | 76 ++++---- libc/netbsd/net/getnameinfo.c | 137 ++++--------- libc/netbsd/resolv/res_cache.c | 342 ++++++++++++++++++++++++++++++--- libc/netbsd/resolv/res_data.c | 6 - libc/netbsd/resolv/res_init.c | 149 +------------- libc/netbsd/resolv/res_send.c | 33 +++- libc/netbsd/resolv/res_state.c | 2 +- libc/private/resolv_cache.h | 20 +- libc/private/resolv_iface.h | 15 ++ libc/private/resolv_private.h | 4 +- 13 files changed, 670 insertions(+), 346 deletions(-) diff --git a/libc/docs/OVERVIEW.TXT b/libc/docs/OVERVIEW.TXT index 753e48a5b..a0a349345 100644 --- a/libc/docs/OVERVIEW.TXT +++ b/libc/docs/OVERVIEW.TXT @@ -282,16 +282,9 @@ DNS resolver: - 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.', 'net.dns2.', etc... Where 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 ead595435..3ea512c75 100644 --- a/libc/include/netdb.h +++ b/libc/include/netdb.h @@ -207,11 +207,13 @@ void endprotoent(void); 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 sethostent(int); 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 9a9f6e240..1ab987e13 100644 --- a/libc/netbsd/gethnamaddr.c +++ b/libc/netbsd/gethnamaddr.c @@ -56,6 +56,7 @@ #include #include +#include #include #include #include "arpa_nameser.h" @@ -69,6 +70,7 @@ #include #include #include +#include #ifndef LOG_AUTH # define LOG_AUTH 0 @@ -80,6 +82,9 @@ #include #include +// 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 struct hostent *_gethtbyname2(const char *, int); 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,40 +495,136 @@ gethostbyname(const char *name) 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; } 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 @@ gethostbyname_internal(const char *name, int af, res_state res) 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 @@ gethostbyaddr(const void *addr, 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 @@ _dns_gethtbyaddr(void *rv, void *cb_data, va_list ap) 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 @@ _dns_gethtbyaddr(void *rv, void *cb_data, va_list ap) 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 @@ _dns_gethtbyaddr(void *rv, void *cb_data, va_list ap) 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); diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index 326b09c58..aed6b9499 100644 --- a/libc/netbsd/net/getaddrinfo.c +++ b/libc/netbsd/net/getaddrinfo.c @@ -214,7 +214,7 @@ struct res_target { 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 @@ _have_ipv4() { 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 @@ android_getaddrinfo_proxy( // 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 @@ android_getaddrinfo_proxy( (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 @@ -488,6 +472,7 @@ android_getaddrinfo_proxy( int result_code = (int)strtol(buf, NULL, 10); // verify the code itself if (result_code != DnsProxyQueryResult ) { + fread(buf, 1, sizeof(buf), proxy); goto exit; } @@ -580,19 +565,25 @@ exit: 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; @@ -601,12 +592,12 @@ getaddrinfo(const char *hostname, const char *servname, 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 +730,10 @@ getaddrinfo(const char *hostname, const char *servname, /* * 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 +762,7 @@ getaddrinfo(const char *hostname, const char *servname, 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 +795,7 @@ getaddrinfo(const char *hostname, const char *servname, */ 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 +821,7 @@ explore_fqdn(const struct addrinfo *pai, const char *hostname, 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 +1889,11 @@ _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) 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 +1975,12 @@ _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) 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 d8ac03743..16ff0cb7b 100644 --- a/libc/netbsd/net/getnameinfo.c +++ b/libc/netbsd/net/getnameinfo.c @@ -98,8 +98,14 @@ struct sockinet { u_short si_port; }; +#if defined(ANDROID_CHANGES) +static int getnameinfo_inet __P((const struct sockaddr *, socklen_t, char *, + socklen_t, char *, socklen_t, int, const char*)); +#else static int getnameinfo_inet __P((const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int)); +#endif + #ifdef INET6 static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *, socklen_t, int)); @@ -122,15 +128,26 @@ static const int DnsProxyQueryResult = 222; */ int getnameinfo(const struct sockaddr* sa, socklen_t salen, char* host, size_t hostlen, char* serv, size_t servlen, int flags) { +#ifdef ANDROID_CHANGES + 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) +{ +#endif /* ANDROID_CHANGES */ switch (sa->sa_family) { case AF_INET: case AF_INET6: return getnameinfo_inet(sa, salen, host, hostlen, +#ifdef ANDROID_CHANGES + serv, servlen, flags, iface); +#else serv, servlen, flags); +#endif #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; @@ -143,108 +160,35 @@ int getnameinfo(const struct sockaddr* sa, socklen_t salen, char* host, size_t h * 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) +{ + struct hostent *hostResult = + android_gethostbyaddrforiface_proxy(addr, addrLen, addrFamily, iface); - int sock; - const int one = 1; - struct sockaddr_un proxy_addr; - const char* cache_mode = getenv("ANDROID_DNS_MODE"); - FILE* proxy = NULL; - int result = -1; + if (hostResult == NULL) return 0; - 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; - } + int lengthResult = strlen(hostResult->h_name); - // 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) { - 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*) (void*) &proxy_addr, - sizeof(proxy_addr))) != 0) { - close(sock); - return -1; - } - - // send request to DnsProxyListener - proxy = fdopen(sock,"r+"); - if (proxy == NULL) { - goto exit; - } - - char buf[INET6_ADDRSTRLEN]; // big enough for IPv4 and IPv6 - const char* addrStr = inet_ntop(addrFamily, addr, buf, sizeof(buf)); - if (addrStr == NULL) { - goto exit; - } - if (fprintf(proxy, "gethostbyaddr %s %d %d", addrStr, addrLen, addrFamily) < 0) { - goto exit; - } - - // literal NULL byte at end, required by FrameworkListener - if (fputc(0, proxy) == EOF || fflush(proxy) != 0) { - goto exit; - } - - result = 0; - char msg_buf[4]; - // read result code for gethostbyaddr - if (fread(msg_buf, 1, sizeof(msg_buf), proxy) != sizeof(msg_buf)) { - goto exit; - } - - int result_code = (int)strtol(msg_buf, NULL, 10); - // verify the code itself - if (result_code != DnsProxyQueryResult) { - goto exit; - } - - uint32_t name_len; - if (fread(&name_len, sizeof(name_len), 1, proxy) != 1) { - goto exit; - } - - name_len = ntohl(name_len); - if (name_len <= 0 || name_len >= nameBufLen) { - goto exit; - } - - if (fread(nameBuf, name_len, 1, proxy) != 1) { - goto exit; - } - - result = name_len; - - exit: - if (proxy != NULL) { - fclose(proxy); - } - - return result; + if (nameBuf) strncpy(nameBuf, hostResult->h_name, nameBufLen); + return lengthResult; } #endif /* * getnameinfo_inet(): * Format an IPv4 or IPv6 sockaddr into a printable string. */ +#ifdef ANDROID_CHANGES +static int +getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags, iface) + const struct sockaddr *sa; + socklen_t salen; + char *host; + socklen_t hostlen; + char *serv; + socklen_t servlen; + int flags; + const char* iface; +#else static int getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags) const struct sockaddr *sa; @@ -254,6 +198,7 @@ getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags) char *serv; socklen_t servlen; int flags; +#endif { const struct afd *afd; struct servent *sp; @@ -398,14 +343,14 @@ getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags) 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 afc9a365f..08a25576c 100644 --- a/libc/netbsd/resolv/res_cache.c +++ b/libc/netbsd/resolv/res_cache.c @@ -43,6 +43,7 @@ #include #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 @@ typedef struct resolv_cache_info { 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 @@ _cache_check_pending_request_locked( struct resolv_cache* cache, Entry* key ) } } 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 @@ _res_cache_get_max_entries( void ) 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 @@ _cache_lookup_p( Cache* cache, pnode = &node->hlink; } - return pnode; + return pnode; } /* Add a new entry to the hash table. 'lookup' must be the @@ -1781,20 +1789,28 @@ Exit: /****************************************************************************/ /****************************************************************************/ -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 int _get_nameserver_locked(const char* ifname, int n, char* addr, int add 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 @@ _res_cache_init(void) 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 @@ _find_cache_info_locked(const char* ifname) 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 @@ _resolv_set_nameservers_for_iface(const char* ifname, char** servers, int numser 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 @@ _resolv_set_nameservers_for_iface(const char* ifname, char** servers, int numser 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,196 @@ _get_addr_locked(const char * ifname) } 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(); // never null, but may be empty + + // if default interface not set. Get first cache with an interface + if (ifname[0] == '\0') { + ifname = _find_any_iface_name_locked(); // may be null + } + + // if we got the default iface or if (no-default) the find_any call gave an answer + 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) { + pthread_mutex_unlock(&_res_cache_list_lock); + 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 014c99bf0..7e5a30849 100644 --- a/libc/netbsd/resolv/res_data.c +++ b/libc/netbsd/resolv/res_data.c @@ -82,13 +82,7 @@ extern struct __res_state _nres; 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 56a25afb8..ff6529944 100644 --- a/libc/netbsd/resolv/res_init.c +++ b/libc/netbsd/resolv/res_init.c @@ -111,13 +111,6 @@ __RCSID("$NetBSD: res_init.c,v 1.8 2006/03/19 03:10:08 christos Exp $"); /* 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 #endif @@ -171,41 +164,6 @@ res_ninit(res_state statp) { 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 @@ __res_vinit(res_state statp, int preinit) { 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 @@ __res_vinit(res_state statp, int preinit) { 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 @@ res_getservers(res_state statp, union res_sockaddr_union *set, int cnt) { } #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 f3ee53979..ceb2c7782 100644 --- a/libc/netbsd/resolv/res_send.c +++ b/libc/netbsd/resolv/res_send.c @@ -370,10 +370,13 @@ res_nsend(res_state statp, 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,17 +388,27 @@ res_nsend(res_state statp, terrno = ETIMEDOUT; #if USE_RESOLV_CACHE - cache = __get_res_cache(); - if (cache != NULL) { - int anslen = 0; - cache_status = _resolv_cache_lookup( - cache, buf, buflen, - ans, anssiz, &anslen); + // get the cache associated with the interface + cache = __get_res_cache(statp->iface); + if (cache != NULL) { + int anslen = 0; + cache_status = _resolv_cache_lookup( + cache, buf, buflen, + ans, anssiz, &anslen); - if (cache_status == RESOLV_CACHE_FOUND) { - return anslen; - } - } + 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 e05846a93..efaa519a1 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 1dcc53fa1..4f32fb75f 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 @@ extern struct in_addr* _resolv_get_addr_of_default_iface(); /* 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 3fe586ec0..bf5abad7d 100644 --- a/libc/private/resolv_iface.h +++ b/libc/private/resolv_iface.h @@ -60,6 +60,21 @@ extern void _resolv_flush_cache_for_default_iface(); /* 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 0f3c6c04e..1c3c1a28c 100644 --- a/libc/private/resolv_private.h +++ b/libc/private/resolv_private.h @@ -56,6 +56,7 @@ #include #include "resolv_static.h" +#include /* * Revision information. This is the release date in YYYYMMDD format. @@ -139,6 +140,7 @@ struct res_sym { 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 @@ void res_setservers(res_state, 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