bionic/libc/netbsd/gethnamaddr.c
Mattias Falk c63e59039d dns cache per interface iteration 2
name server addresses are read from the dns
cache associated wih the interface on which
the request shall be done.

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

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

added getaddrinfoforinface method which takes an
interface as an argument.

bug:4815099
bug:5465296
Change-Id: I7a8fe1980cdf99d4d296ddc5c6411f0c72162263
2013-02-07 09:40:16 -08:00

1405 lines
32 KiB
C

/* $NetBSD: gethnamaddr.c,v 1.70 2006/03/22 00:03:51 christos Exp $ */
/*
* ++Copyright++ 1985, 1988, 1993
* -
* Copyright (c) 1985, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* -
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
* -
* --Copyright--
*/
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "arpa_nameser.h"
#include "resolv_private.h"
#include "resolv_cache.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdio.h>
#include <strings.h>
#include <syslog.h>
#include <unistd.h>
#ifndef LOG_AUTH
# define LOG_AUTH 0
#endif
#define MULTI_PTRS_ARE_ALIASES 1 /* XXX - experimental */
#include "nsswitch.h"
#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\"";
#define MAXPACKET (64*1024)
typedef union {
HEADER hdr;
u_char buf[MAXPACKET];
} querybuf;
typedef union {
int32_t al;
char ac;
} align;
#ifdef DEBUG
static void dprintf(const char *, res_state, ...)
__attribute__((__format__(__printf__, 1, 3)));
#endif
static struct hostent *getanswer(const querybuf *, int, const char *, int,
res_state);
static void map_v4v6_address(const char *, char *);
static void map_v4v6_hostent(struct hostent *, char **, char *);
static void addrsort(char **, int, res_state);
static void _sethtent(int);
static void _endhtent(void);
static struct hostent *_gethtent(void);
void ht_sethostent(int);
void ht_endhostent(void);
struct hostent *ht_gethostbyname(char *);
struct hostent *ht_gethostbyaddr(const char *, int, int);
void dns_service(void);
#undef dn_skipname
int dn_skipname(const u_char *, const u_char *);
static int _gethtbyaddr(void *, void *, va_list);
static int _gethtbyname(void *, void *, va_list);
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, const char *);
static const ns_src default_dns_files[] = {
{ NSSRC_FILES, NS_SUCCESS },
{ NSSRC_DNS, NS_SUCCESS },
{ 0, 0 }
};
#ifdef DEBUG
static void
dprintf(const char *msg, res_state res, ...)
{
assert(msg != NULL);
if (res->options & RES_DEBUG) {
int save = errno;
va_list ap;
va_start (ap, res);
vprintf(msg, ap);
va_end (ap);
errno = save;
}
}
#else
# define dprintf(msg, res, num) ((void)0) /*nada*/
#endif
#define BOUNDED_INCR(x) \
do { \
cp += (x); \
if (cp > eom) { \
h_errno = NO_RECOVERY; \
return NULL; \
} \
} while (/*CONSTCOND*/0)
#define BOUNDS_CHECK(ptr, count) \
do { \
if ((ptr) + (count) > eom) { \
h_errno = NO_RECOVERY; \
return NULL; \
} \
} while (/*CONSTCOND*/0)
static struct hostent *
getanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
res_state res)
{
const HEADER *hp;
const u_char *cp;
int n;
const u_char *eom, *erdata;
char *bp, **ap, **hap, *ep;
int type, class, ancount, qdcount;
int haveanswer, had_error;
int toobig = 0;
char tbuf[MAXDNAME];
const char *tname;
int (*name_ok)(const char *);
res_static rs = __res_get_static();
assert(answer != NULL);
assert(qname != NULL);
tname = qname;
rs->host.h_name = NULL;
eom = answer->buf + anslen;
switch (qtype) {
case T_A:
case T_AAAA:
name_ok = res_hnok;
break;
case T_PTR:
name_ok = res_dnok;
break;
default:
return NULL; /* XXX should be abort(); */
}
/*
* find first satisfactory answer
*/
hp = &answer->hdr;
ancount = ntohs(hp->ancount);
qdcount = ntohs(hp->qdcount);
bp = rs->hostbuf;
ep = rs->hostbuf + sizeof rs->hostbuf;
cp = answer->buf;
BOUNDED_INCR(HFIXEDSZ);
if (qdcount != 1) {
h_errno = NO_RECOVERY;
return NULL;
}
n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
if ((n < 0) || !(*name_ok)(bp)) {
h_errno = NO_RECOVERY;
return NULL;
}
BOUNDED_INCR(n + QFIXEDSZ);
if (qtype == T_A || qtype == T_AAAA) {
/* res_send() has already verified that the query name is the
* same as the one we sent; this just gets the expanded name
* (i.e., with the succeeding search-domain tacked on).
*/
n = strlen(bp) + 1; /* for the \0 */
if (n >= MAXHOSTNAMELEN) {
h_errno = NO_RECOVERY;
return NULL;
}
rs->host.h_name = bp;
bp += n;
/* The qname can be abbreviated, but h_name is now absolute. */
qname = rs->host.h_name;
}
ap = rs->host_aliases;
*ap = NULL;
rs->host.h_aliases = rs->host_aliases;
hap = rs->h_addr_ptrs;
*hap = NULL;
rs->host.h_addr_list = rs->h_addr_ptrs;
haveanswer = 0;
had_error = 0;
while (ancount-- > 0 && cp < eom && !had_error) {
n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
if ((n < 0) || !(*name_ok)(bp)) {
had_error++;
continue;
}
cp += n; /* name */
BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ);
type = _getshort(cp);
cp += INT16SZ; /* type */
class = _getshort(cp);
cp += INT16SZ + INT32SZ; /* class, TTL */
n = _getshort(cp);
cp += INT16SZ; /* len */
BOUNDS_CHECK(cp, n);
erdata = cp + n;
if (class != C_IN) {
/* XXX - debug? syslog? */
cp += n;
continue; /* XXX - had_error++ ? */
}
if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
if (ap >= &rs->host_aliases[MAXALIASES-1])
continue;
n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
if ((n < 0) || !(*name_ok)(tbuf)) {
had_error++;
continue;
}
cp += n;
if (cp != erdata) {
h_errno = NO_RECOVERY;
return NULL;
}
/* Store alias. */
*ap++ = bp;
n = strlen(bp) + 1; /* for the \0 */
if (n >= MAXHOSTNAMELEN) {
had_error++;
continue;
}
bp += n;
/* Get canonical name. */
n = strlen(tbuf) + 1; /* for the \0 */
if (n > ep - bp || n >= MAXHOSTNAMELEN) {
had_error++;
continue;
}
strlcpy(bp, tbuf, (size_t)(ep - bp));
rs->host.h_name = bp;
bp += n;
continue;
}
if (qtype == T_PTR && type == T_CNAME) {
n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
if (n < 0 || !res_dnok(tbuf)) {
had_error++;
continue;
}
cp += n;
if (cp != erdata) {
h_errno = NO_RECOVERY;
return NULL;
}
/* Get canonical name. */
n = strlen(tbuf) + 1; /* for the \0 */
if (n > ep - bp || n >= MAXHOSTNAMELEN) {
had_error++;
continue;
}
strlcpy(bp, tbuf, (size_t)(ep - bp));
tname = bp;
bp += n;
continue;
}
if (type != qtype) {
if (type != T_KEY && type != T_SIG)
syslog(LOG_NOTICE|LOG_AUTH,
"gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"",
qname, p_class(C_IN), p_type(qtype),
p_type(type));
cp += n;
continue; /* XXX - had_error++ ? */
}
switch (type) {
case T_PTR:
if (strcasecmp(tname, bp) != 0) {
syslog(LOG_NOTICE|LOG_AUTH,
AskedForGot, qname, bp);
cp += n;
continue; /* XXX - had_error++ ? */
}
n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
if ((n < 0) || !res_hnok(bp)) {
had_error++;
break;
}
#if MULTI_PTRS_ARE_ALIASES
cp += n;
if (cp != erdata) {
h_errno = NO_RECOVERY;
return NULL;
}
if (!haveanswer)
rs->host.h_name = bp;
else if (ap < &rs->host_aliases[MAXALIASES-1])
*ap++ = bp;
else
n = -1;
if (n != -1) {
n = strlen(bp) + 1; /* for the \0 */
if (n >= MAXHOSTNAMELEN) {
had_error++;
break;
}
bp += n;
}
break;
#else
rs->host.h_name = bp;
if (res->options & RES_USE_INET6) {
n = strlen(bp) + 1; /* for the \0 */
if (n >= MAXHOSTNAMELEN) {
had_error++;
break;
}
bp += n;
map_v4v6_hostent(&rs->host, &bp, ep);
}
h_errno = NETDB_SUCCESS;
return &rs->host;
#endif
case T_A:
case T_AAAA:
if (strcasecmp(rs->host.h_name, bp) != 0) {
syslog(LOG_NOTICE|LOG_AUTH,
AskedForGot, rs->host.h_name, bp);
cp += n;
continue; /* XXX - had_error++ ? */
}
if (n != rs->host.h_length) {
cp += n;
continue;
}
if (type == T_AAAA) {
struct in6_addr in6;
memcpy(&in6, cp, IN6ADDRSZ);
if (IN6_IS_ADDR_V4MAPPED(&in6)) {
cp += n;
continue;
}
}
if (!haveanswer) {
int nn;
rs->host.h_name = bp;
nn = strlen(bp) + 1; /* for the \0 */
bp += nn;
}
bp += sizeof(align) -
(size_t)((u_long)bp % sizeof(align));
if (bp + n >= &rs->hostbuf[sizeof rs->hostbuf]) {
dprintf("size (%d) too big\n", res, n);
had_error++;
continue;
}
if (hap >= &rs->h_addr_ptrs[MAXADDRS-1]) {
if (!toobig++)
dprintf("Too many addresses (%d)\n",
res, MAXADDRS);
cp += n;
continue;
}
(void)memcpy(*hap++ = bp, cp, (size_t)n);
bp += n;
cp += n;
if (cp != erdata) {
h_errno = NO_RECOVERY;
return NULL;
}
break;
default:
abort();
}
if (!had_error)
haveanswer++;
}
if (haveanswer) {
*ap = NULL;
*hap = NULL;
/*
* Note: we sort even if host can take only one address
* in its return structures - should give it the "best"
* address in that case, not some random one
*/
if (res->nsort && haveanswer > 1 && qtype == T_A)
addrsort(rs->h_addr_ptrs, haveanswer, res);
if (!rs->host.h_name) {
n = strlen(qname) + 1; /* for the \0 */
if (n > ep - bp || n >= MAXHOSTNAMELEN)
goto no_recovery;
strlcpy(bp, qname, (size_t)(ep - bp));
rs->host.h_name = bp;
bp += n;
}
if (res->options & RES_USE_INET6)
map_v4v6_hostent(&rs->host, &bp, ep);
h_errno = NETDB_SUCCESS;
return &rs->host;
}
no_recovery:
h_errno = NO_RECOVERY;
return NULL;
}
int
gethostbyname_r(const char *name, struct hostent *hp, char *buf, size_t buflen,
struct hostent**result, int *errorp)
{
struct hostent *res;
res = gethostbyname(name);
*errorp = h_errno;
if (res == NULL) {
*result = NULL;
return -1;
}
memcpy(hp, res, sizeof *hp);
*result = hp;
return 0;
}
struct hostent *
gethostbyname(const char *name)
{
struct hostent *hp;
res_state res = __res_get_state();
if (res == NULL)
return NULL;
assert(name != NULL);
/* try IPv6 first - if that fails do IPv4 */
if (res->options & RES_USE_INET6) {
hp = gethostbyname_internal(name, AF_INET6, res, NULL);
if (hp) {
__res_put_state(res);
return hp;
}
}
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, 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 *
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;
res_static rs = __res_get_static();
static const ns_dtab dtab[] = {
NS_FILES_CB(_gethtbyname, NULL)
{ NSSRC_DNS, _dns_gethtbyname, NULL }, /* force -DHESIOD */
{ 0, 0, 0 }
};
assert(name != NULL);
switch (af) {
case AF_INET:
size = INADDRSZ;
break;
case AF_INET6:
size = IN6ADDRSZ;
break;
default:
h_errno = NETDB_INTERNAL;
errno = EAFNOSUPPORT;
return NULL;
}
rs->host.h_addrtype = af;
rs->host.h_length = size;
/*
* if there aren't any dots, it could be a user-level alias.
* this is also done in res_nquery() since we are not the only
* function that looks up host names.
*/
if (!strchr(name, '.') && (cp = __hostalias(name)))
name = cp;
/*
* disallow names consisting only of digits/dots, unless
* they end in a dot.
*/
if (isdigit((u_char) name[0]))
for (cp = name;; ++cp) {
if (!*cp) {
if (*--cp == '.')
break;
/*
* All-numeric, no dot at the end.
* Fake up a hostent as if we'd actually
* done a lookup.
*/
if (inet_pton(af, name,
(char *)(void *)rs->host_addr) <= 0) {
h_errno = HOST_NOT_FOUND;
return NULL;
}
strncpy(rs->hostbuf, name, MAXDNAME);
rs->hostbuf[MAXDNAME] = '\0';
bp = rs->hostbuf + MAXDNAME;
ep = rs->hostbuf + sizeof rs->hostbuf;
rs->host.h_name = rs->hostbuf;
rs->host.h_aliases = rs->host_aliases;
rs->host_aliases[0] = NULL;
rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr;
rs->h_addr_ptrs[1] = NULL;
rs->host.h_addr_list = rs->h_addr_ptrs;
if (res->options & RES_USE_INET6)
map_v4v6_hostent(&rs->host, &bp, ep);
h_errno = NETDB_SUCCESS;
return &rs->host;
}
if (!isdigit((u_char) *cp) && *cp != '.')
break;
}
if ((isxdigit((u_char) name[0]) && strchr(name, ':') != NULL) ||
name[0] == ':')
for (cp = name;; ++cp) {
if (!*cp) {
if (*--cp == '.')
break;
/*
* All-IPv6-legal, no dot at the end.
* Fake up a hostent as if we'd actually
* done a lookup.
*/
if (inet_pton(af, name,
(char *)(void *)rs->host_addr) <= 0) {
h_errno = HOST_NOT_FOUND;
return NULL;
}
strncpy(rs->hostbuf, name, MAXDNAME);
rs->hostbuf[MAXDNAME] = '\0';
bp = rs->hostbuf + MAXDNAME;
ep = rs->hostbuf + sizeof rs->hostbuf;
rs->host.h_name = rs->hostbuf;
rs->host.h_aliases = rs->host_aliases;
rs->host_aliases[0] = NULL;
rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr;
rs->h_addr_ptrs[1] = NULL;
rs->host.h_addr_list = rs->h_addr_ptrs;
h_errno = NETDB_SUCCESS;
return &rs->host;
}
if (!isxdigit((u_char) *cp) && *cp != ':' && *cp != '.')
break;
}
hp = NULL;
h_errno = NETDB_INTERNAL;
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 *
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;
struct hostent *hp;
static const ns_dtab dtab[] = {
NS_FILES_CB(_gethtbyaddr, NULL)
{ NSSRC_DNS, _dns_gethtbyaddr, NULL }, /* force -DHESIOD */
{ 0, 0, 0 }
};
assert(addr != NULL);
if (af == AF_INET6 && len == IN6ADDRSZ &&
(IN6_IS_ADDR_LINKLOCAL((const struct in6_addr *)(const void *)uaddr) ||
IN6_IS_ADDR_SITELOCAL((const struct in6_addr *)(const void *)uaddr))) {
h_errno = HOST_NOT_FOUND;
return NULL;
}
if (af == AF_INET6 && len == IN6ADDRSZ &&
(IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)(const void *)uaddr) ||
IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)(const void *)uaddr))) {
/* Unmap. */
addr += IN6ADDRSZ - INADDRSZ;
uaddr += IN6ADDRSZ - INADDRSZ;
af = AF_INET;
len = INADDRSZ;
}
switch (af) {
case AF_INET:
size = INADDRSZ;
break;
case AF_INET6:
size = IN6ADDRSZ;
break;
default:
errno = EAFNOSUPPORT;
h_errno = NETDB_INTERNAL;
return NULL;
}
if (size != len) {
errno = EINVAL;
h_errno = NETDB_INTERNAL;
return NULL;
}
hp = NULL;
h_errno = NETDB_INTERNAL;
if (nsdispatch(&hp, dtab, NSDB_HOSTS, "gethostbyaddr",
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)
{
res_static rs = __res_get_static();
if (rs == NULL) return;
if (!rs->hostf)
rs->hostf = fopen(_PATH_HOSTS, "r" );
else
rewind(rs->hostf);
rs->stayopen = f;
}
static void
_endhtent(void)
{
res_static rs = __res_get_static();
if (rs == NULL) return;
if (rs->hostf && !rs->stayopen) {
(void) fclose(rs->hostf);
rs->hostf = NULL;
}
}
static struct hostent *
_gethtent(void)
{
char *p;
char *cp, **q;
int af, len;
res_static rs = __res_get_static();
if (!rs->hostf && !(rs->hostf = fopen(_PATH_HOSTS, "r" ))) {
h_errno = NETDB_INTERNAL;
return NULL;
}
again:
if (!(p = fgets(rs->hostbuf, sizeof rs->hostbuf, rs->hostf))) {
h_errno = HOST_NOT_FOUND;
return NULL;
}
if (*p == '#')
goto again;
if (!(cp = strpbrk(p, "#\n")))
goto again;
*cp = '\0';
if (!(cp = strpbrk(p, " \t")))
goto again;
*cp++ = '\0';
if (inet_pton(AF_INET6, p, (char *)(void *)rs->host_addr) > 0) {
af = AF_INET6;
len = IN6ADDRSZ;
} else if (inet_pton(AF_INET, p, (char *)(void *)rs->host_addr) > 0) {
res_state res = __res_get_state();
if (res == NULL)
return NULL;
if (res->options & RES_USE_INET6) {
map_v4v6_address((char *)(void *)rs->host_addr,
(char *)(void *)rs->host_addr);
af = AF_INET6;
len = IN6ADDRSZ;
} else {
af = AF_INET;
len = INADDRSZ;
}
__res_put_state(res);
} else {
goto again;
}
/* if this is not something we're looking for, skip it. */
if (rs->host.h_addrtype != 0 && rs->host.h_addrtype != af)
goto again;
if (rs->host.h_length != 0 && rs->host.h_length != len)
goto again;
rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr;
rs->h_addr_ptrs[1] = NULL;
rs->host.h_addr_list = rs->h_addr_ptrs;
rs->host.h_length = len;
rs->host.h_addrtype = af;
while (*cp == ' ' || *cp == '\t')
cp++;
rs->host.h_name = cp;
q = rs->host.h_aliases = rs->host_aliases;
if ((cp = strpbrk(cp, " \t")) != NULL)
*cp++ = '\0';
while (cp && *cp) {
if (*cp == ' ' || *cp == '\t') {
cp++;
continue;
}
if (q < &rs->host_aliases[MAXALIASES - 1])
*q++ = cp;
if ((cp = strpbrk(cp, " \t")) != NULL)
*cp++ = '\0';
}
*q = NULL;
h_errno = NETDB_SUCCESS;
return &rs->host;
}
/*ARGSUSED*/
int
_gethtbyname(void *rv, void *cb_data, va_list ap)
{
struct hostent *hp;
const char *name;
int af;
assert(rv != NULL);
name = va_arg(ap, char *);
/* NOSTRICT skip len */(void)va_arg(ap, int);
af = va_arg(ap, int);
hp = NULL;
#if 0
{
res_state res = __res_get_state();
if (res == NULL)
return NS_NOTFOUND;
if (res->options & RES_USE_INET6)
hp = _gethtbyname2(name, AF_INET6);
if (hp==NULL)
hp = _gethtbyname2(name, AF_INET);
__res_put_state(res);
}
#else
hp = _gethtbyname2(name, af);
#endif
*((struct hostent **)rv) = hp;
if (hp == NULL) {
h_errno = HOST_NOT_FOUND;
return NS_NOTFOUND;
}
return NS_SUCCESS;
}
static struct hostent *
_gethtbyname2(const char *name, int af)
{
struct hostent *p;
char *tmpbuf, *ptr, **cp;
int num;
size_t len;
res_static rs = __res_get_static();
assert(name != NULL);
_sethtent(rs->stayopen);
ptr = tmpbuf = NULL;
num = 0;
while ((p = _gethtent()) != NULL && num < MAXADDRS) {
if (p->h_addrtype != af)
continue;
if (strcasecmp(p->h_name, name) != 0) {
for (cp = p->h_aliases; *cp != NULL; cp++)
if (strcasecmp(*cp, name) == 0)
break;
if (*cp == NULL) continue;
}
if (num == 0) {
size_t bufsize;
char *src;
bufsize = strlen(p->h_name) + 2 +
MAXADDRS * p->h_length +
ALIGNBYTES;
for (cp = p->h_aliases; *cp != NULL; cp++)
bufsize += strlen(*cp) + 1;
if ((tmpbuf = malloc(bufsize)) == NULL) {
h_errno = NETDB_INTERNAL;
return NULL;
}
ptr = tmpbuf;
src = p->h_name;
while ((*ptr++ = *src++) != '\0');
for (cp = p->h_aliases; *cp != NULL; cp++) {
src = *cp;
while ((*ptr++ = *src++) != '\0');
}
*ptr++ = '\0';
ptr = (char *)(void *)ALIGN(ptr);
}
(void)memcpy(ptr, p->h_addr_list[0], (size_t)p->h_length);
ptr += p->h_length;
num++;
}
_endhtent();
if (num == 0) return NULL;
len = ptr - tmpbuf;
if (len > (sizeof(rs->hostbuf) - ALIGNBYTES)) {
free(tmpbuf);
errno = ENOSPC;
h_errno = NETDB_INTERNAL;
return NULL;
}
ptr = memcpy((void *)ALIGN(rs->hostbuf), tmpbuf, len);
free(tmpbuf);
rs->host.h_name = ptr;
while (*ptr++);
cp = rs->host_aliases;
while (*ptr) {
*cp++ = ptr;
while (*ptr++);
}
ptr++;
*cp = NULL;
ptr = (char *)(void *)ALIGN(ptr);
cp = rs->h_addr_ptrs;
while (num--) {
*cp++ = ptr;
ptr += rs->host.h_length;
}
*cp = NULL;
return &rs->host;
}
/*ARGSUSED*/
static int
_gethtbyaddr(void *rv, void *cb_data, va_list ap)
{
struct hostent *p;
const unsigned char *addr;
int len, af;
res_static rs = __res_get_static();
assert(rv != NULL);
addr = va_arg(ap, unsigned char *);
len = va_arg(ap, int);
af = va_arg(ap, int);
rs->host.h_length = len;
rs->host.h_addrtype = af;
_sethtent(rs->stayopen);
while ((p = _gethtent()) != NULL)
if (p->h_addrtype == af && !memcmp(p->h_addr, addr,
(size_t)len))
break;
_endhtent();
*((struct hostent **)rv) = p;
if (p==NULL) {
h_errno = HOST_NOT_FOUND;
return NS_NOTFOUND;
}
return NS_SUCCESS;
}
static void
map_v4v6_address(const char *src, char *dst)
{
u_char *p = (u_char *)dst;
char tmp[INADDRSZ];
int i;
assert(src != NULL);
assert(dst != NULL);
/* Stash a temporary copy so our caller can update in place. */
(void)memcpy(tmp, src, INADDRSZ);
/* Mark this ipv6 addr as a mapped ipv4. */
for (i = 0; i < 10; i++)
*p++ = 0x00;
*p++ = 0xff;
*p++ = 0xff;
/* Retrieve the saved copy and we're done. */
(void)memcpy((void *)p, tmp, INADDRSZ);
}
static void
map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep)
{
char **ap;
assert(hp != NULL);
assert(bpp != NULL);
assert(ep != NULL);
if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ)
return;
hp->h_addrtype = AF_INET6;
hp->h_length = IN6ADDRSZ;
for (ap = hp->h_addr_list; *ap; ap++) {
int i = sizeof(align) - (size_t)((u_long)*bpp % sizeof(align));
if (ep - *bpp < (i + IN6ADDRSZ)) {
/* Out of memory. Truncate address list here. XXX */
*ap = NULL;
return;
}
*bpp += i;
map_v4v6_address(*ap, *bpp);
*ap = *bpp;
*bpp += IN6ADDRSZ;
}
}
static void
addrsort(char **ap, int num, res_state res)
{
int i, j;
char **p;
short aval[MAXADDRS];
int needsort = 0;
assert(ap != NULL);
p = ap;
for (i = 0; i < num; i++, p++) {
for (j = 0 ; (unsigned)j < res->nsort; j++)
if (res->sort_list[j].addr.s_addr ==
(((struct in_addr *)(void *)(*p))->s_addr &
res->sort_list[j].mask))
break;
aval[i] = j;
if (needsort == 0 && i > 0 && j < aval[i-1])
needsort = i;
}
if (!needsort)
return;
while (needsort < num) {
for (j = needsort - 1; j >= 0; j--) {
if (aval[j] > aval[j+1]) {
char *hp;
i = aval[j];
aval[j] = aval[j+1];
aval[j+1] = i;
hp = ap[j];
ap[j] = ap[j+1];
ap[j+1] = hp;
} else
break;
}
needsort++;
}
}
struct hostent *
gethostent(void)
{
res_static rs = __res_get_static();
rs->host.h_addrtype = 0;
rs->host.h_length = 0;
return _gethtent();
}
/*ARGSUSED*/
static int
_dns_gethtbyname(void *rv, void *cb_data, va_list ap)
{
querybuf *buf;
int n, type;
struct hostent *hp;
const char *name;
int af;
res_state res;
assert(rv != NULL);
name = va_arg(ap, char *);
/* NOSTRICT skip len */(void)va_arg(ap, int);
af = va_arg(ap, int);
switch (af) {
case AF_INET:
type = T_A;
break;
case AF_INET6:
type = T_AAAA;
break;
default:
return NS_UNAVAIL;
}
buf = malloc(sizeof(*buf));
if (buf == NULL) {
h_errno = NETDB_INTERNAL;
return NS_NOTFOUND;
}
res = __res_get_state();
if (res == NULL) {
free(buf);
return NS_NOTFOUND;
}
n = res_nsearch(res, name, C_IN, type, buf->buf, sizeof(buf->buf));
if (n < 0) {
free(buf);
dprintf("res_nsearch failed (%d)\n", res, n);
__res_put_state(res);
return NS_NOTFOUND;
}
hp = getanswer(buf, n, name, type, res);
free(buf);
__res_put_state(res);
if (hp == NULL)
switch (h_errno) {
case HOST_NOT_FOUND:
return NS_NOTFOUND;
case TRY_AGAIN:
return NS_TRYAGAIN;
default:
return NS_UNAVAIL;
}
*((struct hostent **)rv) = hp;
return NS_SUCCESS;
}
/*ARGSUSED*/
static int
_dns_gethtbyaddr(void *rv, void *cb_data, va_list ap)
{
char qbuf[MAXDNAME + 1], *qp, *ep;
int n;
querybuf *buf;
struct hostent *hp;
const unsigned char *uaddr;
int len, af, advance;
res_state res;
const char* iface;
res_static rs = __res_get_static();
assert(rv != NULL);
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:
(void)snprintf(qbuf, sizeof(qbuf), "%u.%u.%u.%u.in-addr.arpa",
(uaddr[3] & 0xff), (uaddr[2] & 0xff),
(uaddr[1] & 0xff), (uaddr[0] & 0xff));
break;
case AF_INET6:
qp = qbuf;
ep = qbuf + sizeof(qbuf) - 1;
for (n = IN6ADDRSZ - 1; n >= 0; n--) {
advance = snprintf(qp, (size_t)(ep - qp), "%x.%x.",
uaddr[n] & 0xf,
((unsigned int)uaddr[n] >> 4) & 0xf);
if (advance > 0 && qp + advance < ep)
qp += advance;
else {
h_errno = NETDB_INTERNAL;
return NS_NOTFOUND;
}
}
if (strlcat(qbuf, "ip6.arpa", sizeof(qbuf)) >= sizeof(qbuf)) {
h_errno = NETDB_INTERNAL;
return NS_NOTFOUND;
}
break;
default:
abort();
}
buf = malloc(sizeof(*buf));
if (buf == NULL) {
h_errno = NETDB_INTERNAL;
return NS_NOTFOUND;
}
res = __res_get_state();
if (res == NULL) {
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);
dprintf("res_nquery failed (%d)\n", res, n);
__res_put_state(res);
return NS_NOTFOUND;
}
hp = getanswer(buf, n, qbuf, T_PTR, res);
free(buf);
if (hp == NULL) {
__res_put_state(res);
switch (h_errno) {
case HOST_NOT_FOUND:
return NS_NOTFOUND;
case TRY_AGAIN:
return NS_TRYAGAIN;
default:
return NS_UNAVAIL;
}
}
hp->h_addrtype = af;
hp->h_length = len;
(void)memcpy(rs->host_addr, uaddr, (size_t)len);
rs->h_addr_ptrs[0] = (char *)(void *)rs->host_addr;
rs->h_addr_ptrs[1] = NULL;
if (af == AF_INET && (res->options & RES_USE_INET6)) {
map_v4v6_address((char *)(void *)rs->host_addr,
(char *)(void *)rs->host_addr);
hp->h_addrtype = AF_INET6;
hp->h_length = IN6ADDRSZ;
}
__res_put_state(res);
*((struct hostent **)rv) = hp;
h_errno = NETDB_SUCCESS;
return NS_SUCCESS;
}