diff options
Diffstat (limited to 'usr/src/lib/nsswitch/dns/common/gethostent6.c')
| -rw-r--r-- | usr/src/lib/nsswitch/dns/common/gethostent6.c | 534 |
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]))); +} |
