/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * 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. * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #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 #include #include #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); } (void) 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. */ static 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) { /* LINTED: E_BAD_PTR_CAST_ALIGN */ IN6_INADDR_TO_V4MAPPED( (struct in_addr *)h->h_addr_list[i], addrArray[i]); } else { (void) 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, 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; (void) 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); } /*ARGSUSED*/ 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; struct hostent mhe; 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) { mhe = *he; 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)); } mhe.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)); } mhe.h_aliases = mergeAliases; /* reset h_length, h_addrtype */ mhe.h_length = sizeof (struct in6_addr); mhe.h_addrtype = AF_INET6; he = &mhe; } 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 != NULL) { /* * if asked to return data in string, * convert the hostent structure into * string data */ if (argp->buf.result == NULL) { ret = ent2str(he, a, AF_INET6); if (ret == NSS_STR_PARSE_SUCCESS) argp->returnval = argp->buf.buffer; } else { ret = ent2result(he, a, AF_INET6); if (ret == NSS_STR_PARSE_SUCCESS) argp->returnval = argp->buf.result; } if (ret != NSS_STR_PARSE_SUCCESS) { 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); (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); (void) mutex_lock(&one_lane); } _endhostent(&errp); if (mt_disabled) { (void) mutex_unlock(&one_lane); (void) 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]))); } /* * optional NSS2 packed backend gethostsbyipnode with ttl * entry point. * * Returns: * NSS_SUCCESS - successful * NSS_NOTFOUND - successful but nothing found * NSS_ERROR - fallback to NSS backend lookup mode * If successful, buffer will be filled with valid data * */ /*ARGSUSED*/ nss_status_t _nss_get_dns_ipnodes_name(dns_backend_ptr_t *be, void **bufp, size_t *sizep) { return (_nss_dns_gethost_withttl(*bufp, *sizep, 1)); }