summaryrefslogtreecommitdiff
path: root/usr/src/lib/nsswitch/dns/common/gethostent6.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/nsswitch/dns/common/gethostent6.c')
-rw-r--r--usr/src/lib/nsswitch/dns/common/gethostent6.c534
1 files changed, 534 insertions, 0 deletions
diff --git a/usr/src/lib/nsswitch/dns/common/gethostent6.c b/usr/src/lib/nsswitch/dns/common/gethostent6.c
new file mode 100644
index 0000000000..e92ce6a356
--- /dev/null
+++ b/usr/src/lib/nsswitch/dns/common/gethostent6.c
@@ -0,0 +1,534 @@
+/*
+ * 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.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1993-2000, 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * gethostent6.c
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This is the DNS backend for IPv6 addresses.
+ * getbyname() is a local routine, but getbyaddr() actually shares the
+ * same codes as the one in gethostent.c.
+ */
+
+#define endhostent res_endhostent
+
+#include <malloc.h>
+#include <stddef.h>
+#include <string.h>
+#include "dns_common.h"
+
+/*
+ * If the DNS name service switch routines are used in a binary that depends
+ * on an older libresolv (libresolv.so.1, say), then having nss_dns.so.1 or
+ * libnss_dns.a depend on a newer libresolv (libresolv.so.2) will cause
+ * relocation problems. In particular, copy relocation of the _res structure
+ * (which changes in size from libresolv.so.1 to libresolv.so.2) could
+ * cause corruption, and result in a number of strange problems, including
+ * core dumps. Hence, we check if a libresolv is already loaded.
+ */
+
+
+#pragma weak res_endhostent
+
+extern struct hostent *_gethostbyname(int *, const char *);
+extern struct hostent *_nss_dns_gethostbyname2(int *, const char *);
+
+typedef union {
+ long al;
+ char ac;
+} align;
+
+
+static void
+_endhostent(errp)
+ nss_status_t *errp;
+{
+ int ret;
+
+ ret = endhostent();
+ if (ret == 0)
+ *errp = NSS_SUCCESS;
+ else
+ *errp = NSS_UNAVAIL;
+}
+
+
+#ifdef RNDUP
+#undef RNDUP
+#endif
+#define RNDUP(x) ((1 + (((x)-1)/sizeof (void *))) * sizeof (void *))
+
+#ifdef PTROFF
+#undef PTROFF
+#endif
+#define PTROFF(p, o) (((o) == 0) ? 0 : (void *)((char *)(p) + (o)))
+
+
+/*
+ * Make a copy of h->h_name.
+ */
+static char *
+cloneName(struct hostent *h, int *outerr) {
+
+ char *name;
+ int len;
+ int error, *errp;
+
+ if (outerr)
+ errp = outerr;
+ else
+ errp = &error;
+
+ if (h == 0 || h->h_name == 0) {
+ *errp = 0;
+ return (0);
+ }
+
+ len = strlen(h->h_name);
+
+ if ((name = malloc(len+1)) == 0) {
+ *errp = 1;
+ return (0);
+ }
+
+ memcpy(name, h->h_name, len+1);
+
+ *errp = 0;
+ return (name);
+}
+
+
+/*
+ * Copy the h->h_addr_list[] array to a new array, and append the
+ * moreAddrs[] list. If h->h_addr_list[] contains IPv4 addresses,
+ * convert them to v4 mapped IPv6 addresses.
+ *
+ * Note: The pointers to the addresses in the moreAddrs[] array are copied,
+ * but not the IP addresses themselves.
+ */
+struct in6_addr **
+cloneAddrList(struct hostent *h, struct in6_addr **moreAddrs, int *outerr) {
+
+ struct in6_addr **addrArray, *addrList;
+ int domap, addrlen, i, j, addrCount, moreAddrCount = 0;
+
+ int error, *errp;
+
+ if (outerr)
+ errp = outerr;
+ else
+ errp = &error;
+
+ if (h == 0 || h->h_addr_list == 0) {
+ *errp = 0;
+ return (0);
+ }
+
+ /* Should we map v4 to IPv6 ? */
+ domap = (h->h_length == sizeof (struct in_addr)) &&
+ (h->h_addrtype == AF_INET);
+
+ /* If mapping, make sure we allocate enough memory for addresses */
+ addrlen = h->h_length;
+ if (domap && addrlen < sizeof (struct in6_addr))
+ addrlen = sizeof (struct in6_addr);
+
+ for (addrCount = 0; h->h_addr_list[addrCount]; addrCount++);
+
+ if (moreAddrs != 0) {
+ for (moreAddrCount = 0; moreAddrs[moreAddrCount];
+ moreAddrCount++);
+ }
+
+ if ((addrArray = malloc((addrCount+moreAddrCount+1)*sizeof (addrList) +
+ addrCount*addrlen)) == 0) {
+ *errp = 1;
+ return (0);
+ }
+
+ addrList = PTROFF(addrArray, (addrCount+moreAddrCount+1) *
+ sizeof (addrList));
+
+ for (i = 0; i < addrCount; i++) {
+ addrArray[i] = addrList;
+ if (domap) {
+ IN6_INADDR_TO_V4MAPPED(
+ (struct in_addr *)h->h_addr_list[i], addrArray[i]);
+ } else {
+ memcpy(addrArray[i], h->h_addr_list[i], addrlen);
+ }
+ addrList = PTROFF(addrList, addrlen);
+ }
+
+ for (j = 0; j < moreAddrCount; j++, i++) {
+ addrArray[i] = moreAddrs[j];
+ }
+
+ /* Last pointer should be NULL */
+ addrArray[i] = 0;
+
+ *errp = 0;
+ return (addrArray);
+}
+
+
+/*
+ * Create a new alias array that is is a copy of h->h_aliases[] plus
+ * the aliases in mergeAliases[] which aren't duplicates of any alias
+ * in h->h_aliases[].
+ *
+ * Note 1: Only the string pointers (NOT the strings) in the mergeAliases[]
+ * array are copied.
+ *
+ * Note 2: The duplicate aliases in mergeAliases[] are replaced by NULL
+ * pointers.
+ */
+static char **
+cloneAliasList(struct hostent *h, char **mergeAliases, int *outerr) {
+
+ char **aliasArray, *aliasList;
+ int i, j, k, aliasCount, mergeAliasCount = 0, realMac = 0;
+ int stringSize = 0;
+ int error, *errp;
+
+ if (outerr)
+ errp = outerr;
+ else
+ errp = &error;
+
+
+ if (h == 0 || h->h_aliases == 0) {
+ *errp = 0;
+ return (0);
+ }
+
+ for (aliasCount = 0; h->h_aliases[aliasCount]; aliasCount++) {
+ stringSize += RNDUP(strlen(h->h_aliases[aliasCount])+1);
+ }
+
+ if (mergeAliases != 0) {
+ for (; mergeAliases[mergeAliasCount]; mergeAliasCount++) {
+ int countThis = 1;
+ /* Skip duplicates */
+ for (j = 0; j < aliasCount; j++) {
+ if (strcmp(mergeAliases[mergeAliasCount],
+ h->h_aliases[j]) == 0) {
+ countThis = 0;
+ break;
+ }
+ }
+ if (countThis)
+ realMac++;
+ else
+ mergeAliases[mergeAliasCount] = 0;
+ }
+ }
+
+ if ((aliasArray = malloc((aliasCount+realMac+1)*sizeof (char **)+
+ stringSize)) == 0) {
+ *errp = 1;
+ return (0);
+ }
+
+ aliasList = PTROFF(aliasArray,
+ (aliasCount+realMac+1)*sizeof (char **));
+ for (i = 0; i < aliasCount; i++) {
+ int len = strlen(h->h_aliases[i]);
+ aliasArray[i] = aliasList;
+ memcpy(aliasArray[i], h->h_aliases[i], len+1);
+ aliasList = PTROFF(aliasList, RNDUP(len+1));
+ }
+
+ for (j = 0; j < mergeAliasCount; j++) {
+ if (mergeAliases[j] != 0) {
+ aliasArray[i++] = mergeAliases[j];
+ }
+ }
+
+ aliasArray[i] = 0;
+
+ *errp = 0;
+ return (aliasArray);
+}
+
+
+static nss_status_t
+getbyname(be, a)
+ dns_backend_ptr_t be;
+ void *a;
+{
+ struct hostent *he = NULL;
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
+ int ret, mt_disabled;
+ sigset_t oldmask;
+ int converr = 0, gotv6 = 0;
+ struct hostent v6he;
+ char *v6Name = 0;
+ struct in6_addr **v6Addrs = 0, **mergeAddrs = 0;
+ char **v6Aliases = 0, **mergeAliases = 0;
+ int v6_h_errno;
+ int old_retry;
+ int af = argp->key.ipnode.af_family;
+ int flags = argp->key.ipnode.flags;
+
+ switch_resolver_setup(&mt_disabled, &oldmask, &old_retry);
+
+ /* Now get the AAAA records */
+ if (af == AF_INET6)
+ he = _nss_dns_gethostbyname2(&argp->h_errno,
+ argp->key.ipnode.name);
+ if (he != NULL) {
+ /*
+ * pointer in "he" is part of a static pthread key in libresolv
+ * It should be treated as read only.
+ * So clone a copy first.
+ */
+ v6Name = cloneName(he, &converr);
+ if (converr) {
+ argp->h_errno = HOST_NOT_FOUND;
+ argp->erange = 1;
+ switch_resolver_reset(mt_disabled, oldmask, old_retry);
+ return (_herrno2nss(argp->h_errno));
+ }
+ v6Addrs = cloneAddrList(he, 0, &converr);
+ if (converr) {
+ if (v6Name != 0)
+ free(v6Name);
+ argp->h_errno = HOST_NOT_FOUND;
+ argp->erange = 1;
+ switch_resolver_reset(mt_disabled, oldmask, old_retry);
+ return (_herrno2nss(argp->h_errno));
+ }
+ v6Aliases = cloneAliasList(he, 0, &converr);
+ if (converr) {
+ if (v6Name != 0)
+ free(v6Name);
+ if (v6Addrs != 0)
+ free(v6Addrs);
+ argp->h_errno = HOST_NOT_FOUND;
+ argp->erange = 1;
+ switch_resolver_reset(mt_disabled, oldmask, old_retry);
+ return (_herrno2nss(argp->h_errno));
+ }
+ v6_h_errno = argp->h_errno;
+ gotv6 = 1;
+ }
+
+ /*
+ * The conditions to search "A" records:
+ * 1. af is AF_INET
+ * 2. if af is AF_INET6
+ * then flags are either
+ * 1) (AI_ALL | AI_V4MAPPED) or
+ * 2) AI_V4MAPPED and he == NULL
+ * (No V6 addresses found or no search for V6 at all)
+ */
+
+ /* Get the A records, and store the information */
+ if ((af == AF_INET) ||
+ ((af == AF_INET6) &&
+ ((flags & (AI_ALL | AI_V4MAPPED)) ||
+ ((flags & AI_V4MAPPED) && he == NULL))))
+ he = _gethostbyname(&argp->h_errno, argp->key.ipnode.name);
+ else
+ he = NULL;
+
+ /* Merge the results */
+ if (he != NULL) {
+ mergeAddrs = cloneAddrList(he, v6Addrs, &converr);
+ if (converr) {
+ if (v6Name != 0)
+ free(v6Name);
+ if (v6Addrs != 0)
+ free(v6Addrs);
+ if (v6Aliases != 0)
+ free(v6Aliases);
+ argp->h_errno = HOST_NOT_FOUND;
+ argp->erange = 1;
+ switch_resolver_reset(mt_disabled, oldmask,
+ old_retry);
+ return (_herrno2nss(argp->h_errno));
+ }
+ he->h_addr_list = (char **)mergeAddrs;
+
+ mergeAliases = cloneAliasList(he, v6Aliases, &converr);
+ if (converr) {
+ if (v6Name != 0)
+ free(v6Name);
+ if (v6Addrs != 0)
+ free(v6Addrs);
+ if (v6Aliases != 0)
+ free(v6Aliases);
+ if (mergeAddrs != 0)
+ free(mergeAddrs);
+ argp->h_errno = HOST_NOT_FOUND;
+ argp->erange = 1;
+ switch_resolver_reset(mt_disabled, oldmask,
+ old_retry);
+ return (_herrno2nss(argp->h_errno));
+ }
+ he->h_aliases = mergeAliases;
+
+ /* reset h_length, h_addrtype */
+ he->h_length = sizeof (struct in6_addr);
+ he->h_addrtype = AF_INET6;
+
+ } else if (gotv6) {
+ v6he.h_name = v6Name;
+ v6he.h_length = sizeof (struct in6_addr);
+ v6he.h_addrtype = AF_INET6;
+ v6he.h_addr_list = (char **)v6Addrs;
+ v6he.h_aliases = v6Aliases;
+ he = &v6he;
+ argp->h_errno = v6_h_errno;
+ }
+
+ if (he != 0) {
+ ret = ent2result(he, a, AF_INET6);
+ if (ret == NSS_STR_PARSE_SUCCESS) {
+ argp->returnval = argp->buf.result;
+ } else {
+ argp->h_errno = HOST_NOT_FOUND;
+ if (ret == NSS_STR_PARSE_ERANGE) {
+ argp->erange = 1;
+ }
+ }
+ }
+
+ if (v6Name != 0)
+ free(v6Name);
+ if (v6Addrs != 0)
+ free(v6Addrs);
+ if (v6Aliases != 0)
+ free(v6Aliases);
+ if (mergeAddrs != 0)
+ free(mergeAddrs);
+ if (mergeAliases != 0)
+ free(mergeAliases);
+
+ switch_resolver_reset(mt_disabled, oldmask, old_retry);
+
+ return (_herrno2nss(argp->h_errno));
+}
+
+
+extern nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *);
+
+static nss_status_t
+getbyaddr(be, a)
+ dns_backend_ptr_t be;
+ void *a;
+{
+ /* uses the same getbyaddr from IPv4 */
+ return (__nss_dns_getbyaddr(be, a));
+}
+
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_dns_getent(be, args)
+ dns_backend_ptr_t be;
+ void *args;
+{
+ return (NSS_UNAVAIL);
+}
+
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_dns_setent(be, dummy)
+ dns_backend_ptr_t be;
+ void *dummy;
+{
+ /* XXXX not implemented at this point */
+ return (NSS_UNAVAIL);
+}
+
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_dns_endent(be, dummy)
+ dns_backend_ptr_t be;
+ void *dummy;
+{
+ /* XXXX not implemented at this point */
+ return (NSS_UNAVAIL);
+}
+
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_dns_destr(be, dummy)
+ dns_backend_ptr_t be;
+ void *dummy;
+{
+ nss_status_t errp;
+
+ if (be != 0) {
+ /* === Should change to invoke ops[ENDENT] ? */
+ sigset_t oldmask, newmask;
+ int mt_disabled = 1;
+
+ if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) {
+ (void) sigfillset(&newmask);
+ _thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
+ _mutex_lock(&one_lane);
+ }
+
+ _endhostent(&errp);
+
+ if (mt_disabled) {
+ _mutex_unlock(&one_lane);
+ _thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
+ } else {
+ (void) (*disable_mt)();
+ }
+
+ free(be);
+ }
+ return (NSS_SUCCESS); /* In case anyone is dumb enough to check */
+}
+
+
+
+static dns_backend_op_t ipnodes_ops[] = {
+ _nss_dns_destr,
+ _nss_dns_endent,
+ _nss_dns_setent,
+ _nss_dns_getent,
+ getbyname,
+ getbyaddr,
+};
+
+/*ARGSUSED*/
+nss_backend_t *
+_nss_dns_ipnodes_constr(dummy1, dummy2, dummy3)
+ const char *dummy1, *dummy2, *dummy3;
+{
+ return (_nss_dns_constr(ipnodes_ops,
+ sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
+}