summaryrefslogtreecommitdiff
path: root/usr/src/lib/nsswitch/dns/common/dns_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/nsswitch/dns/common/dns_common.c')
-rw-r--r--usr/src/lib/nsswitch/dns/common/dns_common.c389
1 files changed, 376 insertions, 13 deletions
diff --git a/usr/src/lib/nsswitch/dns/common/dns_common.c b/usr/src/lib/nsswitch/dns/common/dns_common.c
index da766c4d12..0ed02f1d23 100644
--- a/usr/src/lib/nsswitch/dns/common/dns_common.c
+++ b/usr/src/lib/nsswitch/dns/common/dns_common.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,16 +19,27 @@
* CDDL HEADER END
*/
/*
- * dns_common.c
- *
- * Copyright (c) 1993,1998 by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * dns_common.c
+ */
#include "dns_common.h"
+#pragma weak dn_expand
+#pragma weak res_ninit
+#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
@@ -58,7 +68,8 @@ dns_netdb_aliases(from_list, to_list, aliaspp, type, count, af_type)
if (*aliaspp <= (char *)&to_list[cnt+1])
return (NSS_STR_PARSE_ERANGE);
if (type == DNS_MAPDLIST) {
- struct in6_addr *addr6p = (struct in6_addr *) *aliaspp;
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ struct in6_addr *addr6p = (struct in6_addr *)*aliaspp;
(void) memset(addr6p, '\0', sizeof (struct in6_addr));
(void) memcpy(&addr6p->s6_addr[12], fstr,
@@ -96,7 +107,7 @@ ent2result(he, argp, af_type)
struct in6_addr *addrp6;
limit = argp->buf.buffer + buflen;
- host = (struct hostent *) argp->buf.result;
+ host = (struct hostent *)argp->buf.result;
buffer = argp->buf.buffer;
/* h_addrtype and h_length */
@@ -114,7 +125,7 @@ ent2result(he, argp, af_type)
/* h_addr_list */
if (af_type == AF_INET) {
- addrp = (struct in_addr *) ROUND_DOWN(limit, sizeof (*addrp));
+ 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,
@@ -152,16 +163,368 @@ ent2result(he, argp, af_type)
return (ret);
}
+/*
+ * Convert the hostent structure into string in the following
+ * format:
+ *
+ * IP-address official-host-name nicknames ...
+ *
+ * If more than one IP-addresses matches the official-host-name,
+ * the above line will be followed by:
+ * IP-address-1 official-host-name
+ * IP-address-2 official-host-name
+ * ...
+ *
+ * This is so that the str2hostent function in libnsl
+ * can convert the string back to the original hostent
+ * data.
+ */
+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;
+
+ /*
+ * for "hosts" lookup, we only want address type of
+ * AF_INET. For "ipnodes", we can have both AF_INET
+ * and AF_INET6.
+ */
+ 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) {
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)*p)) {
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ 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 %s", res, 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)
+ 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);
+ return ((nss_backend_t *)be);
+}
+
+
+/*
+ * nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
+ * nss2 get hosts/ipnodes with ttl backend DNS search engine.
+ *
+ * This API is given a pointer to a packed buffer, and the buffer size
+ * It's job is to perform the appropriate res_nsearch, extract the
+ * results and build a unmarshalled hosts/ipnodes result buffer.
+ * Additionally in the extended results a nssuint_t ttl is placed.
+ * This ttl is the lessor of the ttl's extracted from the result.
+ *
+ * ***Currently the first version of this API only performs simple
+ * single res_nsearch lookups for with T_A or T_AAAA results.
+ * Other searches are deferred to the generic API w/t ttls.
+ *
+ * This function is not a generic res_* operation. It only performs
+ * a single T_A or T_AAAA lookups***
+ *
+ * RETURNS: NSS_SUCCESS or NSS_ERROR
+ * If an NSS_ERROR result is returned, nscd is expected
+ * to resubmit the gethosts request using the old style
+ * nsswitch lookup format.
+ */
+
+struct tsd_priv {
+ struct __res_state *statp; /* dns state block */
+ union msg {
+ uchar_t buf[NS_MAXMSG]; /* max legal DNS answer size */
+ HEADER h;
+ } resbuf;
+ char aliases[NS_MAXMSG]; /* set of aliases */
+};
+
+static void ghttlcleanup(void *ptr)
+{
+ struct tsd_priv *priv = (struct tsd_priv *)ptr;
+
+ if (priv) {
+ if (priv->statp != NULL) {
+ res_nclose(priv->statp);
+ free((void *)priv->statp);
+ }
+ free(ptr);
+ }
+}
+
+nss_status_t
+_nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
+{
+ /* nss buffer variables */
+ 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;
+ /* resolver query variables */
+ static mutex_t keylock;
+ static thread_key_t key;
+ static int once_per_keyname = 0;
+ struct tsd_priv *tsd = NULL;
+ const char *name;
+ int qtype;
+ /* answer parsing variables */
+ HEADER *hp;
+ uchar_t *cp; /* current location in message */
+ uchar_t *bom; /* start of message */
+ uchar_t *eom; /* end of message */
+ uchar_t *eor; /* end of record */
+ int ancount, qdcount;
+ int type, class;
+ nssuint_t nttl, ttl, *pttl; /* The purpose of this API */
+ int n, ret;
+ const char *np;
+ /* temporary buffers */
+ char nbuf[INET6_ADDRSTRLEN]; /* address parser */
+ char host[MAXHOSTNAMELEN]; /* result host name */
+ char ans[MAXHOSTNAMELEN]; /* record name */
+ char aname[MAXHOSTNAMELEN]; /* alias result (C_NAME) */
+ /* misc variables */
+ int af;
+ char *ap, *apc;
+ int hlen, alen, iplen, len;
+
+ if (!once_per_keyname) {
+ (void) mutex_lock(&keylock);
+ if (!once_per_keyname) {
+ (void) thr_keycreate(&key, ghttlcleanup);
+ once_per_keyname++;
+ }
+ (void) mutex_unlock(&keylock);
+ }
+ (void) thr_getspecific(key, (void **)&tsd);
+ if (tsd == NULL) {
+ tsd = (struct tsd_priv *)calloc(1, sizeof (struct tsd_priv));
+ (void) thr_setspecific(key, (void *)tsd);
+ (void) thr_getspecific(key, (void **)&tsd);
+ tsd->statp = (struct __res_state *)
+ calloc(1, sizeof (struct __res_state));
+ if (tsd->statp == NULL)
+ return (NSS_ERROR);
+ if (res_ninit(tsd->statp) == -1) {
+ free(tsd->statp);
+ return (NSS_ERROR);
+ }
+ }
+ ap = apc = (char *)tsd->aliases;
+ alen = 0;
+ ttl = (nssuint_t)0xFFFFFFF; /* start w/max, find smaller */
+
+ /* save space for ttl otherwise, why bother... */
+ 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) {
+ return (NSS_ERROR);
+ }
+
+ if (ipnode) {
+ /* initially only handle the simple cases */
+ if (arg.key.ipnode.flags != 0)
+ return (NSS_ERROR);
+ name = arg.key.ipnode.name;
+ if (arg.key.ipnode.af_family == AF_INET6)
+ qtype = T_AAAA;
+ else
+ qtype = T_A;
+ } else {
+ name = arg.key.name;
+ qtype = T_A;
+ }
+ ret = res_nsearch(tsd->statp, name, C_IN, qtype,
+ tsd->resbuf.buf, NS_MAXMSG);
+ if (ret == -1) {
+ if (tsd->statp->res_h_errno == HOST_NOT_FOUND) {
+ pbuf->p_herrno = HOST_NOT_FOUND;
+ pbuf->p_status = NSS_NOTFOUND;
+ pbuf->data_len = 0;
+ return (NSS_NOTFOUND);
+ }
+ /* else lookup error - handle in general code */
+ return (NSS_ERROR);
+ }
+
+ cp = tsd->resbuf.buf;
+ hp = (HEADER *)&tsd->resbuf.h;
+ bom = cp;
+ eom = cp + ret;
+
+ ancount = ntohs(hp->ancount);
+ qdcount = ntohs(hp->qdcount);
+ cp += HFIXEDSZ;
+ if (qdcount != 1)
+ return (NSS_ERROR);
+ n = dn_expand(bom, eom, cp, host, MAXHOSTNAMELEN);
+ if (n < 0) {
+ return (NSS_ERROR);
+ } else
+ hlen = strlen(host);
+ cp += n + QFIXEDSZ;
+ if (cp > eom)
+ return (NSS_ERROR);
+ while (ancount-- > 0 && cp < eom && blen < bsize) {
+ n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN);
+ if (n > 0) {
+ if (strncasecmp(host, ans, hlen) != 0)
+ return (NSS_ERROR); /* spoof? */
+ }
+ cp += n;
+ /* bounds check */
+ type = ns_get16(cp); /* type */
+ cp += INT16SZ;
+ class = ns_get16(cp); /* class */
+ cp += INT16SZ;
+ nttl = (nssuint_t)ns_get32(cp); /* ttl in sec */
+ if (nttl < ttl)
+ ttl = nttl;
+ cp += INT32SZ;
+ n = ns_get16(cp); /* len */
+ cp += INT16SZ;
+ if (class != C_IN) {
+ cp += n;
+ continue;
+ }
+ eor = cp + n;
+ if (type == T_CNAME) {
+ /* add an alias to the alias list */
+ n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN);
+ if (n > 0) {
+ len = strlen(aname);
+ if (len > 0) {
+ /*
+ * Just error out if there is an
+ * attempted buffer overflow exploit
+ * generic code will do a syslog
+ */
+ if (alen + len + 2 > NS_MAXMSG)
+ return (NSS_ERROR);
+ *apc++ = ' ';
+ alen++;
+ (void) strlcpy(apc, aname, len + 1);
+ alen += len;
+ apc += len;
+ }
+ }
+ cp += n;
+ continue;
+ }
+ if (type != qtype) {
+ cp += n;
+ continue;
+ }
+ /* check data size */
+ 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)
+ return (NSS_ERROR);
+ cp += n;
+ /* append IP host aliases to results */
+ iplen = strlen(np);
+ /* ip <SP> hostname [<SP>][aliases] */
+ len = iplen + 2 + hlen + alen;
+ if (alen > 0)
+ len++;
+ if (blen + len > bsize)
+ 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++;
+ }
+ /* Presumably the buffer is now filled. */
+ len = ROUND_UP(blen, sizeof (nssuint_t));
+ /* still room? */
+ if (len + sizeof (nssuint_t) > pbuf->data_len) {
+ /* sigh, no, what happened? */
+ 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;
+ return (NSS_SUCCESS);
}