10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52830Sdjl  * Common Development and Distribution License (the "License").
62830Sdjl  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*6666Ssm26363  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
232830Sdjl  * Use is subject to license terms.
242830Sdjl  */
252830Sdjl 
262830Sdjl #pragma ident	"%Z%%M%	%I%	%E% SMI"
272830Sdjl 
282830Sdjl /*
290Sstevel@tonic-gate  *	dns_common.c
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
322830Sdjl #include "dns_common.h"
330Sstevel@tonic-gate 
342830Sdjl #pragma weak	dn_expand
352830Sdjl #pragma weak	res_ninit
362830Sdjl #pragma weak	res_nsearch
372830Sdjl #pragma weak	res_nclose
382830Sdjl #pragma weak	ns_get16
392830Sdjl #pragma weak	ns_get32
402830Sdjl #pragma weak	__ns_get16
412830Sdjl #pragma weak	__ns_get32
420Sstevel@tonic-gate 
430Sstevel@tonic-gate #define	DNS_ALIASES	0
440Sstevel@tonic-gate #define	DNS_ADDRLIST	1
450Sstevel@tonic-gate #define	DNS_MAPDLIST	2
460Sstevel@tonic-gate 
47*6666Ssm26363 #ifndef	tolower
48*6666Ssm26363 #define	tolower(c) ((c) >= 'A' && (c) <= 'Z' ? (c) | 0x20 : (c))
49*6666Ssm26363 #endif
50*6666Ssm26363 
510Sstevel@tonic-gate static int
520Sstevel@tonic-gate dns_netdb_aliases(from_list, to_list, aliaspp, type, count, af_type)
530Sstevel@tonic-gate 	char	**from_list, **to_list,	**aliaspp;
540Sstevel@tonic-gate 	int	type, *count, af_type;
550Sstevel@tonic-gate {
560Sstevel@tonic-gate 	char		*fstr;
570Sstevel@tonic-gate 	int		cnt = 0;
580Sstevel@tonic-gate 	size_t		len;
590Sstevel@tonic-gate 
600Sstevel@tonic-gate 	*count = 0;
610Sstevel@tonic-gate 	if ((char *)to_list >= *aliaspp)
620Sstevel@tonic-gate 		return (NSS_STR_PARSE_ERANGE);
630Sstevel@tonic-gate 
640Sstevel@tonic-gate 	for (fstr = from_list[cnt]; fstr != NULL; fstr = from_list[cnt]) {
650Sstevel@tonic-gate 		if (type == DNS_ALIASES)
660Sstevel@tonic-gate 			len = strlen(fstr) + 1;
670Sstevel@tonic-gate 		else
680Sstevel@tonic-gate 			len = (af_type == AF_INET) ? sizeof (struct in_addr)
690Sstevel@tonic-gate 						: sizeof (struct in6_addr);
700Sstevel@tonic-gate 		*aliaspp -= len;
710Sstevel@tonic-gate 		to_list[cnt] = *aliaspp;
720Sstevel@tonic-gate 		if (*aliaspp <= (char *)&to_list[cnt+1])
730Sstevel@tonic-gate 			return (NSS_STR_PARSE_ERANGE);
740Sstevel@tonic-gate 		if (type == DNS_MAPDLIST) {
752830Sdjl 			/* LINTED: E_BAD_PTR_CAST_ALIGN */
762830Sdjl 			struct in6_addr *addr6p = (struct in6_addr *)*aliaspp;
770Sstevel@tonic-gate 
780Sstevel@tonic-gate 			(void) memset(addr6p, '\0', sizeof (struct in6_addr));
790Sstevel@tonic-gate 			(void) memcpy(&addr6p->s6_addr[12], fstr,
800Sstevel@tonic-gate 					sizeof (struct in_addr));
810Sstevel@tonic-gate 			addr6p->s6_addr[10] = 0xffU;
820Sstevel@tonic-gate 			addr6p->s6_addr[11] = 0xffU;
830Sstevel@tonic-gate 			++cnt;
840Sstevel@tonic-gate 		} else {
850Sstevel@tonic-gate 			(void) memcpy (*aliaspp, fstr, len);
860Sstevel@tonic-gate 			++cnt;
870Sstevel@tonic-gate 		}
880Sstevel@tonic-gate 	}
890Sstevel@tonic-gate 	to_list[cnt] = NULL;
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 	*count = cnt;
920Sstevel@tonic-gate 	if (cnt == 0)
930Sstevel@tonic-gate 		return (NSS_STR_PARSE_PARSE);
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 	return (NSS_STR_PARSE_SUCCESS);
960Sstevel@tonic-gate }
970Sstevel@tonic-gate 
980Sstevel@tonic-gate 
990Sstevel@tonic-gate int
1000Sstevel@tonic-gate ent2result(he, argp, af_type)
1010Sstevel@tonic-gate 	struct hostent		*he;
1020Sstevel@tonic-gate 	nss_XbyY_args_t		*argp;
1030Sstevel@tonic-gate 	int			af_type;
1040Sstevel@tonic-gate {
1050Sstevel@tonic-gate 	char		*buffer, *limit;
1060Sstevel@tonic-gate 	int		buflen = argp->buf.buflen;
1070Sstevel@tonic-gate 	int		ret, count;
1080Sstevel@tonic-gate 	size_t len;
1090Sstevel@tonic-gate 	struct hostent 	*host;
1100Sstevel@tonic-gate 	struct in_addr	*addrp;
1110Sstevel@tonic-gate 	struct in6_addr	*addrp6;
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 	limit = argp->buf.buffer + buflen;
1142830Sdjl 	host = (struct hostent *)argp->buf.result;
1150Sstevel@tonic-gate 	buffer = argp->buf.buffer;
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	/* h_addrtype and h_length */
1180Sstevel@tonic-gate 	host->h_addrtype = af_type;
1190Sstevel@tonic-gate 	host->h_length = (af_type == AF_INET) ? sizeof (struct in_addr)
1200Sstevel@tonic-gate 					: sizeof (struct in6_addr);
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 	/* h_name */
1230Sstevel@tonic-gate 	len = strlen(he->h_name) + 1;
1240Sstevel@tonic-gate 	host->h_name = buffer;
1250Sstevel@tonic-gate 	if (host->h_name + len >= limit)
1260Sstevel@tonic-gate 		return (NSS_STR_PARSE_ERANGE);
1270Sstevel@tonic-gate 	(void) memcpy(host->h_name, he->h_name, len);
1280Sstevel@tonic-gate 	buffer += len;
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate 	/* h_addr_list */
1310Sstevel@tonic-gate 	if (af_type == AF_INET) {
1322830Sdjl 		addrp = (struct in_addr *)ROUND_DOWN(limit, sizeof (*addrp));
1330Sstevel@tonic-gate 		host->h_addr_list = (char **)
1340Sstevel@tonic-gate 				ROUND_UP(buffer, sizeof (char **));
1350Sstevel@tonic-gate 		ret = dns_netdb_aliases(he->h_addr_list, host->h_addr_list,
1360Sstevel@tonic-gate 			(char **)&addrp, DNS_ADDRLIST, &count, af_type);
1370Sstevel@tonic-gate 		if (ret != NSS_STR_PARSE_SUCCESS)
1380Sstevel@tonic-gate 			return (ret);
1390Sstevel@tonic-gate 		/* h_aliases */
1400Sstevel@tonic-gate 		host->h_aliases = host->h_addr_list + count + 1;
1410Sstevel@tonic-gate 		ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
1420Sstevel@tonic-gate 			(char **)&addrp, DNS_ALIASES, &count, af_type);
1430Sstevel@tonic-gate 	} else {
1440Sstevel@tonic-gate 		addrp6 = (struct in6_addr *)
1450Sstevel@tonic-gate 			ROUND_DOWN(limit, sizeof (*addrp6));
1460Sstevel@tonic-gate 		host->h_addr_list = (char **)
1470Sstevel@tonic-gate 			ROUND_UP(buffer, sizeof (char **));
1480Sstevel@tonic-gate 		if (he->h_addrtype == AF_INET && af_type == AF_INET6) {
1490Sstevel@tonic-gate 			ret = dns_netdb_aliases(he->h_addr_list,
1500Sstevel@tonic-gate 				host->h_addr_list, (char **)&addrp6,
1510Sstevel@tonic-gate 				DNS_MAPDLIST, &count, af_type);
1520Sstevel@tonic-gate 		} else {
1530Sstevel@tonic-gate 			ret = dns_netdb_aliases(he->h_addr_list,
1540Sstevel@tonic-gate 				host->h_addr_list, (char **)&addrp6,
1550Sstevel@tonic-gate 				DNS_ADDRLIST, &count, af_type);
1560Sstevel@tonic-gate 		}
1570Sstevel@tonic-gate 		if (ret != NSS_STR_PARSE_SUCCESS)
1580Sstevel@tonic-gate 			return (ret);
1590Sstevel@tonic-gate 		/* h_aliases */
1600Sstevel@tonic-gate 		host->h_aliases = host->h_addr_list + count + 1;
1610Sstevel@tonic-gate 		ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
1620Sstevel@tonic-gate 			(char **)&addrp6, DNS_ALIASES, &count, af_type);
1630Sstevel@tonic-gate 	}
1640Sstevel@tonic-gate 	if (ret == NSS_STR_PARSE_PARSE)
1650Sstevel@tonic-gate 		ret = NSS_STR_PARSE_SUCCESS;
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	return (ret);
1680Sstevel@tonic-gate }
1690Sstevel@tonic-gate 
1702830Sdjl /*
1712830Sdjl  * Convert the hostent structure into string in the following
1722830Sdjl  * format:
1732830Sdjl  *
1742830Sdjl  * IP-address official-host-name nicknames ...
1752830Sdjl  *
1762830Sdjl  * If more than one IP-addresses matches the official-host-name,
1772830Sdjl  * the above line will be followed by:
1782830Sdjl  * IP-address-1 official-host-name
1792830Sdjl  * IP-address-2 official-host-name
1802830Sdjl  * ...
1812830Sdjl  *
1822830Sdjl  * This is so that the str2hostent function in libnsl
1832830Sdjl  * can convert the string back to the original hostent
1842830Sdjl  * data.
1852830Sdjl  */
1862830Sdjl int
1872830Sdjl ent2str(
1882830Sdjl 	struct hostent	*hp,
1892830Sdjl 	nss_XbyY_args_t *ap,
1902830Sdjl 	int		af_type)
1912830Sdjl {
1922830Sdjl 	char		**p;
1932830Sdjl 	char		obuf[INET6_ADDRSTRLEN];
1942830Sdjl 	void		*addr;
1952830Sdjl 	struct in_addr	in4;
1962830Sdjl 	int		af;
1972830Sdjl 	int		n;
1982830Sdjl 	const char	*res;
1992830Sdjl 	char		**q;
2002830Sdjl 	int		l = ap->buf.buflen;
2012830Sdjl 	char		*s = ap->buf.buffer;
2022830Sdjl 
2032830Sdjl 	/*
2042830Sdjl 	 * for "hosts" lookup, we only want address type of
2052830Sdjl 	 * AF_INET. For "ipnodes", we can have both AF_INET
2062830Sdjl 	 * and AF_INET6.
2072830Sdjl 	 */
2082830Sdjl 	if (af_type == AF_INET && hp->h_addrtype != AF_INET)
2092830Sdjl 		return (NSS_STR_PARSE_PARSE);
2102830Sdjl 
2112830Sdjl 	for (p = hp->h_addr_list; *p != 0; p++) {
2122830Sdjl 
2132830Sdjl 		if (p != hp->h_addr_list) {
2142830Sdjl 			*s = '\n';
2152830Sdjl 			s++;
2162830Sdjl 			l--;
2172830Sdjl 		}
2182830Sdjl 
2192830Sdjl 		if (hp->h_addrtype == AF_INET6) {
2202830Sdjl 			/* LINTED: E_BAD_PTR_CAST_ALIGN */
2212830Sdjl 			if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)*p)) {
2222830Sdjl 				/* LINTED: E_BAD_PTR_CAST_ALIGN */
2232830Sdjl 				IN6_V4MAPPED_TO_INADDR((struct in6_addr *)*p,
2245161Smichen 				    &in4);
2252830Sdjl 				af = AF_INET;
2262830Sdjl 				addr = &in4;
2272830Sdjl 			} else {
2282830Sdjl 				af = AF_INET6;
2292830Sdjl 				addr = *p;
2302830Sdjl 			}
2312830Sdjl 		} else {
2322830Sdjl 			af = AF_INET;
2332830Sdjl 			addr = *p;
2342830Sdjl 		}
2352830Sdjl 		res = inet_ntop(af, addr, obuf, sizeof (obuf));
2362830Sdjl 		if (res == NULL)
2372830Sdjl 			return (NSS_STR_PARSE_PARSE);
2382830Sdjl 
2395161Smichen 		if ((n = snprintf(s, l, "%s", res)) >= l)
2402830Sdjl 			return (NSS_STR_PARSE_ERANGE);
2412830Sdjl 		l -= n;
2422830Sdjl 		s += n;
2435161Smichen 		if (hp->h_name != NULL && *hp->h_name != '\0') {
2445161Smichen 			if ((n = snprintf(s, l, " %s", hp->h_name)) >= l)
2455161Smichen 				return (NSS_STR_PARSE_ERANGE);
2465161Smichen 			l -= n;
2475161Smichen 			s += n;
2485161Smichen 		}
2492830Sdjl 		if (p == hp->h_addr_list) {
2502830Sdjl 			for (q = hp->h_aliases; q && *q; q++) {
2512830Sdjl 				if ((n = snprintf(s, l, " %s", *q)) >= l)
2522830Sdjl 					return (NSS_STR_PARSE_ERANGE);
2532830Sdjl 				l -= n;
2542830Sdjl 				s += n;
2552830Sdjl 			}
2562830Sdjl 		}
2572830Sdjl 	}
2582830Sdjl 
2592830Sdjl 	ap->returnlen = s - ap->buf.buffer;
2602830Sdjl 	return (NSS_STR_PARSE_SUCCESS);
2612830Sdjl }
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate nss_backend_t *
2640Sstevel@tonic-gate _nss_dns_constr(dns_backend_op_t ops[], int n_ops)
2650Sstevel@tonic-gate {
2660Sstevel@tonic-gate 	dns_backend_ptr_t	be;
2670Sstevel@tonic-gate 
2682830Sdjl 	if ((be = (dns_backend_ptr_t)malloc(sizeof (*be))) == 0)
2690Sstevel@tonic-gate 		return (0);
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 	be->ops = ops;
2720Sstevel@tonic-gate 	be->n_ops = n_ops;
2732830Sdjl 	return ((nss_backend_t *)be);
2742830Sdjl }
2752830Sdjl 
2763176Smichen /*
2773176Smichen  * __res_ndestroy is a simplified version of the non-public function
2783176Smichen  * res_ndestroy in libresolv.so.2. Before res_ndestroy can be made
2793176Smichen  * public, __res_ndestroy will be used to make sure the memory pointed
2803176Smichen  * by statp->_u._ext.ext is freed after res_nclose() is called.
2813176Smichen  */
2823176Smichen static void
2833176Smichen __res_ndestroy(res_state statp) {
2843176Smichen 	res_nclose(statp);
2853176Smichen 	if (statp->_u._ext.ext != NULL)
2863176Smichen 		free(statp->_u._ext.ext);
2873176Smichen }
2882830Sdjl 
2892830Sdjl /*
290*6666Ssm26363  * name_is_alias(aliases_ptr, name_ptr)
291*6666Ssm26363  * Verify name matches an alias in the provided aliases list.
292*6666Ssm26363  *
293*6666Ssm26363  * Within DNS there should be only one canonical name, aliases should
294*6666Ssm26363  * all refer to the one canonical.  However alias chains do occur and
295*6666Ssm26363  * pre BIND 9 servers may also respond with multiple CNAMEs.  This
296*6666Ssm26363  * routine checks if a given name has been provided as a CNAME in the
297*6666Ssm26363  * response.  This assumes that the chains have been sent in-order.
298*6666Ssm26363  *
299*6666Ssm26363  * INPUT:
300*6666Ssm26363  *  aliases_ptr: space separated list of alias names.
301*6666Ssm26363  *  name_ptr: name to look for in aliases_ptr list.
302*6666Ssm26363  * RETURNS: NSS_SUCCESS or NSS_ERROR
303*6666Ssm26363  *  NSS_SUCCESS indicates that the name is listed in the collected aliases.
304*6666Ssm26363  */
305*6666Ssm26363 static nss_status_t
306*6666Ssm26363 name_is_alias(char *aliases_ptr, char *name_ptr) {
307*6666Ssm26363 	char *host_ptr;
308*6666Ssm26363 	/* Loop through alias string and compare it against host string. */
309*6666Ssm26363 	while (*aliases_ptr != '\0') {
310*6666Ssm26363 		host_ptr = name_ptr;
311*6666Ssm26363 
312*6666Ssm26363 		/* Compare name with alias. */
313*6666Ssm26363 		while (tolower(*host_ptr) == tolower(*aliases_ptr) &&
314*6666Ssm26363 		    *host_ptr != '\0') {
315*6666Ssm26363 			host_ptr++;
316*6666Ssm26363 			aliases_ptr++;
317*6666Ssm26363 		}
318*6666Ssm26363 
319*6666Ssm26363 		/*
320*6666Ssm26363 		 * If name was exhausted and the next character in the
321*6666Ssm26363 		 * alias is either the end-of-string or space
322*6666Ssm26363 		 * character then we have a match.
323*6666Ssm26363 		 */
324*6666Ssm26363 		if (*host_ptr == '\0' &&
325*6666Ssm26363 		    (*aliases_ptr == '\0' || *aliases_ptr == ' ')) {
326*6666Ssm26363 			return (NSS_SUCCESS);
327*6666Ssm26363 		}
328*6666Ssm26363 
329*6666Ssm26363 		/* Alias did not match, step over remainder of alias. */
330*6666Ssm26363 		while (*aliases_ptr != ' ' && *aliases_ptr != '\0')
331*6666Ssm26363 			aliases_ptr++;
332*6666Ssm26363 		/* Step over separator character. */
333*6666Ssm26363 		while (*aliases_ptr == ' ') aliases_ptr++;
334*6666Ssm26363 	}
335*6666Ssm26363 	return (NSS_ERROR);
336*6666Ssm26363 }
337*6666Ssm26363 
338*6666Ssm26363 /*
3392830Sdjl  * nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
3402830Sdjl  *      nss2 get hosts/ipnodes with ttl backend DNS search engine.
3412830Sdjl  *
3422830Sdjl  * This API is given a pointer to a packed buffer, and the buffer size
3432830Sdjl  * It's job is to perform the appropriate res_nsearch, extract the
3442830Sdjl  * results and build a unmarshalled hosts/ipnodes result buffer.
3452830Sdjl  * Additionally in the extended results a nssuint_t ttl is placed.
3462830Sdjl  * This ttl is the lessor of the ttl's extracted from the result.
3472830Sdjl  *
3482830Sdjl  * ***Currently the first version of this API only performs simple
3492830Sdjl  *    single res_nsearch lookups for with T_A or T_AAAA results.
3502830Sdjl  *    Other searches are deferred to the generic API w/t ttls.
3512830Sdjl  *
3522830Sdjl  *    This function is not a generic res_* operation.  It only performs
3532830Sdjl  *    a single T_A or T_AAAA lookups***
3542830Sdjl  *
3552830Sdjl  * RETURNS:  NSS_SUCCESS or NSS_ERROR
3562830Sdjl  *	If an NSS_ERROR result is returned, nscd is expected
3572830Sdjl  *	to resubmit the gethosts request using the old style
3582830Sdjl  *	nsswitch lookup format.
3592830Sdjl  */
3602830Sdjl 
3612830Sdjl nss_status_t
3622830Sdjl _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
3632830Sdjl {
3642830Sdjl 	/* nss buffer variables */
3652830Sdjl 	nss_pheader_t	*pbuf = (nss_pheader_t *)buffer;
3662830Sdjl 	nss_XbyY_args_t	arg;
3672830Sdjl 	char		*dbname;
3682830Sdjl 	int		dbop;
3692830Sdjl 	nss_status_t	sret;
3702830Sdjl 	size_t		bsize, blen;
3712830Sdjl 	char		*bptr;
3722830Sdjl 	/* resolver query variables */
3732924Smichen 	struct __res_state stat, *statp;	/* dns state block */
3742924Smichen 	union msg {
3752924Smichen 		uchar_t	buf[NS_MAXMSG];		/* max legal DNS answer size */
3762924Smichen 		HEADER	h;
3772924Smichen 	} resbuf;
3782924Smichen 	char aliases[NS_MAXMSG];		/* set of aliases */
3792830Sdjl 	const char	*name;
3802830Sdjl 	int		qtype;
3812830Sdjl 	/* answer parsing variables */
3822830Sdjl 	HEADER		*hp;
3832830Sdjl 	uchar_t		*cp;	/* current location in message */
3842830Sdjl 	uchar_t		*bom;	/* start of message */
3852830Sdjl 	uchar_t		*eom;	/* end of message */
3862830Sdjl 	uchar_t		*eor;	/* end of record */
3872830Sdjl 	int		ancount, qdcount;
3882830Sdjl 	int		type, class;
3892830Sdjl 	nssuint_t	nttl, ttl, *pttl;	/* The purpose of this API */
3902830Sdjl 	int		n, ret;
3912830Sdjl 	const char	*np;
3922830Sdjl 	/* temporary buffers */
3932830Sdjl 	char		nbuf[INET6_ADDRSTRLEN];	/* address parser */
3942830Sdjl 	char		host[MAXHOSTNAMELEN];	/* result host name */
3952830Sdjl 	char		ans[MAXHOSTNAMELEN];	/* record name */
3962830Sdjl 	char		aname[MAXHOSTNAMELEN];	/* alias result (C_NAME) */
3972830Sdjl 	/* misc variables */
3982830Sdjl 	int		af;
3992830Sdjl 	char		*ap, *apc;
4005161Smichen 	int		hlen = 0, alen, iplen, len;
4012830Sdjl 
4022924Smichen 	statp = &stat;
4032924Smichen 	(void) memset(statp, '\0', sizeof (struct __res_state));
4042924Smichen 	if (res_ninit(statp) == -1)
4052924Smichen 		return (NSS_ERROR);
4062924Smichen 
4072924Smichen 	ap = apc = (char *)aliases;
4082830Sdjl 	alen = 0;
4092830Sdjl 	ttl = (nssuint_t)0xFFFFFFF;		/* start w/max, find smaller */
4102830Sdjl 
4112830Sdjl 	/* save space for ttl otherwise, why bother... */
4122830Sdjl 	bsize = pbuf->data_len - sizeof (nssuint_t);
4132830Sdjl 	bptr = (char *)buffer + pbuf->data_off;
4142830Sdjl 	blen = 0;
4152830Sdjl 	sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg);
4162830Sdjl 	if (sret != NSS_SUCCESS) {
4173176Smichen 		__res_ndestroy(statp);
4182830Sdjl 		return (NSS_ERROR);
4192830Sdjl 	}
4202830Sdjl 
4212830Sdjl 	if (ipnode) {
4222830Sdjl 		/* initially only handle the simple cases */
4232924Smichen 		if (arg.key.ipnode.flags != 0) {
4243176Smichen 			__res_ndestroy(statp);
4252830Sdjl 			return (NSS_ERROR);
4262924Smichen 		}
4272830Sdjl 		name = arg.key.ipnode.name;
4282830Sdjl 		if (arg.key.ipnode.af_family == AF_INET6)
4292830Sdjl 			qtype = T_AAAA;
4302830Sdjl 		else
4312830Sdjl 			qtype = T_A;
4322830Sdjl 	} else {
4332830Sdjl 		name = arg.key.name;
4342830Sdjl 		qtype = T_A;
4352830Sdjl 	}
4362924Smichen 	ret = res_nsearch(statp, name, C_IN, qtype, resbuf.buf, NS_MAXMSG);
4372830Sdjl 	if (ret == -1) {
4382924Smichen 		if (statp->res_h_errno == HOST_NOT_FOUND) {
4392830Sdjl 			pbuf->p_herrno = HOST_NOT_FOUND;
4402830Sdjl 			pbuf->p_status = NSS_NOTFOUND;
4412830Sdjl 			pbuf->data_len = 0;
4423176Smichen 			__res_ndestroy(statp);
4432830Sdjl 			return (NSS_NOTFOUND);
4442830Sdjl 		}
4452830Sdjl 		/* else lookup error - handle in general code */
4463176Smichen 		__res_ndestroy(statp);
4472830Sdjl 		return (NSS_ERROR);
4482830Sdjl 	}
4492830Sdjl 
4502924Smichen 	cp = resbuf.buf;
4512924Smichen 	hp = (HEADER *)&resbuf.h;
4522830Sdjl 	bom = cp;
4532830Sdjl 	eom = cp + ret;
4542830Sdjl 
4552830Sdjl 	ancount = ntohs(hp->ancount);
4562830Sdjl 	qdcount = ntohs(hp->qdcount);
4572830Sdjl 	cp += HFIXEDSZ;
4582924Smichen 	if (qdcount != 1) {
4593176Smichen 		__res_ndestroy(statp);
4602830Sdjl 		return (NSS_ERROR);
4612924Smichen 	}
4622830Sdjl 	n = dn_expand(bom, eom, cp, host, MAXHOSTNAMELEN);
4632830Sdjl 	if (n < 0) {
4643176Smichen 		__res_ndestroy(statp);
4652830Sdjl 		return (NSS_ERROR);
4662830Sdjl 	} else
4672830Sdjl 		hlen = strlen(host);
4685161Smichen 	/* no host name is an error, return */
4695161Smichen 	if (hlen <= 0) {
4705161Smichen 		__res_ndestroy(statp);
4715161Smichen 		return (NSS_ERROR);
4725161Smichen 	}
4732830Sdjl 	cp += n + QFIXEDSZ;
4742924Smichen 	if (cp > eom) {
4753176Smichen 		__res_ndestroy(statp);
4762830Sdjl 		return (NSS_ERROR);
4772924Smichen 	}
4782830Sdjl 	while (ancount-- > 0 && cp < eom && blen < bsize) {
4792830Sdjl 		n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN);
4802830Sdjl 		if (n > 0) {
481*6666Ssm26363 			/*
482*6666Ssm26363 			 * Check that the expanded name is either the
483*6666Ssm26363 			 * name we asked for or a learned alias.
484*6666Ssm26363 			 */
485*6666Ssm26363 			if (strncasecmp(host, ans, hlen) != 0 && (alen == 0 ||
486*6666Ssm26363 			    name_is_alias(aliases, ans) == NSS_ERROR)) {
4873176Smichen 				__res_ndestroy(statp);
4882830Sdjl 				return (NSS_ERROR);	/* spoof? */
4892924Smichen 			}
4902830Sdjl 		}
4912830Sdjl 		cp += n;
4922830Sdjl 		/* bounds check */
4932830Sdjl 		type = ns_get16(cp);			/* type */
4942830Sdjl 		cp += INT16SZ;
4952830Sdjl 		class = ns_get16(cp);			/* class */
4962830Sdjl 		cp += INT16SZ;
4972830Sdjl 		nttl = (nssuint_t)ns_get32(cp);	/* ttl in sec */
4982830Sdjl 		if (nttl < ttl)
4992830Sdjl 			ttl = nttl;
5002830Sdjl 		cp += INT32SZ;
5012830Sdjl 		n = ns_get16(cp);			/* len */
5022830Sdjl 		cp += INT16SZ;
5032830Sdjl 		if (class != C_IN) {
5042830Sdjl 			cp += n;
5052830Sdjl 			continue;
5062830Sdjl 		}
5072830Sdjl 		eor = cp + n;
5082830Sdjl 		if (type == T_CNAME) {
509*6666Ssm26363 			/*
510*6666Ssm26363 			 * The name we looked up is really an alias
511*6666Ssm26363 			 * and the canonical name should be in the
512*6666Ssm26363 			 * RDATA.  A canonical name may have several
513*6666Ssm26363 			 * aliases but an alias should only have one
514*6666Ssm26363 			 * canonical name. However multiple CNAMEs and
515*6666Ssm26363 			 * CNAME chains do exist!  So for caching
516*6666Ssm26363 			 * purposes maintain the alias as the host
517*6666Ssm26363 			 * name, and the CNAME as an alias.
518*6666Ssm26363 			 */
5192830Sdjl 			n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN);
5202830Sdjl 			if (n > 0) {
5212830Sdjl 				len = strlen(aname);
5222830Sdjl 				if (len > 0) {
5232830Sdjl 					/*
5242830Sdjl 					 * Just error out if there is an
5252830Sdjl 					 * attempted buffer overflow exploit
5262830Sdjl 					 * generic code will do a syslog
5272830Sdjl 					 */
5283176Smichen 					if (alen + len + 2 > NS_MAXMSG) {
5293176Smichen 						__res_ndestroy(statp);
5302830Sdjl 						return (NSS_ERROR);
5313176Smichen 					}
5322830Sdjl 					*apc++ = ' ';
5332830Sdjl 					alen++;
5342830Sdjl 					(void) strlcpy(apc, aname, len + 1);
5352830Sdjl 					alen += len;
5362830Sdjl 					apc += len;
5372830Sdjl 				}
5382830Sdjl 			}
5392830Sdjl 			cp += n;
5402830Sdjl 			continue;
5412830Sdjl 		}
5422830Sdjl 		if (type != qtype) {
5432830Sdjl 			cp += n;
5442830Sdjl 			continue;
5452830Sdjl 		}
5462830Sdjl 		/* check data size */
5472830Sdjl 		if ((type == T_A && n != INADDRSZ) ||
5482830Sdjl 		    (type == T_AAAA && n != IN6ADDRSZ)) {
5492830Sdjl 			cp += n;
5502830Sdjl 			continue;
5512830Sdjl 		}
5522830Sdjl 		af = (type == T_A ? AF_INET : AF_INET6);
5532830Sdjl 		np = inet_ntop(af, (void *)cp, nbuf, INET6_ADDRSTRLEN);
5542924Smichen 		if (np == NULL) {
5553176Smichen 			__res_ndestroy(statp);
5562830Sdjl 			return (NSS_ERROR);
5572924Smichen 		}
5582830Sdjl 		cp += n;
5592830Sdjl 		/* append IP host aliases to results */
5602830Sdjl 		iplen = strlen(np);
5612830Sdjl 		/* ip <SP> hostname [<SP>][aliases] */
5622830Sdjl 		len = iplen + 2 + hlen + alen;
5632830Sdjl 		if (alen > 0)
5642830Sdjl 			len++;
5653176Smichen 		if (blen + len > bsize) {
5663176Smichen 			__res_ndestroy(statp);
5672830Sdjl 			return (NSS_ERROR);
5683176Smichen 		}
5692830Sdjl 		(void) strlcpy(bptr, np, bsize - blen);
5702830Sdjl 		blen += iplen;
5712830Sdjl 		bptr += iplen;
5722830Sdjl 		*bptr++ = ' ';
5732830Sdjl 		blen++;
5742830Sdjl 		(void) strlcpy(bptr, host, bsize - blen);
5752830Sdjl 		blen += hlen;
5762830Sdjl 		bptr += hlen;
5772830Sdjl 		if (alen > 0) {
5782830Sdjl 			*bptr++ = ' ';
5792830Sdjl 			blen++;
5802830Sdjl 			(void) strlcpy(bptr, ap, bsize - blen);
5812830Sdjl 			blen += alen;
5822830Sdjl 			bptr += alen;
5832830Sdjl 		}
5842830Sdjl 		*bptr++ = '\n';
5852830Sdjl 		blen++;
5862830Sdjl 	}
5872830Sdjl 	/* Presumably the buffer is now filled. */
5882830Sdjl 	len = ROUND_UP(blen, sizeof (nssuint_t));
5892830Sdjl 	/* still room? */
5902830Sdjl 	if (len + sizeof (nssuint_t) > pbuf->data_len) {
5912830Sdjl 		/* sigh, no, what happened? */
5923176Smichen 		__res_ndestroy(statp);
5932830Sdjl 		return (NSS_ERROR);
5942830Sdjl 	}
5952830Sdjl 	pbuf->ext_off = pbuf->data_off + len;
5962830Sdjl 	pbuf->ext_len = sizeof (nssuint_t);
5972830Sdjl 	pbuf->data_len = blen;
5982830Sdjl 	pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off));
5992830Sdjl 	*pttl = ttl;
6003176Smichen 	__res_ndestroy(statp);
6012830Sdjl 	return (NSS_SUCCESS);
6022830Sdjl }
603