xref: /illumos-gate/usr/src/lib/nsswitch/dns/common/dns_common.c (revision 778e45517720fb0705da061ad104fd0c9e8f5605)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*
26  * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
27  * Copyright 2023 Oxide Computer Company
28  */
29 
30 /*
31  *	dns_common.c
32  */
33 
34 #include "dns_common.h"
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <ifaddrs.h>
38 #include <net/if.h>
39 
40 #pragma weak	dn_expand
41 #pragma weak	res_ninit
42 #pragma weak	res_ndestroy
43 #pragma weak	res_nsearch
44 #pragma weak	res_nclose
45 #pragma weak	ns_get16
46 #pragma weak	ns_get32
47 #pragma weak	__ns_get16
48 #pragma weak	__ns_get32
49 
50 #define	DNS_ALIASES	0
51 #define	DNS_ADDRLIST	1
52 #define	DNS_MAPDLIST	2
53 
54 #ifndef	tolower
55 #define	tolower(c) ((c) >= 'A' && (c) <= 'Z' ? (c) | 0x20 : (c))
56 #endif
57 
58 static int
dns_netdb_aliases(char ** from_list,char ** to_list,char ** aliaspp,int type,int * count,int af_type)59 dns_netdb_aliases(char **from_list, char **to_list, char **aliaspp, int type,
60     int *count, int af_type)
61 {
62 	char		*fstr;
63 	int		cnt = 0;
64 	size_t		len;
65 
66 	*count = 0;
67 	if ((char *)to_list >= *aliaspp)
68 		return (NSS_STR_PARSE_ERANGE);
69 
70 	for (fstr = from_list[cnt]; fstr != NULL; fstr = from_list[cnt]) {
71 		if (type == DNS_ALIASES) {
72 			len = strlen(fstr) + 1;
73 		} else {
74 			len = (af_type == AF_INET) ? sizeof (struct in_addr) :
75 			    sizeof (struct in6_addr);
76 		}
77 		*aliaspp -= len;
78 		to_list[cnt] = *aliaspp;
79 		if (*aliaspp <= (char *)&to_list[cnt+1])
80 			return (NSS_STR_PARSE_ERANGE);
81 		if (type == DNS_MAPDLIST) {
82 			/* LINTED: E_BAD_PTR_CAST_ALIGN */
83 			struct in6_addr *addr6p = (struct in6_addr *)*aliaspp;
84 
85 			(void) memset(addr6p, '\0', sizeof (struct in6_addr));
86 			(void) memcpy(&addr6p->s6_addr[12], fstr,
87 			    sizeof (struct in_addr));
88 			addr6p->s6_addr[10] = 0xffU;
89 			addr6p->s6_addr[11] = 0xffU;
90 			++cnt;
91 		} else {
92 			(void) memcpy (*aliaspp, fstr, len);
93 			++cnt;
94 		}
95 	}
96 	to_list[cnt] = NULL;
97 
98 	*count = cnt;
99 	if (cnt == 0)
100 		return (NSS_STR_PARSE_PARSE);
101 
102 	return (NSS_STR_PARSE_SUCCESS);
103 }
104 
105 
106 int
ent2result(struct hostent * he,nss_XbyY_args_t * argp,int af_type)107 ent2result(struct hostent *he, nss_XbyY_args_t *argp, int af_type)
108 {
109 	char		*buffer, *limit;
110 	int		buflen = argp->buf.buflen;
111 	int		ret, count;
112 	size_t len;
113 	struct hostent	*host;
114 	struct in_addr	*addrp;
115 	struct in6_addr	*addrp6;
116 
117 	limit = argp->buf.buffer + buflen;
118 	host = (struct hostent *)argp->buf.result;
119 	buffer = argp->buf.buffer;
120 
121 	/* h_addrtype and h_length */
122 	host->h_addrtype = af_type;
123 	host->h_length = (af_type == AF_INET) ? sizeof (struct in_addr) :
124 	    sizeof (struct in6_addr);
125 
126 	/* h_name */
127 	len = strlen(he->h_name) + 1;
128 	host->h_name = buffer;
129 	if (host->h_name + len >= limit)
130 		return (NSS_STR_PARSE_ERANGE);
131 	(void) memcpy(host->h_name, he->h_name, len);
132 	buffer += len;
133 
134 	/* h_addr_list */
135 	if (af_type == AF_INET) {
136 		addrp = (struct in_addr *)ROUND_DOWN(limit, sizeof (*addrp));
137 		host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **));
138 		ret = dns_netdb_aliases(he->h_addr_list, host->h_addr_list,
139 		    (char **)&addrp, DNS_ADDRLIST, &count, af_type);
140 		if (ret != NSS_STR_PARSE_SUCCESS)
141 			return (ret);
142 		/* h_aliases */
143 		host->h_aliases = host->h_addr_list + count + 1;
144 		ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
145 		    (char **)&addrp, DNS_ALIASES, &count, af_type);
146 	} else {
147 		addrp6 = (struct in6_addr *)ROUND_DOWN(limit, sizeof (*addrp6));
148 		host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **));
149 		if (he->h_addrtype == AF_INET && af_type == AF_INET6) {
150 			ret = dns_netdb_aliases(he->h_addr_list,
151 			    host->h_addr_list, (char **)&addrp6,
152 			    DNS_MAPDLIST, &count, af_type);
153 		} else {
154 			ret = dns_netdb_aliases(he->h_addr_list,
155 			    host->h_addr_list, (char **)&addrp6,
156 			    DNS_ADDRLIST, &count, af_type);
157 		}
158 		if (ret != NSS_STR_PARSE_SUCCESS)
159 			return (ret);
160 		/* h_aliases */
161 		host->h_aliases = host->h_addr_list + count + 1;
162 		ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
163 		    (char **)&addrp6, DNS_ALIASES, &count, af_type);
164 	}
165 	if (ret == NSS_STR_PARSE_PARSE)
166 		ret = NSS_STR_PARSE_SUCCESS;
167 
168 	return (ret);
169 }
170 
171 /*
172  * Convert the hostent structure into string in the following
173  * format:
174  *
175  * IP-address official-host-name nicknames ...
176  *
177  * If more than one IP-addresses matches the official-host-name,
178  * the above line will be followed by:
179  * IP-address-1 official-host-name
180  * IP-address-2 official-host-name
181  * ...
182  *
183  * This is so that the str2hostent function in libnsl
184  * can convert the string back to the original hostent
185  * data.
186  */
187 int
ent2str(struct hostent * hp,nss_XbyY_args_t * ap,int af_type)188 ent2str(struct hostent	*hp, nss_XbyY_args_t *ap, int af_type)
189 {
190 	char		**p;
191 	char		obuf[INET6_ADDRSTRLEN];
192 	void		*addr;
193 	struct in_addr	in4;
194 	int		af;
195 	int		n;
196 	const char	*res;
197 	char		**q;
198 	int		l = ap->buf.buflen;
199 	char		*s = ap->buf.buffer;
200 
201 	/*
202 	 * for "hosts" lookup, we only want address type of
203 	 * AF_INET. For "ipnodes", we can have both AF_INET
204 	 * and AF_INET6.
205 	 */
206 	if (af_type == AF_INET && hp->h_addrtype != AF_INET)
207 		return (NSS_STR_PARSE_PARSE);
208 
209 	for (p = hp->h_addr_list; *p != 0; p++) {
210 
211 		if (p != hp->h_addr_list) {
212 			*s = '\n';
213 			s++;
214 			l--;
215 		}
216 
217 		if (hp->h_addrtype == AF_INET6) {
218 			/* LINTED: E_BAD_PTR_CAST_ALIGN */
219 			if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)*p)) {
220 				/* LINTED: E_BAD_PTR_CAST_ALIGN */
221 				IN6_V4MAPPED_TO_INADDR((struct in6_addr *)*p,
222 				    &in4);
223 				af = AF_INET;
224 				addr = &in4;
225 			} else {
226 				af = AF_INET6;
227 				addr = *p;
228 			}
229 		} else {
230 			af = AF_INET;
231 			addr = *p;
232 		}
233 		res = inet_ntop(af, addr, obuf, sizeof (obuf));
234 		if (res == NULL)
235 			return (NSS_STR_PARSE_PARSE);
236 
237 		if ((n = snprintf(s, l, "%s", res)) >= l)
238 			return (NSS_STR_PARSE_ERANGE);
239 		l -= n;
240 		s += n;
241 		if (hp->h_name != NULL && *hp->h_name != '\0') {
242 			if ((n = snprintf(s, l, " %s", hp->h_name)) >= l)
243 				return (NSS_STR_PARSE_ERANGE);
244 			l -= n;
245 			s += n;
246 		}
247 		if (p == hp->h_addr_list) {
248 			for (q = hp->h_aliases; q && *q; q++) {
249 				if ((n = snprintf(s, l, " %s", *q)) >= l)
250 					return (NSS_STR_PARSE_ERANGE);
251 				l -= n;
252 				s += n;
253 			}
254 		}
255 	}
256 
257 	ap->returnlen = s - ap->buf.buffer;
258 	return (NSS_STR_PARSE_SUCCESS);
259 }
260 
261 nss_backend_t *
_nss_dns_constr(dns_backend_op_t ops[],int n_ops)262 _nss_dns_constr(dns_backend_op_t ops[], int n_ops)
263 {
264 	dns_backend_ptr_t	be;
265 
266 	if ((be = (dns_backend_ptr_t)malloc(sizeof (*be))) == 0)
267 		return (0);
268 
269 	be->ops = ops;
270 	be->n_ops = n_ops;
271 	return ((nss_backend_t *)be);
272 }
273 
274 /*
275  * name_is_alias(aliases_ptr, name_ptr)
276  * Verify name matches an alias in the provided aliases list.
277  *
278  * Within DNS there should be only one canonical name, aliases should
279  * all refer to the one canonical.  However alias chains do occur and
280  * pre BIND 9 servers may also respond with multiple CNAMEs.  This
281  * routine checks if a given name has been provided as a CNAME in the
282  * response.  This assumes that the chains have been sent in-order.
283  *
284  * INPUT:
285  *  aliases_ptr: space separated list of alias names.
286  *  name_ptr: name to look for in aliases_ptr list.
287  * RETURNS: NSS_SUCCESS or NSS_NOTFOUND
288  *  NSS_SUCCESS indicates that the name is listed in the collected aliases.
289  */
290 static nss_status_t
name_is_alias(char * aliases_ptr,char * name_ptr)291 name_is_alias(char *aliases_ptr, char *name_ptr)
292 {
293 	char *host_ptr;
294 	/* Loop through alias string and compare it against host string. */
295 	while (*aliases_ptr != '\0') {
296 		host_ptr = name_ptr;
297 
298 		/* Compare name with alias. */
299 		while (tolower(*host_ptr) == tolower(*aliases_ptr) &&
300 		    *host_ptr != '\0') {
301 			host_ptr++;
302 			aliases_ptr++;
303 		}
304 
305 		/*
306 		 * If name was exhausted and the next character in the
307 		 * alias is either the end-of-string or space
308 		 * character then we have a match.
309 		 */
310 		if (*host_ptr == '\0' &&
311 		    (*aliases_ptr == '\0' || *aliases_ptr == ' ')) {
312 			return (NSS_SUCCESS);
313 		}
314 
315 		/* Alias did not match, step over remainder of alias. */
316 		while (*aliases_ptr != ' ' && *aliases_ptr != '\0')
317 			aliases_ptr++;
318 		/* Step over separator character. */
319 		while (*aliases_ptr == ' ') aliases_ptr++;
320 	}
321 	return (NSS_NOTFOUND);
322 }
323 
324 static int
_nss_has_interfaces(boolean_t * v4,boolean_t * v6)325 _nss_has_interfaces(boolean_t *v4, boolean_t *v6)
326 {
327 	struct ifaddrs *ifp, *i;
328 	struct in_addr in4;
329 	struct in6_addr in6;
330 	const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
331 
332 	*v4 = *v6 = B_FALSE;
333 
334 	if (getifaddrs(&ifp) != 0)
335 		return (-1);
336 
337 	for (i = ifp; i != NULL; i = i->ifa_next) {
338 		if (i->ifa_flags & IFF_LOOPBACK)
339 			continue;
340 		if ((i->ifa_flags & IFF_UP) == 0)
341 			continue;
342 
343 		if (i->ifa_addr->sa_family == AF_INET) {
344 			if (*v4 != B_FALSE)
345 				continue;
346 
347 			if (((struct sockaddr_in *)i->ifa_addr)->
348 			    sin_addr.s_addr == INADDR_ANY)
349 				continue;
350 			*v4 = B_TRUE;
351 		}
352 
353 		if (i->ifa_addr->sa_family == AF_INET6) {
354 			if (*v6 != B_FALSE)
355 				continue;
356 
357 			if (memcmp(&in6addr_any,
358 			    &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr,
359 			    sizeof (struct in6_addr)) == 0)
360 				continue;
361 			*v6 = B_TRUE;
362 		}
363 	}
364 
365 	freeifaddrs(ifp);
366 	return (0);
367 }
368 
369 /*
370  * nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
371  *      nss2 get hosts/ipnodes with ttl backend DNS search engine.
372  *
373  * This API is given a pointer to a packed buffer, and the buffer size
374  * It's job is to perform the appropriate res_nsearch, extract the
375  * results and build a unmarshalled hosts/ipnodes result buffer.
376  * Additionally in the extended results a nssuint_t ttl is placed.
377  * This ttl is the lessor of the ttl's extracted from the result.
378  *
379  * RETURNS:  NSS_SUCCESS or NSS_ERROR
380  *	If an NSS_ERROR result is returned, nscd is expected
381  *	to resubmit the gethosts request using the old style
382  *	nsswitch lookup format.
383  */
384 
385 nss_status_t
_nss_dns_gethost_withttl(void * buffer,size_t bufsize,int ipnode)386 _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
387 {
388 	/* nss buffer variables */
389 	nss_pheader_t	*pbuf = (nss_pheader_t *)buffer;
390 	nss_XbyY_args_t	arg;
391 	char		*dbname;
392 	int		dbop;
393 	nss_status_t	sret;
394 	size_t		bsize, blen;
395 	char		*bptr;
396 	/* resolver query variables */
397 	struct __res_state stat, *statp;	/* dns state block */
398 	union msg {
399 		uchar_t	buf[NS_MAXMSG];		/* max legal DNS answer size */
400 		HEADER	h;
401 	} resbuf;
402 	char aliases[NS_MAXMSG];		/* set of aliases */
403 	const char	*name;
404 	int		qtype;
405 	/* answer parsing variables */
406 	HEADER		*hp;
407 	uchar_t		*cp;	/* current location in message */
408 	uchar_t		*bom;	/* start of message */
409 	uchar_t		*eom;	/* end of message */
410 	uchar_t		*eor;	/* end of record */
411 	int		ancount, qdcount;
412 	int		type, class;
413 	nssuint_t	nttl, ttl, *pttl;	/* The purpose of this API */
414 	int		n, ret;
415 	const char	*np;
416 	/* temporary buffers */
417 	char		nbuf[INET6_ADDRSTRLEN];	/* address parser */
418 	char		host[MAXHOSTNAMELEN];	/* result host name */
419 	char		ans[MAXHOSTNAMELEN];	/* record name */
420 	char		aname[MAXHOSTNAMELEN];	/* alias result (C_NAME) */
421 	/* misc variables */
422 	int		af;
423 	char		*ap, *apc;
424 	int		hlen = 0, alen, iplen, len, isans;
425 	boolean_t	has_v4 = B_FALSE, has_v6 = B_FALSE;
426 	int		flags, family, pass2 = 0;
427 
428 	statp = &stat;
429 	(void) memset(statp, '\0', sizeof (struct __res_state));
430 	if (res_ninit(statp) == -1) {
431 		return (NSS_ERROR);
432 	}
433 
434 	ap = apc = (char *)aliases;
435 	alen = 0;
436 	ttl = (nssuint_t)0xFFFFFFF;		/* start w/max, find smaller */
437 
438 	/* save space for ttl otherwise, why bother... */
439 	bsize = pbuf->data_len - sizeof (nssuint_t);
440 	bptr = (char *)buffer + pbuf->data_off;
441 	blen = 0;
442 	sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg);
443 	if (sret != NSS_SUCCESS) {
444 		res_ndestroy(statp);
445 		return (NSS_ERROR);
446 	}
447 
448 	/*
449 	 * There may be flags set when we are handling ipnode. There are three
450 	 * different values for flags:
451 	 *
452 	 *  o AI_V4MAPPED
453 	 *  o AI_ALL
454 	 *  o AI_ADDRCONFIG
455 	 *
456 	 * The first two only have a meaning when af_family is ipv6. The latter
457 	 * means something in both cases. These flags are documented in
458 	 * getipnodebyname(3SOCKET), though the combinations leave a little
459 	 * something to be desired. It would be great if we could actually use
460 	 * getipnodebyname directly here since it already knows how to handle
461 	 * this kind of logic; however, we're not quite so lucky. Ideally we
462 	 * would add such an interface to libresolv.so.2 to handle this kind of
463 	 * thing, but that's rather painful as well. We'll summarize what has to
464 	 * happen below:
465 	 *
466 	 * AI_ALL is only meaningful when AI_V4MAPPED is also specified. Both
467 	 * are ignored if the family is not AF_INET6
468 	 *
469 	 * family == AF_INET, flags | AI_ADDRCONFIG
470 	 *  - lookup A records iff we have v4 plumbed
471 	 * family == AF_INET, !(flags | AI_ADDRCONFIG)
472 	 *  - lookup A records
473 	 * family == AF_INET6, flags == 0 || flags == AI_ALL
474 	 *  - lookup AAAA records
475 	 * family == AF_INET6, flags | AI_V4MAPPED
476 	 *  - lookup AAAA, if none, lookup A
477 	 * family == AF_INET6, flags | AI_ADDRCONFIG
478 	 *  - lookup AAAA records if ipv6
479 	 * family == AF_INET6, flags | AI_V4MAPPED && flags | AI_ALL
480 	 *  - lookup AAAA records, lookup A records
481 	 * family == AF_INET6, flags | AI_V4MAPPED && flags | AI_ADDRCONFIG
482 	 *  - lookup AAAA records if ipv6
483 	 *  - If no AAAA && ipv4 exists, lookup A
484 	 * family == AF_INET6, flags | AI_V4MAPPED && flags | AI_ADDRCONFIG &&
485 	 * flags | AI_ALL
486 	 *  - lookup AAAA records if ipv6
487 	 *  - loookup A records if ipv4
488 	 */
489 	if (ipnode) {
490 		/* initially only handle the simple cases */
491 		name = arg.key.ipnode.name;
492 		flags = arg.key.ipnode.flags;
493 		family = arg.key.ipnode.af_family;
494 		if (flags != 0) {
495 			/*
496 			 * Figure out our first pass. We'll determine if we need
497 			 * to do a second pass afterwards once we successfully
498 			 * finish our first pass.
499 			 */
500 			if ((flags & AI_ADDRCONFIG) != 0) {
501 				if (_nss_has_interfaces(&has_v4, &has_v6) !=
502 				    0) {
503 					res_ndestroy(statp);
504 					return (NSS_ERROR);
505 				}
506 				/* Impossible situations... */
507 				if (family == AF_INET && has_v4 == B_FALSE) {
508 					res_ndestroy(statp);
509 					return (NSS_NOTFOUND);
510 				}
511 				if (family == AF_INET6 && has_v6 == B_FALSE &&
512 				    !(flags & AI_V4MAPPED)) {
513 					res_ndestroy(statp);
514 					return (NSS_NOTFOUND);
515 				}
516 				if (family == AF_INET6 && has_v6)
517 					qtype = T_AAAA;
518 				if (family == AF_INET || (family == AF_INET6 &&
519 				    has_v6 == B_FALSE && flags & AI_V4MAPPED))
520 					qtype = T_A;
521 			} else {
522 				has_v4 = has_v6 = B_TRUE;
523 				if (family == AF_INET6)
524 					qtype = T_AAAA;
525 				else
526 					qtype = T_A;
527 			}
528 		} else {
529 			if (family == AF_INET6)
530 				qtype = T_AAAA;
531 			else
532 				qtype = T_A;
533 		}
534 	} else {
535 		name = arg.key.name;
536 		qtype = T_A;
537 	}
538 
539 searchagain:
540 	ret = res_nsearch(statp, name, C_IN, qtype, resbuf.buf, NS_MAXMSG);
541 	if (ret == -1) {
542 		/*
543 		 * We want to continue on unless we got NO_RECOVERY. Otherwise,
544 		 * HOST_NOT_FOUND, TRY_AGAIN, and NO_DATA all suggest to me that
545 		 * we should keep going.
546 		 */
547 		if (statp->res_h_errno == NO_RECOVERY) {
548 			/* else lookup error - handle in general code */
549 			res_ndestroy(statp);
550 			return (NSS_ERROR);
551 		}
552 
553 		/*
554 		 * We found something on our first pass. Make sure that we do
555 		 * not clobber this information. This ultimately means that we
556 		 * were successful.
557 		 */
558 		if (pass2 == 2)
559 			goto out;
560 
561 		/*
562 		 * If we're on the second pass (eg. we need to check both for A
563 		 * and AAAA records), or we were only ever doing a search for
564 		 * one type of record and are not supposed to do a second pass,
565 		 * then we need to return that we couldn't find anything to the
566 		 * user.
567 		 */
568 		if (pass2 == 1 || flags == 0 || family == AF_INET ||
569 		    (family == AF_INET6 && !(flags & AI_V4MAPPED))) {
570 			pbuf->p_herrno = HOST_NOT_FOUND;
571 			pbuf->p_status = NSS_NOTFOUND;
572 			pbuf->data_len = 0;
573 			res_ndestroy(statp);
574 			return (NSS_NOTFOUND);
575 		}
576 
577 		/*
578 		 * If we were only requested to search for flags on an IPv6
579 		 * interface or we have no IPv4 interface, we stick to only
580 		 * doing a single pass and bail now.
581 		 */
582 		if ((flags & AI_ADDRCONFIG) && !(flags & AI_ALL) &&
583 		    has_v4 == B_FALSE) {
584 			pbuf->p_herrno = HOST_NOT_FOUND;
585 			pbuf->p_status = NSS_NOTFOUND;
586 			pbuf->data_len = 0;
587 			res_ndestroy(statp);
588 			return (NSS_NOTFOUND);
589 		}
590 		qtype = T_A;
591 		flags = 0;
592 		pass2 = 1;
593 		goto searchagain;
594 	}
595 
596 	cp = resbuf.buf;
597 	hp = (HEADER *)&resbuf.h;
598 	bom = cp;
599 	eom = cp + ret;
600 
601 	ancount = ntohs(hp->ancount);
602 	qdcount = ntohs(hp->qdcount);
603 	cp += HFIXEDSZ;
604 	if (qdcount != 1) {
605 		res_ndestroy(statp);
606 		return (NSS_ERROR);
607 	}
608 	n = dn_expand(bom, eom, cp, host, MAXHOSTNAMELEN);
609 	if (n < 0) {
610 		res_ndestroy(statp);
611 		return (NSS_ERROR);
612 	} else
613 		hlen = strlen(host);
614 	/* no host name is an error, return */
615 	if (hlen <= 0) {
616 		res_ndestroy(statp);
617 		return (NSS_ERROR);
618 	}
619 	cp += n + QFIXEDSZ;
620 	if (cp > eom) {
621 		res_ndestroy(statp);
622 		return (NSS_ERROR);
623 	}
624 	while (ancount-- > 0 && cp < eom && blen < bsize) {
625 		n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN);
626 		if (n > 0) {
627 			/*
628 			 * Check that the expanded name is either the
629 			 * name we asked for or a learned alias.
630 			 */
631 			if ((isans = strncasecmp(host, ans, hlen)) != 0 &&
632 			    (alen == 0 || name_is_alias(aliases, ans)
633 			    == NSS_NOTFOUND)) {
634 				res_ndestroy(statp);
635 				return (NSS_ERROR);	/* spoof? */
636 			}
637 		}
638 		cp += n;
639 		/* bounds check */
640 		type = ns_get16(cp);			/* type */
641 		cp += INT16SZ;
642 		class = ns_get16(cp);			/* class */
643 		cp += INT16SZ;
644 		nttl = (nssuint_t)ns_get32(cp);		/* ttl in sec */
645 		if (nttl < ttl)
646 			ttl = nttl;
647 		cp += INT32SZ;
648 		n = ns_get16(cp);			/* len */
649 		cp += INT16SZ;
650 		if (class != C_IN) {
651 			cp += n;
652 			continue;
653 		}
654 		eor = cp + n;
655 		if (type == T_CNAME) {
656 			/*
657 			 * The name looked up is really an alias and the
658 			 * canonical name should be in the RDATA.
659 			 * A canonical name may have several aliases but an
660 			 * alias should only have one canonical name.
661 			 * However multiple CNAMEs and CNAME chains do exist!
662 			 *
663 			 * Just error out on attempted buffer overflow exploit,
664 			 * generic code will syslog.
665 			 *
666 			 */
667 			n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN);
668 			if (n > 0 && (len = strlen(aname)) > 0) {
669 				if (isans == 0) { /* host matched ans. */
670 					/*
671 					 * Append host to alias list.
672 					 */
673 					if (alen + hlen + 2 > NS_MAXMSG) {
674 						res_ndestroy(statp);
675 						return (NSS_ERROR);
676 					}
677 					*apc++ = ' ';
678 					alen++;
679 					(void) strlcpy(apc, host,
680 					    NS_MAXMSG - alen);
681 					alen += hlen;
682 					apc += hlen;
683 				}
684 				/*
685 				 * Overwrite host with canonical name.
686 				 */
687 				if (strlcpy(host, aname, MAXHOSTNAMELEN) >=
688 				    MAXHOSTNAMELEN) {
689 					res_ndestroy(statp);
690 					return (NSS_ERROR);
691 				}
692 				hlen = len;
693 			}
694 			cp += n;
695 			continue;
696 		}
697 		if (type != qtype) {
698 			cp += n;
699 			continue;
700 		}
701 		/* check data size */
702 		if ((type == T_A && n != INADDRSZ) ||
703 		    (type == T_AAAA && n != IN6ADDRSZ)) {
704 			cp += n;
705 			continue;
706 		}
707 		af = (type == T_A ? AF_INET : AF_INET6);
708 		np = inet_ntop(af, (void *)cp, nbuf, INET6_ADDRSTRLEN);
709 		if (np == NULL) {
710 			res_ndestroy(statp);
711 			return (NSS_ERROR);
712 		}
713 		cp += n;
714 		/* append IP host aliases to results */
715 		iplen = strlen(np);
716 		/* ip <SP> hostname [<SP>][aliases] */
717 		len = iplen + 2 + hlen + alen;
718 		if (alen > 0)
719 			len++;
720 		if (blen + len > bsize) {
721 			res_ndestroy(statp);
722 			return (NSS_ERROR);
723 		}
724 		(void) strlcpy(bptr, np, bsize - blen);
725 		blen += iplen;
726 		bptr += iplen;
727 		*bptr++ = ' ';
728 		blen++;
729 		(void) strlcpy(bptr, host, bsize - blen);
730 		blen += hlen;
731 		bptr += hlen;
732 		if (alen > 0) {
733 			*bptr++ = ' ';
734 			blen++;
735 			(void) strlcpy(bptr, ap, bsize - blen);
736 			blen += alen;
737 			bptr += alen;
738 		}
739 		*bptr++ = '\n';
740 		blen++;
741 	}
742 
743 	/* Depending on our flags we may need to go back another time. */
744 	if (qtype == T_AAAA && family == AF_INET6 &&
745 	    ((flags & AI_V4MAPPED) != 0) && ((flags & AI_ALL) != 0) &&
746 	    has_v4 == B_TRUE) {
747 		qtype = T_A;
748 		pass2 = 2; /* Indicate that we found data this pass */
749 		goto searchagain;
750 	}
751 
752 out:
753 	/* Presumably the buffer is now filled. */
754 	len = ROUND_UP(blen, sizeof (nssuint_t));
755 	/* still room? */
756 	if (len + sizeof (nssuint_t) > pbuf->data_len) {
757 		/* sigh, no, what happened? */
758 		res_ndestroy(statp);
759 		return (NSS_ERROR);
760 	}
761 
762 	pbuf->ext_off = pbuf->data_off + len;
763 	pbuf->ext_len = sizeof (nssuint_t);
764 	pbuf->data_len = blen;
765 	pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off));
766 	*pttl = ttl;
767 	res_ndestroy(statp);
768 	return (NSS_SUCCESS);
769 }
770