Merge "dns cache per interface iteration 2"
This commit is contained in:
commit
a7d9b655e5
@ -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.<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.
|
||||
|
@ -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 *);
|
||||
|
@ -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 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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 @@ 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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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 <resolv.h>
|
||||
#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 */
|
||||
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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_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
|
||||
|
Loading…
Reference in New Issue
Block a user