#include "dns_common.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <net/if.h>
#pragma weak dn_expand
#pragma weak res_ninit
#pragma weak res_ndestroy
#pragma weak res_nsearch
#pragma weak res_nclose
#pragma weak ns_get16
#pragma weak ns_get32
#pragma weak __ns_get16
#pragma weak __ns_get32
#define DNS_ALIASES 0
#define DNS_ADDRLIST 1
#define DNS_MAPDLIST 2
#ifndef tolower
#define tolower(c) ((c) >= 'A' && (c) <= 'Z' ? (c) | 0x20 : (c))
#endif
static int
dns_netdb_aliases(char **from_list, char **to_list, char **aliaspp, int type,
int *count, int af_type)
{
char *fstr;
int cnt = 0;
size_t len;
*count = 0;
if ((char *)to_list >= *aliaspp)
return (NSS_STR_PARSE_ERANGE);
for (fstr = from_list[cnt]; fstr != NULL; fstr = from_list[cnt]) {
if (type == DNS_ALIASES) {
len = strlen(fstr) + 1;
} else {
len = (af_type == AF_INET) ? sizeof (struct in_addr) :
sizeof (struct in6_addr);
}
*aliaspp -= len;
to_list[cnt] = *aliaspp;
if (*aliaspp <= (char *)&to_list[cnt+1])
return (NSS_STR_PARSE_ERANGE);
if (type == DNS_MAPDLIST) {
struct in6_addr *addr6p = (struct in6_addr *)*aliaspp;
(void) memset(addr6p, '\0', sizeof (struct in6_addr));
(void) memcpy(&addr6p->s6_addr[12], fstr,
sizeof (struct in_addr));
addr6p->s6_addr[10] = 0xffU;
addr6p->s6_addr[11] = 0xffU;
++cnt;
} else {
(void) memcpy (*aliaspp, fstr, len);
++cnt;
}
}
to_list[cnt] = NULL;
*count = cnt;
if (cnt == 0)
return (NSS_STR_PARSE_PARSE);
return (NSS_STR_PARSE_SUCCESS);
}
int
ent2result(struct hostent *he, nss_XbyY_args_t *argp, int af_type)
{
char *buffer, *limit;
int buflen = argp->buf.buflen;
int ret, count;
size_t len;
struct hostent *host;
struct in_addr *addrp;
struct in6_addr *addrp6;
limit = argp->buf.buffer + buflen;
host = (struct hostent *)argp->buf.result;
buffer = argp->buf.buffer;
host->h_addrtype = af_type;
host->h_length = (af_type == AF_INET) ? sizeof (struct in_addr) :
sizeof (struct in6_addr);
len = strlen(he->h_name) + 1;
host->h_name = buffer;
if (host->h_name + len >= limit)
return (NSS_STR_PARSE_ERANGE);
(void) memcpy(host->h_name, he->h_name, len);
buffer += len;
if (af_type == AF_INET) {
addrp = (struct in_addr *)ROUND_DOWN(limit, sizeof (*addrp));
host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **));
ret = dns_netdb_aliases(he->h_addr_list, host->h_addr_list,
(char **)&addrp, DNS_ADDRLIST, &count, af_type);
if (ret != NSS_STR_PARSE_SUCCESS)
return (ret);
host->h_aliases = host->h_addr_list + count + 1;
ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
(char **)&addrp, DNS_ALIASES, &count, af_type);
} else {
addrp6 = (struct in6_addr *)ROUND_DOWN(limit, sizeof (*addrp6));
host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **));
if (he->h_addrtype == AF_INET && af_type == AF_INET6) {
ret = dns_netdb_aliases(he->h_addr_list,
host->h_addr_list, (char **)&addrp6,
DNS_MAPDLIST, &count, af_type);
} else {
ret = dns_netdb_aliases(he->h_addr_list,
host->h_addr_list, (char **)&addrp6,
DNS_ADDRLIST, &count, af_type);
}
if (ret != NSS_STR_PARSE_SUCCESS)
return (ret);
host->h_aliases = host->h_addr_list + count + 1;
ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
(char **)&addrp6, DNS_ALIASES, &count, af_type);
}
if (ret == NSS_STR_PARSE_PARSE)
ret = NSS_STR_PARSE_SUCCESS;
return (ret);
}
int
ent2str(struct hostent *hp, nss_XbyY_args_t *ap, int af_type)
{
char **p;
char obuf[INET6_ADDRSTRLEN];
void *addr;
struct in_addr in4;
int af;
int n;
const char *res;
char **q;
int l = ap->buf.buflen;
char *s = ap->buf.buffer;
if (af_type == AF_INET && hp->h_addrtype != AF_INET)
return (NSS_STR_PARSE_PARSE);
for (p = hp->h_addr_list; *p != 0; p++) {
if (p != hp->h_addr_list) {
*s = '\n';
s++;
l--;
}
if (hp->h_addrtype == AF_INET6) {
if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)*p)) {
IN6_V4MAPPED_TO_INADDR((struct in6_addr *)*p,
&in4);
af = AF_INET;
addr = &in4;
} else {
af = AF_INET6;
addr = *p;
}
} else {
af = AF_INET;
addr = *p;
}
res = inet_ntop(af, addr, obuf, sizeof (obuf));
if (res == NULL)
return (NSS_STR_PARSE_PARSE);
if ((n = snprintf(s, l, "%s", res)) >= l)
return (NSS_STR_PARSE_ERANGE);
l -= n;
s += n;
if (hp->h_name != NULL && *hp->h_name != '\0') {
if ((n = snprintf(s, l, " %s", hp->h_name)) >= l)
return (NSS_STR_PARSE_ERANGE);
l -= n;
s += n;
}
if (p == hp->h_addr_list) {
for (q = hp->h_aliases; q && *q; q++) {
if ((n = snprintf(s, l, " %s", *q)) >= l)
return (NSS_STR_PARSE_ERANGE);
l -= n;
s += n;
}
}
}
ap->returnlen = s - ap->buf.buffer;
return (NSS_STR_PARSE_SUCCESS);
}
nss_backend_t *
_nss_dns_constr(dns_backend_op_t ops[], int n_ops)
{
dns_backend_ptr_t be;
if ((be = (dns_backend_ptr_t)malloc(sizeof (*be))) == 0)
return (0);
be->ops = ops;
be->n_ops = n_ops;
return ((nss_backend_t *)be);
}
static nss_status_t
name_is_alias(char *aliases_ptr, char *name_ptr)
{
char *host_ptr;
while (*aliases_ptr != '\0') {
host_ptr = name_ptr;
while (tolower(*host_ptr) == tolower(*aliases_ptr) &&
*host_ptr != '\0') {
host_ptr++;
aliases_ptr++;
}
if (*host_ptr == '\0' &&
(*aliases_ptr == '\0' || *aliases_ptr == ' ')) {
return (NSS_SUCCESS);
}
while (*aliases_ptr != ' ' && *aliases_ptr != '\0')
aliases_ptr++;
while (*aliases_ptr == ' ') aliases_ptr++;
}
return (NSS_NOTFOUND);
}
static int
_nss_has_interfaces(boolean_t *v4, boolean_t *v6)
{
struct ifaddrs *ifp, *i;
struct in_addr in4;
struct in6_addr in6;
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
*v4 = *v6 = B_FALSE;
if (getifaddrs(&ifp) != 0)
return (-1);
for (i = ifp; i != NULL; i = i->ifa_next) {
if (i->ifa_flags & IFF_LOOPBACK)
continue;
if ((i->ifa_flags & IFF_UP) == 0)
continue;
if (i->ifa_addr->sa_family == AF_INET) {
if (*v4 != B_FALSE)
continue;
if (((struct sockaddr_in *)i->ifa_addr)->
sin_addr.s_addr == INADDR_ANY)
continue;
*v4 = B_TRUE;
}
if (i->ifa_addr->sa_family == AF_INET6) {
if (*v6 != B_FALSE)
continue;
if (memcmp(&in6addr_any,
&((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr,
sizeof (struct in6_addr)) == 0)
continue;
*v6 = B_TRUE;
}
}
freeifaddrs(ifp);
return (0);
}
nss_status_t
_nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
{
nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
nss_XbyY_args_t arg;
char *dbname;
int dbop;
nss_status_t sret;
size_t bsize, blen;
char *bptr;
struct __res_state stat, *statp;
union msg {
uchar_t buf[NS_MAXMSG];
HEADER h;
} resbuf;
char aliases[NS_MAXMSG];
const char *name;
int qtype;
HEADER *hp;
uchar_t *cp;
uchar_t *bom;
uchar_t *eom;
uchar_t *eor;
int ancount, qdcount;
int type, class;
nssuint_t nttl, ttl, *pttl;
int n, ret;
const char *np;
char nbuf[INET6_ADDRSTRLEN];
char host[MAXHOSTNAMELEN];
char ans[MAXHOSTNAMELEN];
char aname[MAXHOSTNAMELEN];
int af;
char *ap, *apc;
int hlen = 0, alen, iplen, len, isans;
boolean_t has_v4 = B_FALSE, has_v6 = B_FALSE;
int flags, family, pass2 = 0;
statp = &stat;
(void) memset(statp, '\0', sizeof (struct __res_state));
if (res_ninit(statp) == -1) {
return (NSS_ERROR);
}
ap = apc = (char *)aliases;
alen = 0;
ttl = (nssuint_t)0xFFFFFFF;
bsize = pbuf->data_len - sizeof (nssuint_t);
bptr = (char *)buffer + pbuf->data_off;
blen = 0;
sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg);
if (sret != NSS_SUCCESS) {
res_ndestroy(statp);
return (NSS_ERROR);
}
if (ipnode) {
name = arg.key.ipnode.name;
flags = arg.key.ipnode.flags;
family = arg.key.ipnode.af_family;
if (flags != 0) {
if ((flags & AI_ADDRCONFIG) != 0) {
if (_nss_has_interfaces(&has_v4, &has_v6) !=
0) {
res_ndestroy(statp);
return (NSS_ERROR);
}
if (family == AF_INET && has_v4 == B_FALSE) {
res_ndestroy(statp);
return (NSS_NOTFOUND);
}
if (family == AF_INET6 && has_v6 == B_FALSE &&
!(flags & AI_V4MAPPED)) {
res_ndestroy(statp);
return (NSS_NOTFOUND);
}
if (family == AF_INET6 && has_v6)
qtype = T_AAAA;
if (family == AF_INET || (family == AF_INET6 &&
has_v6 == B_FALSE && flags & AI_V4MAPPED))
qtype = T_A;
} else {
has_v4 = has_v6 = B_TRUE;
if (family == AF_INET6)
qtype = T_AAAA;
else
qtype = T_A;
}
} else {
if (family == AF_INET6)
qtype = T_AAAA;
else
qtype = T_A;
}
} else {
name = arg.key.name;
qtype = T_A;
}
searchagain:
ret = res_nsearch(statp, name, C_IN, qtype, resbuf.buf, NS_MAXMSG);
if (ret == -1) {
if (statp->res_h_errno == NO_RECOVERY) {
res_ndestroy(statp);
return (NSS_ERROR);
}
if (pass2 == 2)
goto out;
if (pass2 == 1 || flags == 0 || family == AF_INET ||
(family == AF_INET6 && !(flags & AI_V4MAPPED))) {
pbuf->p_herrno = HOST_NOT_FOUND;
pbuf->p_status = NSS_NOTFOUND;
pbuf->data_len = 0;
res_ndestroy(statp);
return (NSS_NOTFOUND);
}
if ((flags & AI_ADDRCONFIG) && !(flags & AI_ALL) &&
has_v4 == B_FALSE) {
pbuf->p_herrno = HOST_NOT_FOUND;
pbuf->p_status = NSS_NOTFOUND;
pbuf->data_len = 0;
res_ndestroy(statp);
return (NSS_NOTFOUND);
}
qtype = T_A;
flags = 0;
pass2 = 1;
goto searchagain;
}
cp = resbuf.buf;
hp = (HEADER *)&resbuf.h;
bom = cp;
eom = cp + ret;
ancount = ntohs(hp->ancount);
qdcount = ntohs(hp->qdcount);
cp += HFIXEDSZ;
if (qdcount != 1) {
res_ndestroy(statp);
return (NSS_ERROR);
}
n = dn_expand(bom, eom, cp, host, MAXHOSTNAMELEN);
if (n < 0) {
res_ndestroy(statp);
return (NSS_ERROR);
} else
hlen = strlen(host);
if (hlen <= 0) {
res_ndestroy(statp);
return (NSS_ERROR);
}
cp += n + QFIXEDSZ;
if (cp > eom) {
res_ndestroy(statp);
return (NSS_ERROR);
}
while (ancount-- > 0 && cp < eom && blen < bsize) {
n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN);
if (n > 0) {
if ((isans = strncasecmp(host, ans, hlen)) != 0 &&
(alen == 0 || name_is_alias(aliases, ans)
== NSS_NOTFOUND)) {
res_ndestroy(statp);
return (NSS_ERROR);
}
}
cp += n;
type = ns_get16(cp);
cp += INT16SZ;
class = ns_get16(cp);
cp += INT16SZ;
nttl = (nssuint_t)ns_get32(cp);
if (nttl < ttl)
ttl = nttl;
cp += INT32SZ;
n = ns_get16(cp);
cp += INT16SZ;
if (class != C_IN) {
cp += n;
continue;
}
eor = cp + n;
if (type == T_CNAME) {
n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN);
if (n > 0 && (len = strlen(aname)) > 0) {
if (isans == 0) {
if (alen + hlen + 2 > NS_MAXMSG) {
res_ndestroy(statp);
return (NSS_ERROR);
}
*apc++ = ' ';
alen++;
(void) strlcpy(apc, host,
NS_MAXMSG - alen);
alen += hlen;
apc += hlen;
}
if (strlcpy(host, aname, MAXHOSTNAMELEN) >=
MAXHOSTNAMELEN) {
res_ndestroy(statp);
return (NSS_ERROR);
}
hlen = len;
}
cp += n;
continue;
}
if (type != qtype) {
cp += n;
continue;
}
if ((type == T_A && n != INADDRSZ) ||
(type == T_AAAA && n != IN6ADDRSZ)) {
cp += n;
continue;
}
af = (type == T_A ? AF_INET : AF_INET6);
np = inet_ntop(af, (void *)cp, nbuf, INET6_ADDRSTRLEN);
if (np == NULL) {
res_ndestroy(statp);
return (NSS_ERROR);
}
cp += n;
iplen = strlen(np);
len = iplen + 2 + hlen + alen;
if (alen > 0)
len++;
if (blen + len > bsize) {
res_ndestroy(statp);
return (NSS_ERROR);
}
(void) strlcpy(bptr, np, bsize - blen);
blen += iplen;
bptr += iplen;
*bptr++ = ' ';
blen++;
(void) strlcpy(bptr, host, bsize - blen);
blen += hlen;
bptr += hlen;
if (alen > 0) {
*bptr++ = ' ';
blen++;
(void) strlcpy(bptr, ap, bsize - blen);
blen += alen;
bptr += alen;
}
*bptr++ = '\n';
blen++;
}
if (qtype == T_AAAA && family == AF_INET6 &&
((flags & AI_V4MAPPED) != 0) && ((flags & AI_ALL) != 0) &&
has_v4 == B_TRUE) {
qtype = T_A;
pass2 = 2;
goto searchagain;
}
out:
len = ROUND_UP(blen, sizeof (nssuint_t));
if (len + sizeof (nssuint_t) > pbuf->data_len) {
res_ndestroy(statp);
return (NSS_ERROR);
}
pbuf->ext_off = pbuf->data_off + len;
pbuf->ext_len = sizeof (nssuint_t);
pbuf->data_len = blen;
pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off));
*pttl = ttl;
res_ndestroy(statp);
return (NSS_SUCCESS);
}