1/*
2 * Portions Copyright (C) 2004-2006, 2008  Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 1996-2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/*
19 * Copyright (c) 1985, 1988, 1993
20 *    The Regents of the University of California.  All rights reserved.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 * 1. Redistributions of source code must retain the above copyright
26 *    notice, this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 *    notice, this list of conditions and the following disclaimer in the
29 *    documentation and/or other materials provided with the distribution.
30 * 3. All advertising materials mentioning features or use of this software
31 *    must display the following acknowledgement:
32 * 	This product includes software developed by the University of
33 * 	California, Berkeley and its contributors.
34 * 4. Neither the name of the University nor the names of its contributors
35 *    may be used to endorse or promote products derived from this software
36 *    without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
39 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
42 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 */
50
51/* from gethostnamadr.c	8.1 (Berkeley) 6/4/93 */
52/* BIND Id: gethnamaddr.c,v 8.15 1996/05/22 04:56:30 vixie Exp $ */
53
54#if defined(LIBC_SCCS) && !defined(lint)
55static const char rcsid[] = "$Id: dns_ho.c,v 1.23 2008/11/14 02:36:51 marka Exp $";
56#endif /* LIBC_SCCS and not lint */
57
58/* Imports. */
59
60#include "port_before.h"
61
62#include <sys/types.h>
63#include <sys/param.h>
64#include <sys/socket.h>
65
66#include <netinet/in.h>
67#include <arpa/inet.h>
68#include <arpa/nameser.h>
69
70#include <ctype.h>
71#include <errno.h>
72#include <stdlib.h>
73#include <netdb.h>
74#include <resolv.h>
75#include <stdio.h>
76#include <string.h>
77#include <syslog.h>
78#include <unistd.h>
79
80#include <isc/memcluster.h>
81#include <irs.h>
82
83#include "port_after.h"
84
85#include "irs_p.h"
86#include "dns_p.h"
87
88#ifdef SPRINTF_CHAR
89# define SPRINTF(x) strlen(sprintf/**/x)
90#else
91# define SPRINTF(x) sprintf x
92#endif
93
94/* Definitions. */
95
96#define	MAXALIASES	35
97#define	MAXADDRS	35
98
99#define MAXPACKET (65535)	/*%< Maximum TCP message size */
100#define BOUNDS_CHECK(ptr, count) \
101	if ((ptr) + (count) > eom) { \
102		had_error++; \
103		continue; \
104	} else (void)0
105
106typedef union {
107	HEADER hdr;
108	u_char buf[MAXPACKET];
109} querybuf;
110
111struct dns_res_target {
112	struct dns_res_target *next;
113	querybuf qbuf;		/*%< query buffer */
114	u_char *answer;		/*%< buffer to put answer */
115	int anslen;		/*%< size of answer buffer */
116	int qclass, qtype;	/*%< class and type of query */
117	int action;		/*%< condition whether query is really issued */
118	char qname[MAXDNAME +1]; /*%< domain name */
119#if 0
120	int n;			/*%< result length */
121#endif
122};
123enum {RESTGT_DOALWAYS, RESTGT_AFTERFAILURE, RESTGT_IGNORE};
124enum {RESQRY_SUCCESS, RESQRY_FAIL};
125
126struct pvt {
127	struct hostent	host;
128	char *		h_addr_ptrs[MAXADDRS + 1];
129	char *		host_aliases[MAXALIASES];
130	char		hostbuf[8*1024];
131	u_char		host_addr[16];	/*%< IPv4 or IPv6 */
132	struct __res_state  *res;
133	void		(*free_res)(void *);
134};
135
136typedef union {
137	int32_t al;
138	char ac;
139} align;
140
141static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
142static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
143/* Note: the IPv6 loopback address is in the "tunnel" space */
144static const u_char v6local[] = { 0,0, 0,1 }; /*%< last 4 bytes of IPv6 addr */
145/* Forwards. */
146
147static void		ho_close(struct irs_ho *this);
148static struct hostent *	ho_byname(struct irs_ho *this, const char *name);
149static struct hostent *	ho_byname2(struct irs_ho *this, const char *name,
150				   int af);
151static struct hostent *	ho_byaddr(struct irs_ho *this, const void *addr,
152				  int len, int af);
153static struct hostent *	ho_next(struct irs_ho *this);
154static void		ho_rewind(struct irs_ho *this);
155static void		ho_minimize(struct irs_ho *this);
156static struct __res_state * ho_res_get(struct irs_ho *this);
157static void		ho_res_set(struct irs_ho *this,
158				   struct __res_state *res,
159				   void (*free_res)(void *));
160static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name,
161				     const struct addrinfo *pai);
162
163static void		map_v4v6_hostent(struct hostent *hp, char **bp,
164					 char *ep);
165static void		addrsort(res_state, char **, int);
166static struct hostent *	gethostans(struct irs_ho *this,
167				   const u_char *ansbuf, int anslen,
168				   const char *qname, int qtype,
169				   int af, int size,
170				   struct addrinfo **ret_aip,
171				   const struct addrinfo *pai);
172static int add_hostent(struct pvt *pvt, char *bp, char **hap,
173		       struct addrinfo *ai);
174static int		init(struct irs_ho *this);
175
176/* Exports. */
177
178struct irs_ho *
179irs_dns_ho(struct irs_acc *this) {
180	struct irs_ho *ho;
181	struct pvt *pvt;
182
183	UNUSED(this);
184
185	if (!(pvt = memget(sizeof *pvt))) {
186		errno = ENOMEM;
187		return (NULL);
188	}
189	memset(pvt, 0, sizeof *pvt);
190
191	if (!(ho = memget(sizeof *ho))) {
192		memput(pvt, sizeof *pvt);
193		errno = ENOMEM;
194		return (NULL);
195	}
196	memset(ho, 0x5e, sizeof *ho);
197	ho->private = pvt;
198	ho->close = ho_close;
199	ho->byname = ho_byname;
200	ho->byname2 = ho_byname2;
201	ho->byaddr = ho_byaddr;
202	ho->next = ho_next;
203	ho->rewind = ho_rewind;
204	ho->minimize = ho_minimize;
205	ho->res_get = ho_res_get;
206	ho->res_set = ho_res_set;
207	ho->addrinfo = ho_addrinfo;
208	return (ho);
209}
210
211/* Methods. */
212
213static void
214ho_close(struct irs_ho *this) {
215	struct pvt *pvt = (struct pvt *)this->private;
216
217	ho_minimize(this);
218	if (pvt->res && pvt->free_res)
219		(*pvt->free_res)(pvt->res);
220	memput(pvt, sizeof *pvt);
221	memput(this, sizeof *this);
222}
223
224static struct hostent *
225ho_byname(struct irs_ho *this, const char *name) {
226	struct pvt *pvt = (struct pvt *)this->private;
227	struct hostent *hp;
228
229	if (init(this) == -1)
230		return (NULL);
231
232	if (pvt->res->options & RES_USE_INET6) {
233		hp = ho_byname2(this, name, AF_INET6);
234		if (hp)
235			return (hp);
236	}
237	return (ho_byname2(this, name, AF_INET));
238}
239
240static struct hostent *
241ho_byname2(struct irs_ho *this, const char *name, int af)
242{
243	struct pvt *pvt = (struct pvt *)this->private;
244	struct hostent *hp = NULL;
245	int n, size;
246	char tmp[NS_MAXDNAME];
247	const char *cp;
248	struct addrinfo ai;
249	struct dns_res_target *q, *p;
250	int querystate = RESQRY_FAIL;
251
252	if (init(this) == -1)
253		return (NULL);
254
255	q = memget(sizeof(*q));
256	if (q == NULL) {
257		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
258		errno = ENOMEM;
259		goto cleanup;
260	}
261	memset(q, 0, sizeof(*q));
262
263	switch (af) {
264	case AF_INET:
265		size = INADDRSZ;
266		q->qclass = C_IN;
267		q->qtype = T_A;
268		q->answer = q->qbuf.buf;
269		q->anslen = sizeof(q->qbuf);
270		q->action = RESTGT_DOALWAYS;
271		break;
272	case AF_INET6:
273		size = IN6ADDRSZ;
274		q->qclass = C_IN;
275		q->qtype = T_AAAA;
276		q->answer = q->qbuf.buf;
277		q->anslen = sizeof(q->qbuf);
278		q->action = RESTGT_DOALWAYS;
279		break;
280	default:
281		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
282		errno = EAFNOSUPPORT;
283		hp = NULL;
284		goto cleanup;
285	}
286
287	/*
288	 * if there aren't any dots, it could be a user-level alias.
289	 * this is also done in res_nquery() since we are not the only
290	 * function that looks up host names.
291	 */
292	if (!strchr(name, '.') && (cp = res_hostalias(pvt->res, name,
293						      tmp, sizeof tmp)))
294		name = cp;
295
296	for (p = q; p; p = p->next) {
297		switch(p->action) {
298		case RESTGT_DOALWAYS:
299			break;
300		case RESTGT_AFTERFAILURE:
301			if (querystate == RESQRY_SUCCESS)
302				continue;
303			break;
304		case RESTGT_IGNORE:
305			continue;
306		}
307
308		if ((n = res_nsearch(pvt->res, name, p->qclass, p->qtype,
309				     p->answer, p->anslen)) < 0) {
310			querystate = RESQRY_FAIL;
311			continue;
312		}
313
314		memset(&ai, 0, sizeof(ai));
315		ai.ai_family = af;
316		if ((hp = gethostans(this, p->answer, n, name, p->qtype,
317				     af, size, NULL,
318				     (const struct addrinfo *)&ai)) != NULL)
319			goto cleanup;	/*%< no more loop is necessary */
320		querystate = RESQRY_FAIL;
321		continue;
322	}
323
324 cleanup:
325	if (q != NULL)
326		memput(q, sizeof(*q));
327	return(hp);
328}
329
330static struct hostent *
331ho_byaddr(struct irs_ho *this, const void *addr, int len, int af)
332{
333	struct pvt *pvt = (struct pvt *)this->private;
334	const u_char *uaddr = addr;
335	char *qp;
336	struct hostent *hp = NULL;
337	struct addrinfo ai;
338	struct dns_res_target *q, *q2, *p;
339	int n, size, i;
340	int querystate = RESQRY_FAIL;
341
342	if (init(this) == -1)
343		return (NULL);
344
345	q = memget(sizeof(*q));
346	q2 = memget(sizeof(*q2));
347	if (q == NULL || q2 == NULL) {
348		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
349		errno = ENOMEM;
350		goto cleanup;
351	}
352	memset(q, 0, sizeof(*q));
353	memset(q2, 0, sizeof(*q2));
354
355	if (af == AF_INET6 && len == IN6ADDRSZ &&
356	    (!memcmp(uaddr, mapped, sizeof mapped) ||
357	   (!memcmp(uaddr, tunnelled, sizeof tunnelled) &&
358	    memcmp(&uaddr[sizeof tunnelled], v6local, sizeof(v6local))))) {
359		/* Unmap. */
360		addr = (const char *)addr + sizeof mapped;
361		uaddr += sizeof mapped;
362		af = AF_INET;
363		len = INADDRSZ;
364	}
365	switch (af) {
366	case AF_INET:
367		size = INADDRSZ;
368		q->qclass = C_IN;
369		q->qtype = T_PTR;
370		q->answer = q->qbuf.buf;
371		q->anslen = sizeof(q->qbuf);
372		q->action = RESTGT_DOALWAYS;
373		break;
374	case AF_INET6:
375		size = IN6ADDRSZ;
376		q->qclass = C_IN;
377		q->qtype = T_PTR;
378		q->answer = q->qbuf.buf;
379		q->anslen = sizeof(q->qbuf);
380		q->next = q2;
381		q->action = RESTGT_DOALWAYS;
382		q2->qclass = C_IN;
383		q2->qtype = T_PTR;
384		q2->answer = q2->qbuf.buf;
385		q2->anslen = sizeof(q2->qbuf);
386		if ((pvt->res->options & RES_NO_NIBBLE2) != 0U)
387			q2->action = RESTGT_IGNORE;
388		else
389			q2->action = RESTGT_AFTERFAILURE;
390		break;
391	default:
392		errno = EAFNOSUPPORT;
393		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
394		hp = NULL;
395		goto cleanup;
396	}
397	if (size > len) {
398		errno = EINVAL;
399		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
400		hp = NULL;
401		goto cleanup;
402	}
403	switch (af) {
404	case AF_INET:
405		qp = q->qname;
406		(void) sprintf(qp, "%u.%u.%u.%u.in-addr.arpa",
407			       (uaddr[3] & 0xff),
408			       (uaddr[2] & 0xff),
409			       (uaddr[1] & 0xff),
410			       (uaddr[0] & 0xff));
411		break;
412	case AF_INET6:
413		if (q->action != RESTGT_IGNORE) {
414			const char *nibsuff = res_get_nibblesuffix(pvt->res);
415			qp = q->qname;
416			for (n = IN6ADDRSZ - 1; n >= 0; n--) {
417				i = SPRINTF((qp, "%x.%x.",
418					       uaddr[n] & 0xf,
419					       (uaddr[n] >> 4) & 0xf));
420				if (i != 4)
421					abort();
422				qp += i;
423			}
424			if (strlen(q->qname) + strlen(nibsuff) + 1 >
425			    sizeof q->qname) {
426				errno = ENAMETOOLONG;
427				RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
428				hp = NULL;
429				goto cleanup;
430			}
431			strcpy(qp, nibsuff);	/* (checked) */
432		}
433		if (q2->action != RESTGT_IGNORE) {
434			const char *nibsuff2 = res_get_nibblesuffix2(pvt->res);
435			qp = q2->qname;
436			for (n = IN6ADDRSZ - 1; n >= 0; n--) {
437				i = SPRINTF((qp, "%x.%x.",
438					       uaddr[n] & 0xf,
439					       (uaddr[n] >> 4) & 0xf));
440				if (i != 4)
441					abort();
442				qp += i;
443			}
444			if (strlen(q2->qname) + strlen(nibsuff2) + 1 >
445			    sizeof q2->qname) {
446				errno = ENAMETOOLONG;
447				RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
448				hp = NULL;
449				goto cleanup;
450			}
451			strcpy(qp, nibsuff2);	/* (checked) */
452		}
453		break;
454	default:
455		abort();
456	}
457
458	for (p = q; p; p = p->next) {
459		switch(p->action) {
460		case RESTGT_DOALWAYS:
461			break;
462		case RESTGT_AFTERFAILURE:
463			if (querystate == RESQRY_SUCCESS)
464				continue;
465			break;
466		case RESTGT_IGNORE:
467			continue;
468		}
469
470		if ((n = res_nquery(pvt->res, p->qname, p->qclass, p->qtype,
471				    p->answer, p->anslen)) < 0) {
472			querystate = RESQRY_FAIL;
473			continue;
474		}
475
476		memset(&ai, 0, sizeof(ai));
477		ai.ai_family = af;
478		hp = gethostans(this, p->answer, n, p->qname, T_PTR, af, size,
479				NULL, (const struct addrinfo *)&ai);
480		if (!hp) {
481			querystate = RESQRY_FAIL;
482			continue;
483		}
484
485		memcpy(pvt->host_addr, addr, len);
486		pvt->h_addr_ptrs[0] = (char *)pvt->host_addr;
487		pvt->h_addr_ptrs[1] = NULL;
488		if (af == AF_INET && (pvt->res->options & RES_USE_INET6)) {
489			map_v4v6_address((char*)pvt->host_addr,
490					 (char*)pvt->host_addr);
491			pvt->host.h_addrtype = AF_INET6;
492			pvt->host.h_length = IN6ADDRSZ;
493		}
494
495		RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS);
496		goto cleanup;	/*%< no more loop is necessary. */
497	}
498	hp = NULL; /*%< H_ERRNO was set by subroutines */
499 cleanup:
500	if (q != NULL)
501		memput(q, sizeof(*q));
502	if (q2 != NULL)
503		memput(q2, sizeof(*q2));
504	return(hp);
505}
506
507static struct hostent *
508ho_next(struct irs_ho *this) {
509
510	UNUSED(this);
511
512	return (NULL);
513}
514
515static void
516ho_rewind(struct irs_ho *this) {
517
518	UNUSED(this);
519
520	/* NOOP */
521}
522
523static void
524ho_minimize(struct irs_ho *this) {
525	struct pvt *pvt = (struct pvt *)this->private;
526
527	if (pvt->res)
528		res_nclose(pvt->res);
529}
530
531static struct __res_state *
532ho_res_get(struct irs_ho *this) {
533	struct pvt *pvt = (struct pvt *)this->private;
534
535	if (!pvt->res) {
536		struct __res_state *res;
537		res = (struct __res_state *)malloc(sizeof *res);
538		if (!res) {
539			errno = ENOMEM;
540			return (NULL);
541		}
542		memset(res, 0, sizeof *res);
543		ho_res_set(this, res, free);
544	}
545
546	return (pvt->res);
547}
548
549static int
550ipv6_available(void)
551{
552	static int socket6;
553	if (socket6 == 0) {
554		socket6 = socket(AF_INET6, SOCK_DGRAM, 0);
555		if (socket6 >= 0)
556			close(socket6);
557	}
558	return socket6 >= 0;
559}
560
561/* XXX */
562extern struct addrinfo *addr2addrinfo __P((const struct addrinfo *,
563					   const char *));
564
565static struct addrinfo *
566ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai)
567{
568	struct pvt *pvt = (struct pvt *)this->private;
569	int n;
570	char tmp[NS_MAXDNAME];
571	const char *cp;
572	struct dns_res_target *q, *q2, *p;
573	struct addrinfo sentinel, *cur;
574	int querystate = RESQRY_FAIL;
575
576	if (init(this) == -1)
577		return (NULL);
578
579	memset(&sentinel, 0, sizeof(sentinel));
580	cur = &sentinel;
581
582	q = memget(sizeof(*q));
583	q2 = memget(sizeof(*q2));
584	if (q == NULL || q2 == NULL) {
585		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
586		errno = ENOMEM;
587		goto cleanup;
588	}
589	memset(q, 0, sizeof(*q2));
590	memset(q2, 0, sizeof(*q2));
591
592	switch (pai->ai_family) {
593	case AF_UNSPEC:
594		if (ipv6_available()) {
595			/* prefer IPv6 */
596			q->qclass = C_IN;
597			q->qtype = T_AAAA;
598			q->answer = q->qbuf.buf;
599			q->anslen = sizeof(q->qbuf);
600			q->next = q2;
601			q->action = RESTGT_DOALWAYS;
602			q2->qclass = C_IN;
603			q2->qtype = T_A;
604			q2->answer = q2->qbuf.buf;
605			q2->anslen = sizeof(q2->qbuf);
606			q2->action = RESTGT_DOALWAYS;
607			break;
608		}
609		// supposed to fall through
610	case AF_INET:
611		q->qclass = C_IN;
612		q->qtype = T_A;
613		q->answer = q->qbuf.buf;
614		q->anslen = sizeof(q->qbuf);
615		q->action = RESTGT_DOALWAYS;
616		break;
617	case AF_INET6:
618		q->qclass = C_IN;
619		q->qtype = T_AAAA;
620		q->answer = q->qbuf.buf;
621		q->anslen = sizeof(q->qbuf);
622		q->action = RESTGT_DOALWAYS;
623		break;
624	default:
625		RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); /*%< better error? */
626		goto cleanup;
627	}
628
629	/*
630	 * if there aren't any dots, it could be a user-level alias.
631	 * this is also done in res_nquery() since we are not the only
632	 * function that looks up host names.
633	 */
634	if (!strchr(name, '.') && (cp = res_hostalias(pvt->res, name,
635						      tmp, sizeof tmp)))
636		name = cp;
637
638	for (p = q; p; p = p->next) {
639		struct addrinfo *ai;
640
641		switch(p->action) {
642		case RESTGT_DOALWAYS:
643			break;
644		case RESTGT_AFTERFAILURE:
645			if (querystate == RESQRY_SUCCESS)
646				continue;
647			break;
648		case RESTGT_IGNORE:
649			continue;
650		}
651
652		if ((n = res_nsearch(pvt->res, name, p->qclass, p->qtype,
653				     p->answer, p->anslen)) < 0) {
654			querystate = RESQRY_FAIL;
655			continue;
656		}
657		(void)gethostans(this, p->answer, n, name, p->qtype,
658				 pai->ai_family, /*%< XXX: meaningless */
659				 0, &ai, pai);
660		if (ai) {
661			querystate = RESQRY_SUCCESS;
662			cur->ai_next = ai;
663			while (cur->ai_next)
664				cur = cur->ai_next;
665		} else
666			querystate = RESQRY_FAIL;
667	}
668
669 cleanup:
670	if (q != NULL)
671		memput(q, sizeof(*q));
672	if (q2 != NULL)
673		memput(q2, sizeof(*q2));
674	return(sentinel.ai_next);
675}
676
677static void
678ho_res_set(struct irs_ho *this, struct __res_state *res,
679		void (*free_res)(void *)) {
680	struct pvt *pvt = (struct pvt *)this->private;
681
682	if (pvt->res && pvt->free_res) {
683		res_nclose(pvt->res);
684		(*pvt->free_res)(pvt->res);
685	}
686
687	pvt->res = res;
688	pvt->free_res = free_res;
689}
690
691/* Private. */
692
693static struct hostent *
694gethostans(struct irs_ho *this,
695	   const u_char *ansbuf, int anslen, const char *qname, int qtype,
696	   int af, int size,	/*!< meaningless for addrinfo cases  */
697	   struct addrinfo **ret_aip, const struct addrinfo *pai)
698{
699	struct pvt *pvt = (struct pvt *)this->private;
700	int type, class, ancount, qdcount, n, haveanswer, had_error;
701	int error = NETDB_SUCCESS;
702	int (*name_ok)(const char *);
703	const HEADER *hp;
704	const u_char *eom;
705	const u_char *eor;
706	const u_char *cp;
707	const char *tname;
708	const char *hname;
709	char *bp, *ep, **ap, **hap;
710	char tbuf[MAXDNAME+1];
711	struct addrinfo sentinel, *cur, ai;
712
713	if (pai == NULL) abort();
714	if (ret_aip != NULL)
715		*ret_aip = NULL;
716	memset(&sentinel, 0, sizeof(sentinel));
717	cur = &sentinel;
718
719	tname = qname;
720	eom = ansbuf + anslen;
721	switch (qtype) {
722	case T_A:
723	case T_AAAA:
724	case T_ANY:	/*%< use T_ANY only for T_A/T_AAAA lookup */
725		name_ok = res_hnok;
726		break;
727	case T_PTR:
728		name_ok = res_dnok;
729		break;
730	default:
731		abort();
732	}
733
734	pvt->host.h_addrtype = af;
735	pvt->host.h_length = size;
736	hname = pvt->host.h_name = NULL;
737
738	/*
739	 * Find first satisfactory answer.
740	 */
741	if (ansbuf + HFIXEDSZ > eom) {
742		RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
743		return (NULL);
744	}
745	hp = (const HEADER *)ansbuf;
746	ancount = ntohs(hp->ancount);
747	qdcount = ntohs(hp->qdcount);
748	bp = pvt->hostbuf;
749	ep = pvt->hostbuf + sizeof(pvt->hostbuf);
750	cp = ansbuf + HFIXEDSZ;
751	if (qdcount != 1) {
752		RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
753		return (NULL);
754	}
755	n = dn_expand(ansbuf, eom, cp, bp, ep - bp);
756	if (n < 0 || !maybe_ok(pvt->res, bp, name_ok)) {
757		RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
758		return (NULL);
759	}
760	cp += n + QFIXEDSZ;
761	if (cp > eom) {
762		RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
763		return (NULL);
764	}
765	if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) {
766		/* res_nsend() has already verified that the query name is the
767		 * same as the one we sent; this just gets the expanded name
768		 * (i.e., with the succeeding search-domain tacked on).
769		 */
770		n = strlen(bp) + 1;		/*%< for the \\0 */
771		if (n > MAXHOSTNAMELEN) {
772			RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
773			return (NULL);
774		}
775		pvt->host.h_name = bp;
776		hname = bp;
777		bp += n;
778		/* The qname can be abbreviated, but hname is now absolute. */
779		qname = pvt->host.h_name;
780	}
781	ap = pvt->host_aliases;
782	*ap = NULL;
783	pvt->host.h_aliases = pvt->host_aliases;
784	hap = pvt->h_addr_ptrs;
785	*hap = NULL;
786	pvt->host.h_addr_list = pvt->h_addr_ptrs;
787	haveanswer = 0;
788	had_error = 0;
789	while (ancount-- > 0 && cp < eom && !had_error) {
790		n = dn_expand(ansbuf, eom, cp, bp, ep - bp);
791		if (n < 0 || !maybe_ok(pvt->res, bp, name_ok)) {
792			had_error++;
793			continue;
794		}
795		cp += n;			/*%< name */
796		BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ);
797		type = ns_get16(cp);
798		cp += INT16SZ;			/*%< type */
799		class = ns_get16(cp);
800		cp += INT16SZ + INT32SZ;	/*%< class, TTL */
801		n = ns_get16(cp);
802		cp += INT16SZ;			/*%< len */
803		BOUNDS_CHECK(cp, n);
804		if (class != C_IN) {
805			cp += n;
806			continue;
807		}
808		eor = cp + n;
809		if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) &&
810		    type == T_CNAME) {
811			if (haveanswer) {
812				int level = LOG_CRIT;
813#ifdef LOG_SECURITY
814				level |= LOG_SECURITY;
815#endif
816				syslog(level,
817 "gethostans: possible attempt to exploit buffer overflow while looking up %s",
818					*qname ? qname : ".");
819			}
820			n = dn_expand(ansbuf, eor, cp, tbuf, sizeof tbuf);
821			if (n < 0 || !maybe_ok(pvt->res, tbuf, name_ok)) {
822				had_error++;
823				continue;
824			}
825			cp += n;
826			/* Store alias. */
827			if (ap >= &pvt->host_aliases[MAXALIASES-1])
828				continue;
829			*ap++ = bp;
830			n = strlen(bp) + 1;	/*%< for the \\0 */
831			bp += n;
832			/* Get canonical name. */
833			n = strlen(tbuf) + 1;	/*%< for the \\0 */
834			if (n > (ep - bp) || n > MAXHOSTNAMELEN) {
835				had_error++;
836				continue;
837			}
838			strcpy(bp, tbuf);	/* (checked) */
839			pvt->host.h_name = bp;
840			hname = bp;
841			bp += n;
842			continue;
843		}
844		if (qtype == T_PTR && type == T_CNAME) {
845			n = dn_expand(ansbuf, eor, cp, tbuf, sizeof tbuf);
846			if (n < 0 || !maybe_dnok(pvt->res, tbuf)) {
847				had_error++;
848				continue;
849			}
850			cp += n;
851#ifdef RES_USE_DNAME
852			if ((pvt->res->options & RES_USE_DNAME) != 0U)
853#endif
854			{
855				/*
856				 * We may be able to check this regardless
857				 * of the USE_DNAME bit, but we add the check
858				 * for now since the DNAME support is
859				 * experimental.
860				 */
861				if (ns_samename(tname, bp) != 1)
862					continue;
863			}
864			/* Get canonical name. */
865			n = strlen(tbuf) + 1;	/*%< for the \\0 */
866			if (n > (ep - bp)) {
867				had_error++;
868				continue;
869			}
870			strcpy(bp, tbuf);	/* (checked) */
871			tname = bp;
872			bp += n;
873			continue;
874		}
875		if (qtype == T_ANY) {
876			if (!(type == T_A || type == T_AAAA)) {
877				cp += n;
878				continue;
879			}
880		} else if (type != qtype) {
881			cp += n;
882			continue;
883		}
884		switch (type) {
885		case T_PTR:
886			if (ret_aip != NULL) {
887				/* addrinfo never needs T_PTR */
888				cp += n;
889				continue;
890			}
891			if (ns_samename(tname, bp) != 1) {
892				cp += n;
893				continue;
894			}
895			n = dn_expand(ansbuf, eor, cp, bp, ep - bp);
896			if (n < 0 || !maybe_hnok(pvt->res, bp) ||
897			    n >= MAXHOSTNAMELEN) {
898				had_error++;
899				break;
900			}
901			cp += n;
902			if (!haveanswer) {
903				pvt->host.h_name = bp;
904				hname = bp;
905			}
906			else if (ap < &pvt->host_aliases[MAXALIASES-1])
907				*ap++ = bp;
908			else
909				n = -1;
910			if (n != -1) {
911				n = strlen(bp) + 1;	/*%< for the \\0 */
912				bp += n;
913			}
914			break;
915		case T_A:
916		case T_AAAA:
917			if (ns_samename(hname, bp) != 1) {
918				cp += n;
919				continue;
920			}
921			if (type == T_A && n != INADDRSZ) {
922				cp += n;
923				continue;
924			}
925			if (type == T_AAAA && n != IN6ADDRSZ) {
926				cp += n;
927				continue;
928			}
929
930			/* make addrinfo. don't overwrite constant PAI */
931			ai = *pai;
932			ai.ai_family = (type == T_AAAA) ? AF_INET6 : AF_INET;
933			cur->ai_next = addr2addrinfo(
934					(const struct addrinfo *)&ai,
935					(const char *)cp);
936			if (cur->ai_next == NULL)
937				had_error++;
938
939			if (!haveanswer) {
940				int nn;
941
942				nn = strlen(bp) + 1;	/*%< for the \\0 */
943				if (nn >= MAXHOSTNAMELEN) {
944					cp += n;
945					had_error++;
946					continue;
947				}
948				pvt->host.h_name = bp;
949				hname = bp;
950				bp += nn;
951			}
952			/* Ensure alignment. */
953			bp = (char *)(((u_long)bp + (sizeof(align) - 1)) &
954				      ~(sizeof(align) - 1));
955			/* Avoid overflows. */
956			if (bp + n > &pvt->hostbuf[sizeof(pvt->hostbuf) - 1]) {
957				had_error++;
958				continue;
959			}
960			if (ret_aip) { /*%< need addrinfo. keep it. */
961				while (cur->ai_next)
962					cur = cur->ai_next;
963			} else if (cur->ai_next) { /*%< need hostent */
964				struct addrinfo *aip = cur->ai_next;
965
966				for (aip = cur->ai_next; aip;
967				     aip = aip->ai_next) {
968					int m;
969
970					m = add_hostent(pvt, bp, hap, aip);
971					if (m < 0) {
972						had_error++;
973						break;
974					}
975					if (m == 0)
976						continue;
977					if (hap < &pvt->h_addr_ptrs[MAXADDRS])
978						hap++;
979					*hap = NULL;
980					bp += m;
981				}
982
983				freeaddrinfo(cur->ai_next);
984				cur->ai_next = NULL;
985			}
986			cp += n;
987			break;
988		default:
989			abort();
990		}
991		if (!had_error)
992			haveanswer++;
993	}
994	if (haveanswer) {
995		if (ret_aip == NULL) {
996			*ap = NULL;
997			*hap = NULL;
998
999			if (pvt->res->nsort && hap != pvt->h_addr_ptrs &&
1000			    qtype == T_A)
1001				addrsort(pvt->res, pvt->h_addr_ptrs,
1002					 hap - pvt->h_addr_ptrs);
1003			if (pvt->host.h_name == NULL) {
1004				n = strlen(qname) + 1;	/*%< for the \\0 */
1005				if (n > (ep - bp) || n >= MAXHOSTNAMELEN)
1006					goto no_recovery;
1007				strcpy(bp, qname);	/* (checked) */
1008				pvt->host.h_name = bp;
1009				bp += n;
1010			}
1011			if (pvt->res->options & RES_USE_INET6)
1012				map_v4v6_hostent(&pvt->host, &bp, ep);
1013			RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS);
1014			return (&pvt->host);
1015		} else {
1016			if ((pai->ai_flags & AI_CANONNAME) != 0) {
1017				if (pvt->host.h_name == NULL) {
1018					sentinel.ai_next->ai_canonname =
1019						strdup(qname);
1020				}
1021				else {
1022					sentinel.ai_next->ai_canonname =
1023						strdup(pvt->host.h_name);
1024				}
1025			}
1026			*ret_aip = sentinel.ai_next;
1027			return(NULL);
1028		}
1029	}
1030 no_recovery:
1031	if (sentinel.ai_next) {
1032		/* this should be impossible, but check it for safety */
1033		freeaddrinfo(sentinel.ai_next);
1034	}
1035	if (error == NETDB_SUCCESS)
1036		RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
1037	else
1038		RES_SET_H_ERRNO(pvt->res, error);
1039	return(NULL);
1040}
1041
1042static int
1043add_hostent(struct pvt *pvt, char *bp, char **hap, struct addrinfo *ai)
1044{
1045	int addrlen;
1046	char *addrp;
1047	const char **tap;
1048	char *obp = bp;
1049
1050	switch(ai->ai_addr->sa_family) {
1051	case AF_INET6:
1052		addrlen = IN6ADDRSZ;
1053		addrp = (char *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
1054		break;
1055	case AF_INET:
1056		addrlen = INADDRSZ;
1057		addrp = (char *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr;
1058		break;
1059	default:
1060		return(-1);	/*%< abort? */
1061	}
1062
1063	/* Ensure alignment. */
1064	bp = (char *)(((u_long)bp + (sizeof(align) - 1)) &
1065		      ~(sizeof(align) - 1));
1066	/* Avoid overflows. */
1067	if (bp + addrlen > &pvt->hostbuf[sizeof(pvt->hostbuf) - 1])
1068		return(-1);
1069	if (hap >= &pvt->h_addr_ptrs[MAXADDRS])
1070		return(0); /*%< fail, but not treat it as an error. */
1071	/* Suppress duplicates. */
1072	for (tap = (const char **)pvt->h_addr_ptrs;
1073	     *tap != NULL;
1074	     tap++)
1075		if (memcmp(*tap, addrp, addrlen) == 0)
1076			break;
1077	if (*tap != NULL)
1078		return (0);
1079
1080	memcpy(*hap = bp, addrp, addrlen);
1081	return((bp + addrlen) - obp);
1082}
1083
1084static void
1085map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep) {
1086	char **ap;
1087
1088	if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ)
1089		return;
1090	hp->h_addrtype = AF_INET6;
1091	hp->h_length = IN6ADDRSZ;
1092	for (ap = hp->h_addr_list; *ap; ap++) {
1093		int i = (u_long)*bpp % sizeof(align);
1094
1095		if (i != 0)
1096			i = sizeof(align) - i;
1097
1098		if ((ep - *bpp) < (i + IN6ADDRSZ)) {
1099			/* Out of memory.  Truncate address list here. */
1100			*ap = NULL;
1101			return;
1102		}
1103		*bpp += i;
1104		map_v4v6_address(*ap, *bpp);
1105		*ap = *bpp;
1106		*bpp += IN6ADDRSZ;
1107	}
1108}
1109
1110static void
1111addrsort(res_state statp, char **ap, int num) {
1112	int i, j, needsort = 0, aval[MAXADDRS];
1113	char **p;
1114
1115	p = ap;
1116	for (i = 0; i < num; i++, p++) {
1117		for (j = 0 ; (unsigned)j < statp->nsort; j++)
1118			if (statp->sort_list[j].addr.s_addr ==
1119			    (((struct in_addr *)(*p))->s_addr &
1120			     statp->sort_list[j].mask))
1121				break;
1122		aval[i] = j;
1123		if (needsort == 0 && i > 0 && j < aval[i-1])
1124			needsort = i;
1125	}
1126	if (!needsort)
1127		return;
1128
1129	while (needsort < num) {
1130		for (j = needsort - 1; j >= 0; j--) {
1131			if (aval[j] > aval[j+1]) {
1132				char *hp;
1133
1134				i = aval[j];
1135				aval[j] = aval[j+1];
1136				aval[j+1] = i;
1137
1138				hp = ap[j];
1139				ap[j] = ap[j+1];
1140				ap[j+1] = hp;
1141
1142			} else
1143				break;
1144		}
1145		needsort++;
1146	}
1147}
1148
1149static int
1150init(struct irs_ho *this) {
1151	struct pvt *pvt = (struct pvt *)this->private;
1152
1153	if (!pvt->res && !ho_res_get(this))
1154		return (-1);
1155	if (((pvt->res->options & RES_INIT) == 0U) &&
1156	    res_ninit(pvt->res) == -1)
1157		return (-1);
1158	return (0);
1159}
1160