dns cache per interface iteration 2

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

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

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

added getaddrinfoforinface method which takes an
interface as an argument.

Change-Id: I851ec8ab8ce3112626ad2a729078b91d013f32fd
bug:4815099
bug:5465296
This commit is contained in:
Mattias Falk 2011-08-23 14:34:14 +02:00 committed by Robert Greenwalt
parent 0529a52c60
commit f1464ff956
13 changed files with 572 additions and 256 deletions

View File

@ -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.

View File

@ -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 *);

View File

@ -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,161 @@ 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 struct hostent *gethostbyname_internal_real(const char *name, int af, res_state res);
// very similar in proxy-ness to android_getaddrinfo_proxy
static struct hostent *
gethostbyname_internal(const char *name, int af, res_state res)
gethostbyname_internal(const char *name, int af, res_state res, const char *iface)
{
int sock;
const int one = 1;
struct sockaddr_un proxy_addr;
const char *cache_mode = getenv("ANDROID_DNS_MODE");
FILE* proxy = NULL;
if (cache_mode != NULL && strcmp(cache_mode, "local") == 0) {
res_setiface(res, iface);
return gethostbyname_internal_real(name, af, res);
}
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
return NULL;
}
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
memset(&proxy_addr, 0, sizeof(proxy_addr));
proxy_addr.sun_family = AF_UNIX;
strlcpy(proxy_addr.sun_path, "/dev/socket/dnsproxyd", sizeof(proxy_addr.sun_path));
if (TEMP_FAILURE_RETRY(connect(sock,
(const struct sockaddr*) &proxy_addr,
sizeof(proxy_addr))) != 0) {
close(sock);
return NULL;
}
proxy = fdopen(sock, "r+");
/* This is writing to system/netd/DnsProxyListener.cpp and changes
* here need to be matched there */
if (fprintf(proxy, "gethostbyname %d %s %d",
getpid(),
iface == NULL ? "^" : iface,
name == NULL ? "^" : name,
af) < 0) {
goto exit;
}
if (fputc(0, proxy) == EOF || fflush(proxy) != 0) {
goto exit;
}
uint32_t size;
char *buf = (char *)&size;
if (fread(buf, 1, sizeof(size), proxy) != 1) {
goto exit;
}
/* This is reading serialized data from system/netd/DnsProxyListener.cpp
* and changes here need to be matched there */
int result_code = (int)strtol(buf, NULL, 10);
if (result_code != DnsProxyQueryResult) goto exit;
if (fread(buf, 1, sizeof(size), proxy) != 1) goto exit;
size = ntohl(size);
res_static rs = __res_get_static();
memset(&rs->host, 0, sizeof(rs->host));
char *ptr = rs->hostbuf;
if (fread(ptr, 1, size, proxy) != 1) goto exit;
ptr += size;
rs->host.h_name = rs->hostbuf;
char **aliases = rs->host_aliases;
rs->host.h_aliases = rs->host_aliases;
while (1) {
if (fread(buf, 1, sizeof(size), proxy) != 1) goto exit;
size = ntohl(size);
if (fread(ptr, 1, size, proxy) != 1) goto exit;
if (size == 1) {
*aliases = NULL;
break;
}
*aliases++ = ptr;
ptr += size;
}
if (fread(buf, 1, sizeof(size), proxy) != 1) goto exit;
rs->host.h_addrtype = ntohl(size);
if (fread(buf, 1, sizeof(size), proxy) != 1) goto exit;
rs->host.h_length = ntohl(size);
char **addrs = rs->h_addr_ptrs;
rs->host.h_addr_list = rs->h_addr_ptrs;
while (1) {
if (fread(buf, 1, sizeof(size), proxy) != 1) goto exit;
size = ntohl(size);
if (fread(ptr, 1, size, proxy) != 1) goto exit;
if (size == 1) {
*addrs = NULL;
break;
}
*addrs++ = ptr;
ptr += size;
}
fclose(proxy);
return &rs->host;
exit:
if (proxy != NULL) {
fclose(proxy);
}
return NULL;
}
static struct hostent *
gethostbyname_internal_real(const char *name, int af, res_state res)
{
const char *cp;
char *bp, *ep;
int size;
struct hostent *hp;
struct resolv_cache* cache;
res_static rs = __res_get_static();
res_static rs = __res_get_static();
static const ns_dtab dtab[] = {
NS_FILES_CB(_gethtbyname, NULL)
@ -640,6 +766,13 @@ gethostbyname_internal(const char *name, int af, res_state res)
struct hostent *
gethostbyaddr(const void *addr,
socklen_t len, int af)
{
return android_gethostbyaddrforiface(addr, len, af, NULL);
}
struct hostent *
android_gethostbyaddrforiface(const void *addr,
socklen_t len, int af, const char* iface)
{
const u_char *uaddr = (const u_char *)addr;
socklen_t size;
@ -687,7 +820,7 @@ 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;
@ -1124,6 +1257,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 +1265,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 +1307,8 @@ _dns_gethtbyaddr(void *rv, void *cb_data, va_list ap)
free(buf);
return NS_NOTFOUND;
}
res_setiface(res, iface);
_resolv_populate_res_for_iface(res);
n = res_nquery(res, qbuf, C_IN, T_PTR, buf->buf, sizeof(buf->buf));
if (n < 0) {
free(buf);

View File

@ -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
@ -580,19 +564,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 +591,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 +729,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 +761,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 +794,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 +820,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 +1888,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 +1974,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);

View File

@ -99,7 +99,7 @@ struct sockinet {
};
static int getnameinfo_inet __P((const struct sockaddr *, socklen_t, char *,
socklen_t, char *, socklen_t, int));
socklen_t, char *, socklen_t, int, const char*));
#ifdef INET6
static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
socklen_t, int));
@ -121,29 +121,34 @@ static const int DnsProxyQueryResult = 222;
* appropriate function to call.
*/
int getnameinfo(const struct sockaddr* sa, socklen_t salen, char* host, size_t hostlen, char* serv, size_t servlen, int flags)
{
return android_getnameinfoforiface(sa, salen, host, hostlen, serv, servlen, flags, NULL);
}
int android_getnameinfoforiface(const struct sockaddr* sa, socklen_t salen, char* host, size_t hostlen, char* serv, size_t servlen, int flags, const char* iface)
{
switch (sa->sa_family) {
case AF_INET:
case AF_INET6:
return getnameinfo_inet(sa, salen, host, hostlen,
serv, servlen, flags);
serv, servlen, flags, iface);
#if defined(ANDROID_CHANGES) && defined(AF_LINK)
case AF_LINK:
return getnameinfo_link(sa, salen, host, hostlen,
serv, servlen, flags);
serv, servlen, flags);
#endif
default:
return EAI_FAMILY;
}
}
#ifdef ANDROID_CHANGES
/* On success length of the host name is returned. A return
* value of 0 means there's no host name associated with
* the address. On failure -1 is returned in which case
* normal execution flow shall continue. */
static int
android_gethostbyaddr_proxy(char* nameBuf, size_t nameBufLen, const void *addr, socklen_t addrLen, int addrFamily) {
android_gethostbyaddr_proxy(char* nameBuf, size_t nameBufLen, const void *addr, socklen_t addrLen, int addrFamily, const char* iface)
{
int sock;
const int one = 1;
@ -158,15 +163,6 @@ android_gethostbyaddr_proxy(char* nameBuf, size_t nameBufLen, const void *addr,
return -1;
}
// Temporary cautious hack to disable the DNS proxy for processes
// requesting special treatment. Ideally the DNS proxy should
// accomodate these apps, though.
char propname[PROP_NAME_MAX];
char propvalue[PROP_VALUE_MAX];
snprintf(propname, sizeof(propname), "net.dns1.%d", getpid());
if (__system_property_get(propname, propvalue) > 0) {
return -1;
}
// create socket
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
@ -195,7 +191,8 @@ android_gethostbyaddr_proxy(char* nameBuf, size_t nameBufLen, const void *addr,
if (addrStr == NULL) {
goto exit;
}
if (fprintf(proxy, "gethostbyaddr %s %d %d", addrStr, addrLen, addrFamily) < 0) {
if (fprintf(proxy, "gethostbyaddr %s %d %d %d %s",
addrStr, addrLen, addrFamily, getpid(), iface == NULL ? "^" : iface) < 0) {
goto exit;
}
@ -246,7 +243,7 @@ android_gethostbyaddr_proxy(char* nameBuf, size_t nameBufLen, const void *addr,
* Format an IPv4 or IPv6 sockaddr into a printable string.
*/
static int
getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags)
getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags, iface)
const struct sockaddr *sa;
socklen_t salen;
char *host;
@ -254,6 +251,7 @@ getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags)
char *serv;
socklen_t servlen;
int flags;
const char* iface;
{
const struct afd *afd;
struct servent *sp;
@ -398,14 +396,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);

View File

@ -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,192 @@ _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();
// if default interface not set. Get first cache with an interface
if (ifname[0] == '\0') {
ifname = _find_any_iface_name_locked();
}
if (ifname) {
len = strlen(ifname);
if (len < buffLen) {
strncpy(buff, ifname, len);
buff[len] = '\0';
}
} else {
buff[0] = '\0';
}
pthread_mutex_unlock(&_res_cache_list_lock);
return len;
}
int
_resolv_populate_res_for_iface(res_state statp)
{
int nserv;
struct resolv_cache_info* info = NULL;
if (statp) {
struct addrinfo* ai;
if (statp->iface[0] == '\0') { // no interface set assign default
_resolv_get_default_iface(statp->iface, sizeof(statp->iface));
}
pthread_once(&_res_cache_once, _res_cache_init);
pthread_mutex_lock(&_res_cache_list_lock);
info = _find_cache_info_locked(statp->iface);
if (info == NULL) return 0;
XLOG("_resolv_populate_res_for_iface: %s\n", statp->iface);
for (nserv = 0; nserv < MAXNS; nserv++) {
ai = info->nsaddrinfo[nserv];
if (ai == NULL) {
break;
}
if ((size_t) ai->ai_addrlen <= sizeof(statp->_u._ext.ext->nsaddrs[0])) {
if (statp->_u._ext.ext != NULL) {
memcpy(&statp->_u._ext.ext->nsaddrs[nserv], ai->ai_addr, ai->ai_addrlen);
statp->nsaddr_list[nserv].sin_family = AF_UNSPEC;
} else {
if ((size_t) ai->ai_addrlen
<= sizeof(statp->nsaddr_list[0])) {
memcpy(&statp->nsaddr_list[nserv], ai->ai_addr,
ai->ai_addrlen);
} else {
statp->nsaddr_list[nserv].sin_family = AF_UNSPEC;
}
}
} else {
XLOG("_resolv_populate_res_for_iface found too long addrlen");
}
}
statp->nscount = nserv;
// now do search domains. Note that we cache the offsets as this code runs alot
// but the setting/offset-computer only runs when set/changed
strlcpy(statp->defdname, info->defdname, sizeof(statp->defdname));
register char **pp = statp->dnsrch;
register int *p = info->dnsrch_offset;
while (pp < statp->dnsrch + MAXDNSRCH && *p != -1) {
*pp++ = &statp->defdname + *p++;
}
pthread_mutex_unlock(&_res_cache_list_lock);
}
return nserv;
}

View File

@ -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) {

View File

@ -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 */

View File

@ -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,7 +388,8 @@ res_nsend(res_state statp,
terrno = ETIMEDOUT;
#if USE_RESOLV_CACHE
cache = __get_res_cache();
// get the cache associated with the interface
cache = __get_res_cache(statp->iface);
if (cache != NULL) {
int anslen = 0;
cache_status = _resolv_cache_lookup(
@ -394,8 +398,17 @@ res_nsend(res_state statp,
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
/*

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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