xref: /freebsd/lib/libc/net/gethostbydns.c (revision 734e82fe33aa764367791a7d603b383996c6b40b)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * ++Copyright++ 1985, 1988, 1993
5  * -
6  * Copyright (c) 1985, 1988, 1993
7  *	The Regents of the University of California.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  * -
33  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
34  *
35  * Permission to use, copy, modify, and distribute this software for any
36  * purpose with or without fee is hereby granted, provided that the above
37  * copyright notice and this permission notice appear in all copies, and that
38  * the name of Digital Equipment Corporation not be used in advertising or
39  * publicity pertaining to distribution of the document or software without
40  * specific, written prior permission.
41  *
42  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
43  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
44  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
45  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
46  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
47  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
48  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
49  * SOFTWARE.
50  * -
51  * --Copyright--
52  */
53 
54 #if defined(LIBC_SCCS) && !defined(lint)
55 static char sccsid[] = "@(#)gethostnamadr.c	8.1 (Berkeley) 6/4/93";
56 static char fromrcsid[] = "From: Id: gethnamaddr.c,v 8.23 1998/04/07 04:59:46 vixie Exp $";
57 #endif /* LIBC_SCCS and not lint */
58 #include <sys/cdefs.h>
59 #include <sys/param.h>
60 #include <sys/socket.h>
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
63 #include <arpa/nameser.h>
64 
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <unistd.h>
68 #include <string.h>
69 #include <netdb.h>
70 #include <resolv.h>
71 #include <ctype.h>
72 #include <errno.h>
73 #include <syslog.h>
74 #include <stdarg.h>
75 #include <nsswitch.h>
76 
77 #include "netdb_private.h"
78 #include "res_config.h"
79 
80 #define SPRINTF(x) ((size_t)sprintf x)
81 
82 static const char AskedForGot[] =
83 		"gethostby*.gethostanswer: asked for \"%s\", got \"%s\"";
84 
85 #ifdef RESOLVSORT
86 static void addrsort(char **, int, res_state);
87 #endif
88 
89 #ifdef DEBUG
90 static void dbg_printf(char *, int, res_state) __printflike(1, 0);
91 #endif
92 
93 #define MAXPACKET	(64*1024)
94 
95 typedef union {
96     HEADER hdr;
97     u_char buf[MAXPACKET];
98 } querybuf;
99 
100 typedef union {
101     int32_t al;
102     char ac;
103 } align;
104 
105 int _dns_ttl_;
106 
107 #ifdef DEBUG
108 static void
109 dbg_printf(char *msg, int num, res_state res)
110 {
111 	if (res->options & RES_DEBUG) {
112 		int save = errno;
113 
114 		printf(msg, num);
115 		errno = save;
116 	}
117 }
118 #else
119 # define dbg_printf(msg, num, res) /*nada*/
120 #endif
121 
122 #define BOUNDED_INCR(x) \
123 	do { \
124 		cp += x; \
125 		if (cp > eom) { \
126 			RES_SET_H_ERRNO(statp, NO_RECOVERY); \
127 			return (-1); \
128 		} \
129 	} while (0)
130 
131 #define BOUNDS_CHECK(ptr, count) \
132 	do { \
133 		if ((ptr) + (count) > eom) { \
134 			RES_SET_H_ERRNO(statp, NO_RECOVERY); \
135 			return (-1); \
136 		} \
137 	} while (0)
138 
139 static int
140 gethostanswer(const querybuf *answer, int anslen, const char *qname, int qtype,
141     struct hostent *he, struct hostent_data *hed, res_state statp)
142 {
143 	const HEADER *hp;
144 	const u_char *cp;
145 	int n;
146 	const u_char *eom, *erdata;
147 	char *bp, *ep, **ap, **hap;
148 	int type, class, ancount, qdcount;
149 	int haveanswer, had_error;
150 	int toobig = 0;
151 	char tbuf[MAXDNAME];
152 	const char *tname;
153 	int (*name_ok)(const char *);
154 
155 	tname = qname;
156 	he->h_name = NULL;
157 	eom = answer->buf + anslen;
158 	switch (qtype) {
159 	case T_A:
160 	case T_AAAA:
161 		name_ok = res_hnok;
162 		break;
163 	case T_PTR:
164 		name_ok = res_dnok;
165 		break;
166 	default:
167 		RES_SET_H_ERRNO(statp, NO_RECOVERY);
168 		return (-1);	/* XXX should be abort(); */
169 	}
170 	/*
171 	 * find first satisfactory answer
172 	 */
173 	hp = &answer->hdr;
174 	ancount = ntohs(hp->ancount);
175 	qdcount = ntohs(hp->qdcount);
176 	bp = hed->hostbuf;
177 	ep = hed->hostbuf + sizeof hed->hostbuf;
178 	cp = answer->buf;
179 	BOUNDED_INCR(HFIXEDSZ);
180 	if (qdcount != 1) {
181 		RES_SET_H_ERRNO(statp, NO_RECOVERY);
182 		return (-1);
183 	}
184 	n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
185 	if ((n < 0) || !(*name_ok)(bp)) {
186 		RES_SET_H_ERRNO(statp, NO_RECOVERY);
187 		return (-1);
188 	}
189 	BOUNDED_INCR(n + QFIXEDSZ);
190 	if (qtype == T_A || qtype == T_AAAA) {
191 		/* res_send() has already verified that the query name is the
192 		 * same as the one we sent; this just gets the expanded name
193 		 * (i.e., with the succeeding search-domain tacked on).
194 		 */
195 		n = strlen(bp) + 1;		/* for the \0 */
196 		if (n >= MAXHOSTNAMELEN) {
197 			RES_SET_H_ERRNO(statp, NO_RECOVERY);
198 			return (-1);
199 		}
200 		he->h_name = bp;
201 		bp += n;
202 		/* The qname can be abbreviated, but h_name is now absolute. */
203 		qname = he->h_name;
204 	}
205 	ap = hed->host_aliases;
206 	*ap = NULL;
207 	he->h_aliases = hed->host_aliases;
208 	hap = hed->h_addr_ptrs;
209 	*hap = NULL;
210 	he->h_addr_list = hed->h_addr_ptrs;
211 	haveanswer = 0;
212 	had_error = 0;
213 	_dns_ttl_ = -1;
214 	while (ancount-- > 0 && cp < eom && !had_error) {
215 		n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
216 		if ((n < 0) || !(*name_ok)(bp)) {
217 			had_error++;
218 			continue;
219 		}
220 		cp += n;			/* name */
221 		BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ);
222 		type = _getshort(cp);
223  		cp += INT16SZ;			/* type */
224 		class = _getshort(cp);
225  		cp += INT16SZ;			/* class */
226 		if (qtype == T_A  && type == T_A)
227 			_dns_ttl_ = _getlong(cp);
228 		cp += INT32SZ;			/* TTL */
229 		n = _getshort(cp);
230 		cp += INT16SZ;			/* len */
231 		BOUNDS_CHECK(cp, n);
232 		erdata = cp + n;
233 		if (class != C_IN) {
234 			/* XXX - debug? syslog? */
235 			cp += n;
236 			continue;		/* XXX - had_error++ ? */
237 		}
238 		if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
239 			if (ap >= &hed->host_aliases[_MAXALIASES-1])
240 				continue;
241 			n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
242 			if ((n < 0) || !(*name_ok)(tbuf)) {
243 				had_error++;
244 				continue;
245 			}
246 			cp += n;
247 			if (cp != erdata) {
248 				RES_SET_H_ERRNO(statp, NO_RECOVERY);
249 				return (-1);
250 			}
251 			/* Store alias. */
252 			*ap++ = bp;
253 			n = strlen(bp) + 1;	/* for the \0 */
254 			if (n >= MAXHOSTNAMELEN) {
255 				had_error++;
256 				continue;
257 			}
258 			bp += n;
259 			/* Get canonical name. */
260 			n = strlen(tbuf) + 1;	/* for the \0 */
261 			if (n > ep - bp || n >= MAXHOSTNAMELEN) {
262 				had_error++;
263 				continue;
264 			}
265 			strcpy(bp, tbuf);
266 			he->h_name = bp;
267 			bp += n;
268 			continue;
269 		}
270 		if (qtype == T_PTR && type == T_CNAME) {
271 			n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
272 			if (n < 0 || !res_dnok(tbuf)) {
273 				had_error++;
274 				continue;
275 			}
276 			cp += n;
277 			if (cp != erdata) {
278 				RES_SET_H_ERRNO(statp, NO_RECOVERY);
279 				return (-1);
280 			}
281 			/* Get canonical name. */
282 			n = strlen(tbuf) + 1;	/* for the \0 */
283 			if (n > ep - bp || n >= MAXHOSTNAMELEN) {
284 				had_error++;
285 				continue;
286 			}
287 			strcpy(bp, tbuf);
288 			tname = bp;
289 			bp += n;
290 			continue;
291 		}
292 		if (type != qtype) {
293 #ifdef DEBUG
294 			if (type != T_KEY && type != T_SIG &&
295 			    type != T_DNAME && type != T_RRSIG)
296 				syslog(LOG_NOTICE|LOG_AUTH,
297 	"gethostby*.gethostanswer: asked for \"%s %s %s\", got type \"%s\"",
298 				       qname, p_class(C_IN), p_type(qtype),
299 				       p_type(type));
300 #endif
301 			cp += n;
302 			continue;		/* XXX - had_error++ ? */
303 		}
304 		switch (type) {
305 		case T_PTR:
306 			if (strcasecmp(tname, bp) != 0) {
307 				syslog(LOG_NOTICE|LOG_AUTH,
308 				       AskedForGot, qname, bp);
309 				cp += n;
310 				continue;	/* XXX - had_error++ ? */
311 			}
312 			n = dn_expand(answer->buf, eom, cp, bp, ep - bp);
313 			if ((n < 0) || !res_hnok(bp)) {
314 				had_error++;
315 				break;
316 			}
317 #if MULTI_PTRS_ARE_ALIASES
318 			cp += n;
319 			if (cp != erdata) {
320 				RES_SET_H_ERRNO(statp, NO_RECOVERY);
321 				return (-1);
322 			}
323 			if (!haveanswer)
324 				he->h_name = bp;
325 			else if (ap < &hed->host_aliases[_MAXALIASES-1])
326 				*ap++ = bp;
327 			else
328 				n = -1;
329 			if (n != -1) {
330 				n = strlen(bp) + 1;	/* for the \0 */
331 				if (n >= MAXHOSTNAMELEN) {
332 					had_error++;
333 					break;
334 				}
335 				bp += n;
336 			}
337 			break;
338 #else
339 			he->h_name = bp;
340 			if (statp->options & RES_USE_INET6) {
341 				n = strlen(bp) + 1;	/* for the \0 */
342 				if (n >= MAXHOSTNAMELEN) {
343 					had_error++;
344 					break;
345 				}
346 				bp += n;
347 				_map_v4v6_hostent(he, &bp, ep);
348 			}
349 			RES_SET_H_ERRNO(statp, NETDB_SUCCESS);
350 			return (0);
351 #endif
352 		case T_A:
353 		case T_AAAA:
354 			if (strcasecmp(he->h_name, bp) != 0) {
355 				syslog(LOG_NOTICE|LOG_AUTH,
356 				       AskedForGot, he->h_name, bp);
357 				cp += n;
358 				continue;	/* XXX - had_error++ ? */
359 			}
360 			if (n != he->h_length) {
361 				cp += n;
362 				continue;
363 			}
364 			if (!haveanswer) {
365 				int nn;
366 
367 				he->h_name = bp;
368 				nn = strlen(bp) + 1;	/* for the \0 */
369 				bp += nn;
370 			}
371 
372 			bp += sizeof(align) - ((u_long)bp % sizeof(align));
373 
374 			if (bp + n >= ep) {
375 				dbg_printf("size (%d) too big\n", n, statp);
376 				had_error++;
377 				continue;
378 			}
379 			if (hap >= &hed->h_addr_ptrs[_MAXADDRS-1]) {
380 				if (!toobig++)
381 					dbg_printf("Too many addresses (%d)\n",
382 						_MAXADDRS, statp);
383 				cp += n;
384 				continue;
385 			}
386 			memcpy(*hap++ = bp, cp, n);
387 			bp += n;
388 			cp += n;
389 			if (cp != erdata) {
390 				RES_SET_H_ERRNO(statp, NO_RECOVERY);
391 				return (-1);
392 			}
393 			break;
394 		default:
395 			dbg_printf("Impossible condition (type=%d)\n", type,
396 			    statp);
397 			RES_SET_H_ERRNO(statp, NO_RECOVERY);
398 			return (-1);
399 			/* BIND has abort() here, too risky on bad data */
400 		}
401 		if (!had_error)
402 			haveanswer++;
403 	}
404 	if (haveanswer) {
405 		*ap = NULL;
406 		*hap = NULL;
407 # if defined(RESOLVSORT)
408 		/*
409 		 * Note: we sort even if host can take only one address
410 		 * in its return structures - should give it the "best"
411 		 * address in that case, not some random one
412 		 */
413 		if (statp->nsort && haveanswer > 1 && qtype == T_A)
414 			addrsort(hed->h_addr_ptrs, haveanswer, statp);
415 # endif /*RESOLVSORT*/
416 		if (!he->h_name) {
417 			n = strlen(qname) + 1;	/* for the \0 */
418 			if (n > ep - bp || n >= MAXHOSTNAMELEN)
419 				goto no_recovery;
420 			strcpy(bp, qname);
421 			he->h_name = bp;
422 			bp += n;
423 		}
424 		if (statp->options & RES_USE_INET6)
425 			_map_v4v6_hostent(he, &bp, ep);
426 		RES_SET_H_ERRNO(statp, NETDB_SUCCESS);
427 		return (0);
428 	}
429  no_recovery:
430 	RES_SET_H_ERRNO(statp, NO_RECOVERY);
431 	return (-1);
432 }
433 
434 /* XXX: for async DNS resolver in ypserv */
435 struct hostent *
436 __dns_getanswer(const char *answer, int anslen, const char *qname, int qtype)
437 {
438 	struct hostent *he;
439 	struct hostent_data *hed;
440 	int error;
441 	res_state statp;
442 
443 	statp = __res_state();
444 	if ((he = __hostent_init()) == NULL ||
445 	    (hed = __hostent_data_init()) == NULL) {
446 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
447 		return (NULL);
448 	}
449 	switch (qtype) {
450 	case T_AAAA:
451 		he->h_addrtype = AF_INET6;
452 		he->h_length = NS_IN6ADDRSZ;
453 		break;
454 	case T_A:
455 	default:
456 		he->h_addrtype = AF_INET;
457 		he->h_length = NS_INADDRSZ;
458 		break;
459 	}
460 
461 	error = gethostanswer((const querybuf *)answer, anslen, qname, qtype,
462 	    he, hed, statp);
463 	return (error == 0) ? he : NULL;
464 }
465 
466 int
467 _dns_gethostbyname(void *rval, void *cb_data, va_list ap)
468 {
469 	const char *name;
470 	int af;
471 	char *buffer;
472 	size_t buflen;
473 	int *errnop, *h_errnop;
474 	struct hostent *hptr, he;
475 	struct hostent_data *hed;
476 	querybuf *buf;
477 	int n, type, error;
478 	res_state statp;
479 
480 	name = va_arg(ap, const char *);
481 	af = va_arg(ap, int);
482 	hptr = va_arg(ap, struct hostent *);
483 	buffer = va_arg(ap, char *);
484 	buflen = va_arg(ap, size_t);
485 	errnop = va_arg(ap, int *);
486 	h_errnop = va_arg(ap, int *);
487 
488 	*((struct hostent **)rval) = NULL;
489 
490 	statp = __res_state();
491 	if ((hed = __hostent_data_init()) == NULL) {
492 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
493 		*h_errnop = statp->res_h_errno;
494 		return (NS_NOTFOUND);
495 	}
496 
497 	he.h_addrtype = af;
498 	switch (af) {
499 	case AF_INET:
500 		he.h_length = NS_INADDRSZ;
501 		type = T_A;
502 		break;
503 	case AF_INET6:
504 		he.h_length = NS_IN6ADDRSZ;
505 		type = T_AAAA;
506 		break;
507 	default:
508 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
509 		*h_errnop = statp->res_h_errno;
510 		errno = EAFNOSUPPORT;
511 		return (NS_UNAVAIL);
512 	}
513 
514 	if ((buf = malloc(sizeof(*buf))) == NULL) {
515 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
516 		*h_errnop = statp->res_h_errno;
517 		return (NS_NOTFOUND);
518 	}
519 	n = res_nsearch(statp, name, C_IN, type, buf->buf, sizeof(buf->buf));
520 	if (n < 0) {
521 		free(buf);
522 		dbg_printf("res_nsearch failed (%d)\n", n, statp);
523 		*h_errnop = statp->res_h_errno;
524 		return (NS_NOTFOUND);
525 	} else if (n > sizeof(buf->buf)) {
526 		free(buf);
527 		dbg_printf("static buffer is too small (%d)\n", n, statp);
528 		*h_errnop = statp->res_h_errno;
529 		return (NS_UNAVAIL);
530 	}
531 	error = gethostanswer(buf, n, name, type, &he, hed, statp);
532 	free(buf);
533 	if (error != 0) {
534 		*h_errnop = statp->res_h_errno;
535 		switch (statp->res_h_errno) {
536 		case HOST_NOT_FOUND:
537 			return (NS_NOTFOUND);
538 		case TRY_AGAIN:
539 			return (NS_TRYAGAIN);
540 		default:
541 			return (NS_UNAVAIL);
542 		}
543 		/*NOTREACHED*/
544 	}
545 	if (__copy_hostent(&he, hptr, buffer, buflen) != 0) {
546 		*errnop = errno;
547 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
548 		*h_errnop = statp->res_h_errno;
549 		return (NS_RETURN);
550 	}
551 	RES_SET_H_ERRNO(statp, NETDB_SUCCESS);
552 	*((struct hostent **)rval) = hptr;
553 	return (NS_SUCCESS);
554 }
555 
556 int
557 _dns_gethostbyaddr(void *rval, void *cb_data, va_list ap)
558 {
559 	const void *addr;
560 	socklen_t len;
561 	int af;
562 	char *buffer;
563 	size_t buflen;
564 	int *errnop, *h_errnop;
565 	const u_char *uaddr;
566 	struct hostent *hptr, he;
567 	struct hostent_data *hed;
568 	int n;
569 	querybuf *buf;
570 	char qbuf[MAXDNAME+1], *qp;
571 	res_state statp;
572 #ifdef SUNSECURITY
573 	struct hostdata rhd;
574 	struct hostent *rhe;
575 	char **haddr;
576 	u_long old_options;
577 	char hname2[MAXDNAME+1], numaddr[46];
578 	int ret_h_error;
579 #endif /*SUNSECURITY*/
580 
581 	addr = va_arg(ap, const void *);
582 	len = va_arg(ap, socklen_t);
583 	af = va_arg(ap, int);
584 	hptr = va_arg(ap, struct hostent *);
585 	buffer = va_arg(ap, char *);
586 	buflen = va_arg(ap, size_t);
587 	errnop = va_arg(ap, int *);
588 	h_errnop = va_arg(ap, int *);
589 	uaddr = (const u_char *)addr;
590 
591 	*((struct hostent **)rval) = NULL;
592 
593 	statp = __res_state();
594 	if ((hed = __hostent_data_init()) == NULL) {
595 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
596 		*h_errnop = statp->res_h_errno;
597 		return (NS_NOTFOUND);
598 	}
599 
600 	switch (af) {
601 	case AF_INET:
602 		(void) sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa",
603 			       (uaddr[3] & 0xff),
604 			       (uaddr[2] & 0xff),
605 			       (uaddr[1] & 0xff),
606 			       (uaddr[0] & 0xff));
607 		break;
608 	case AF_INET6:
609 		qp = qbuf;
610 		for (n = NS_IN6ADDRSZ - 1; n >= 0; n--) {
611 			qp += SPRINTF((qp, "%x.%x.",
612 				       uaddr[n] & 0xf,
613 				       (uaddr[n] >> 4) & 0xf));
614 		}
615 		strlcat(qbuf, "ip6.arpa", sizeof(qbuf));
616 		break;
617 	default:
618 		abort();
619 	}
620 	if ((buf = malloc(sizeof(*buf))) == NULL) {
621 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
622 		*h_errnop = statp->res_h_errno;
623 		return NS_NOTFOUND;
624 	}
625 	n = res_nquery(statp, qbuf, C_IN, T_PTR, (u_char *)buf->buf,
626 	    sizeof buf->buf);
627 	if (n < 0) {
628 		free(buf);
629 		dbg_printf("res_nquery failed (%d)\n", n, statp);
630 		*h_errnop = statp->res_h_errno;
631 		return (NS_UNAVAIL);
632 	}
633 	if (n > sizeof buf->buf) {
634 		free(buf);
635 		dbg_printf("static buffer is too small (%d)\n", n, statp);
636 		*h_errnop = statp->res_h_errno;
637 		return (NS_UNAVAIL);
638 	}
639 	if (gethostanswer(buf, n, qbuf, T_PTR, &he, hed, statp) != 0) {
640 		free(buf);
641 		*h_errnop = statp->res_h_errno;
642 		switch (statp->res_h_errno) {
643 		case HOST_NOT_FOUND:
644 			return (NS_NOTFOUND);
645 		case TRY_AGAIN:
646 			return (NS_TRYAGAIN);
647 		default:
648 			return (NS_UNAVAIL);
649 		}
650 		/*NOTREACHED*/
651 	}
652 	free(buf);
653 #ifdef SUNSECURITY
654 	if (af == AF_INET) {
655 	    /*
656 	     * turn off search as the name should be absolute,
657 	     * 'localhost' should be matched by defnames
658 	     */
659 	    strncpy(hname2, he.h_name, MAXDNAME);
660 	    hname2[MAXDNAME] = '\0';
661 	    old_options = statp->options;
662 	    statp->options &= ~RES_DNSRCH;
663 	    statp->options |= RES_DEFNAMES;
664 	    memset(&rhd, 0, sizeof rhd);
665 	    rhe = gethostbyname_r(hname2, &rhd.host, &rhd.data,
666 	        sizeof(rhd.data), &ret_h_error);
667 	    if (rhe == NULL) {
668 		if (inet_ntop(af, addr, numaddr, sizeof(numaddr)) == NULL)
669 		    strlcpy(numaddr, "UNKNOWN", sizeof(numaddr));
670 		syslog(LOG_NOTICE|LOG_AUTH,
671 		       "gethostbyaddr: No A record for %s (verifying [%s])",
672 		       hname2, numaddr);
673 		statp->options = old_options;
674 		RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
675 		*h_errnop = statp->res_h_errno;
676 		return (NS_NOTFOUND);
677 	    }
678 	    statp->options = old_options;
679 	    for (haddr = rhe->h_addr_list; *haddr; haddr++)
680 		if (!memcmp(*haddr, addr, NS_INADDRSZ))
681 			break;
682 	    if (!*haddr) {
683 		if (inet_ntop(af, addr, numaddr, sizeof(numaddr)) == NULL)
684 		    strlcpy(numaddr, "UNKNOWN", sizeof(numaddr));
685 		syslog(LOG_NOTICE|LOG_AUTH,
686 		       "gethostbyaddr: A record of %s != PTR record [%s]",
687 		       hname2, numaddr);
688 		RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
689 		*h_errnop = statp->res_h_errno;
690 		return (NS_NOTFOUND);
691 	    }
692 	}
693 #endif /*SUNSECURITY*/
694 	he.h_addrtype = af;
695 	he.h_length = len;
696 	memcpy(hed->host_addr, uaddr, len);
697 	hed->h_addr_ptrs[0] = (char *)hed->host_addr;
698 	hed->h_addr_ptrs[1] = NULL;
699 	if (af == AF_INET && (statp->options & RES_USE_INET6)) {
700 		_map_v4v6_address((char*)hed->host_addr, (char*)hed->host_addr);
701 		he.h_addrtype = AF_INET6;
702 		he.h_length = NS_IN6ADDRSZ;
703 	}
704 	if (__copy_hostent(&he, hptr, buffer, buflen) != 0) {
705 		*errnop = errno;
706 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
707 		*h_errnop = statp->res_h_errno;
708 		return (NS_RETURN);
709 	}
710 	RES_SET_H_ERRNO(statp, NETDB_SUCCESS);
711 	*((struct hostent **)rval) = hptr;
712 	return (NS_SUCCESS);
713 }
714 
715 #ifdef RESOLVSORT
716 static void
717 addrsort(char **ap, int num, res_state res)
718 {
719 	int i, j;
720 	char **p;
721 	short aval[_MAXADDRS];
722 	int needsort = 0;
723 
724 	p = ap;
725 	for (i = 0; i < num; i++, p++) {
726 	    for (j = 0 ; (unsigned)j < res->nsort; j++)
727 		if (res->sort_list[j].addr.s_addr ==
728 		    (((struct in_addr *)(*p))->s_addr & res->sort_list[j].mask))
729 			break;
730 	    aval[i] = j;
731 	    if (needsort == 0 && i > 0 && j < aval[i-1])
732 		needsort = i;
733 	}
734 	if (!needsort)
735 	    return;
736 
737 	while (needsort < num) {
738 	    for (j = needsort - 1; j >= 0; j--) {
739 		if (aval[j] > aval[j+1]) {
740 		    char *hp;
741 
742 		    i = aval[j];
743 		    aval[j] = aval[j+1];
744 		    aval[j+1] = i;
745 
746 		    hp = ap[j];
747 		    ap[j] = ap[j+1];
748 		    ap[j+1] = hp;
749 
750 		} else
751 		    break;
752 	    }
753 	    needsort++;
754 	}
755 }
756 #endif
757 
758 void
759 _sethostdnsent(int stayopen)
760 {
761 	res_state statp;
762 
763 	statp = __res_state();
764 	if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1)
765 		return;
766 	if (stayopen)
767 		statp->options |= RES_STAYOPEN | RES_USEVC;
768 }
769 
770 void
771 _endhostdnsent(void)
772 {
773 	res_state statp;
774 
775 	statp = __res_state();
776 	statp->options &= ~(RES_STAYOPEN | RES_USEVC);
777 	res_nclose(statp);
778 }
779