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