xref: /openbsd-src/lib/libc/asr/getaddrinfo_async.c (revision d1f9129b3751a332ed93634d090cd47f59f5974a)
1*d1f9129bSflorian /*	$OpenBSD: getaddrinfo_async.c,v 1.63 2024/08/21 05:53:10 florian Exp $	*/
2b44da627Seric /*
3b44da627Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4b44da627Seric  *
5b44da627Seric  * Permission to use, copy, modify, and distribute this software for any
6b44da627Seric  * purpose with or without fee is hereby granted, provided that the above
7b44da627Seric  * copyright notice and this permission notice appear in all copies.
8b44da627Seric  *
9b44da627Seric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b44da627Seric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b44da627Seric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b44da627Seric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b44da627Seric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b44da627Seric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b44da627Seric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b44da627Seric  */
1780f48568Seric 
18b44da627Seric #include <sys/types.h>
19d216d6b1Seric #include <sys/socket.h>
20b44da627Seric #include <sys/uio.h>
210af765e7Sderaadt #include <netinet/in.h>
22b44da627Seric #include <arpa/nameser.h>
2320149d17Ssperreault #include <net/if.h>
24d216d6b1Seric #include <netdb.h>
25b44da627Seric 
26d216d6b1Seric #include <asr.h>
2780f48568Seric #include <errno.h>
2820149d17Ssperreault #include <ifaddrs.h>
298f32d9f2Sjca #include <resolv.h>
3080f48568Seric #include <stdlib.h>
3180f48568Seric #include <string.h>
3280f48568Seric #include <unistd.h>
33e7e445a1Sderaadt #include <limits.h>
34e7e445a1Sderaadt 
35b44da627Seric #include "asr_private.h"
36b44da627Seric 
37b44da627Seric struct match {
38b44da627Seric 	int family;
39b44da627Seric 	int socktype;
40b44da627Seric 	int protocol;
41b44da627Seric };
42b44da627Seric 
435be03f8fSeric static int getaddrinfo_async_run(struct asr_query *, struct asr_result *);
44b44da627Seric static int get_port(const char *, const char *, int);
455be03f8fSeric static int iter_family(struct asr_query *, int);
465be03f8fSeric static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *);
475be03f8fSeric static int addrinfo_from_file(struct asr_query *, int,  FILE *);
485be03f8fSeric static int addrinfo_from_pkt(struct asr_query *, char *, size_t);
499c0437c0Sjca static int addrconfig_setup(struct asr_query *);
50b44da627Seric 
51b44da627Seric static const struct match matches[] = {
52b44da627Seric 	{ PF_INET,	SOCK_DGRAM,	IPPROTO_UDP	},
53b44da627Seric 	{ PF_INET,	SOCK_STREAM,	IPPROTO_TCP	},
54b44da627Seric 	{ PF_INET,	SOCK_RAW,	0		},
55b44da627Seric 	{ PF_INET6,	SOCK_DGRAM,	IPPROTO_UDP	},
56b44da627Seric 	{ PF_INET6,	SOCK_STREAM,	IPPROTO_TCP	},
57b44da627Seric 	{ PF_INET6,	SOCK_RAW,	0		},
58b44da627Seric 	{ -1,		0,		0,		},
59b44da627Seric };
60b44da627Seric 
61b44da627Seric #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC)
6299210259Seric #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0)
63c43131adSkrw /* Do not match SOCK_RAW unless explicitly specified */
64b44da627Seric #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \
65b44da627Seric 				matches[(b)].socktype != SOCK_RAW))
66b44da627Seric 
673e41c455Seric enum {
683e41c455Seric 	DOM_INIT,
693e41c455Seric 	DOM_DOMAIN,
703e41c455Seric 	DOM_DONE
713e41c455Seric };
723e41c455Seric 
735be03f8fSeric struct asr_query *
74b44da627Seric getaddrinfo_async(const char *hostname, const char *servname,
755be03f8fSeric 	const struct addrinfo *hints, void *asr)
76b44da627Seric {
77b44da627Seric 	struct asr_ctx		*ac;
785be03f8fSeric 	struct asr_query	*as;
79b44da627Seric 
809936a0e9Seric 	if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0)
81253ef892Sderaadt 		ac = _asr_use_resolver(asr);
82656b8d51Sderaadt 	else
83656b8d51Sderaadt 		ac = _asr_no_resolver();
84253ef892Sderaadt 	if ((as = _asr_async_new(ac, ASR_GETADDRINFO)) == NULL)
85b44da627Seric 		goto abort; /* errno set */
86b44da627Seric 	as->as_run = getaddrinfo_async_run;
87b44da627Seric 
8820f7c2a3Seric 	if (hostname) {
8920f7c2a3Seric 		if ((as->as.ai.hostname = strdup(hostname)) == NULL)
90b44da627Seric 			goto abort; /* errno set */
9120f7c2a3Seric 	}
92b44da627Seric 	if (servname && (as->as.ai.servname = strdup(servname)) == NULL)
93b44da627Seric 		goto abort; /* errno set */
94b44da627Seric 	if (hints)
95b44da627Seric 		memmove(&as->as.ai.hints, hints, sizeof *hints);
96b44da627Seric 	else {
97b44da627Seric 		memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints);
98b44da627Seric 		as->as.ai.hints.ai_family = PF_UNSPEC;
9920149d17Ssperreault 		as->as.ai.hints.ai_flags = AI_ADDRCONFIG;
100b44da627Seric 	}
101b44da627Seric 
102253ef892Sderaadt 	_asr_ctx_unref(ac);
103b44da627Seric 	return (as);
104b44da627Seric     abort:
105b44da627Seric 	if (as)
106253ef892Sderaadt 		_asr_async_free(as);
107253ef892Sderaadt 	_asr_ctx_unref(ac);
108b44da627Seric 	return (NULL);
109b44da627Seric }
1105826fd8cSguenther DEF_WEAK(getaddrinfo_async);
111b44da627Seric 
112b44da627Seric static int
1135be03f8fSeric getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar)
114b44da627Seric {
1153e41c455Seric 	char		 fqdn[MAXDNAME];
116b44da627Seric 	const char	*str;
117b44da627Seric 	struct addrinfo	*ai;
118fada2b0bSflorian 	int		 i, family, r, is_localhost = 0;
119c5c8c49bSeric 	FILE		*f;
120b44da627Seric 	union {
121b44da627Seric 		struct sockaddr		sa;
122b44da627Seric 		struct sockaddr_in	sain;
123b44da627Seric 		struct sockaddr_in6	sain6;
124b44da627Seric 	} sa;
125b44da627Seric 
126b44da627Seric     next:
127b44da627Seric 	switch (as->as_state) {
128b44da627Seric 
129b44da627Seric 	case ASR_STATE_INIT:
130b44da627Seric 
131b44da627Seric 		/*
132b44da627Seric 		 * First, make sure the parameters are valid.
133b44da627Seric 		 */
134b44da627Seric 
135b44da627Seric 		as->as_count = 0;
136b44da627Seric 
137b44da627Seric 		if (as->as.ai.hostname == NULL &&
138b44da627Seric 		    as->as.ai.servname == NULL) {
139b44da627Seric 			ar->ar_gai_errno = EAI_NONAME;
1404a508230Seric 			async_set_state(as, ASR_STATE_HALT);
141b44da627Seric 			break;
142b44da627Seric 		}
143b44da627Seric 
144ab50be5eSeric 		if (as->as.ai.hostname && as->as.ai.hostname[0] == '\0') {
145ab50be5eSeric 			ar->ar_gai_errno = EAI_NODATA;
146ab50be5eSeric 			async_set_state(as, ASR_STATE_HALT);
147ab50be5eSeric 			break;
148ab50be5eSeric 		}
149ab50be5eSeric 
150b44da627Seric 		ai = &as->as.ai.hints;
151b44da627Seric 
152b44da627Seric 		if (ai->ai_addrlen ||
153b44da627Seric 		    ai->ai_canonname ||
154b44da627Seric 		    ai->ai_addr ||
155b44da627Seric 		    ai->ai_next) {
156b44da627Seric 			ar->ar_gai_errno = EAI_BADHINTS;
1574a508230Seric 			async_set_state(as, ASR_STATE_HALT);
158b44da627Seric 			break;
159b44da627Seric 		}
160b44da627Seric 
161b44da627Seric 		if (ai->ai_flags & ~AI_MASK ||
162b44da627Seric 		    (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) {
163b44da627Seric 			ar->ar_gai_errno = EAI_BADFLAGS;
1644a508230Seric 			async_set_state(as, ASR_STATE_HALT);
165b44da627Seric 			break;
166b44da627Seric 		}
167b44da627Seric 
168b44da627Seric 		if (ai->ai_family != PF_UNSPEC &&
169b44da627Seric 		    ai->ai_family != PF_INET &&
170b44da627Seric 		    ai->ai_family != PF_INET6) {
171b44da627Seric 			ar->ar_gai_errno = EAI_FAMILY;
1724a508230Seric 			async_set_state(as, ASR_STATE_HALT);
173b44da627Seric 			break;
174b44da627Seric 		}
175b44da627Seric 
176b44da627Seric 		if (ai->ai_socktype &&
177b44da627Seric 		    ai->ai_socktype != SOCK_DGRAM  &&
178b44da627Seric 		    ai->ai_socktype != SOCK_STREAM &&
179b44da627Seric 		    ai->ai_socktype != SOCK_RAW) {
180b44da627Seric 			ar->ar_gai_errno = EAI_SOCKTYPE;
1814a508230Seric 			async_set_state(as, ASR_STATE_HALT);
182b44da627Seric 			break;
183b44da627Seric 		}
184b44da627Seric 
185b44da627Seric 		if (ai->ai_socktype == SOCK_RAW &&
1863d13cefaSeric 		    get_port(as->as.ai.servname, NULL, 1) != 0) {
187b44da627Seric 			ar->ar_gai_errno = EAI_SERVICE;
1884a508230Seric 			async_set_state(as, ASR_STATE_HALT);
189b44da627Seric 			break;
190b44da627Seric 		}
191b44da627Seric 
19220149d17Ssperreault 		/* Restrict result set to configured address families */
19320149d17Ssperreault 		if (ai->ai_flags & AI_ADDRCONFIG) {
194cbd3ae78Seric 			if (addrconfig_setup(as) == -1) {
195cbd3ae78Seric 				ar->ar_errno = errno;
196cbd3ae78Seric 				ar->ar_gai_errno = EAI_SYSTEM;
19720149d17Ssperreault 				async_set_state(as, ASR_STATE_HALT);
19820149d17Ssperreault 				break;
19920149d17Ssperreault 			}
20020149d17Ssperreault 		}
20120149d17Ssperreault 
202b44da627Seric 		/* Make sure there is at least a valid combination */
203b44da627Seric 		for (i = 0; matches[i].family != -1; i++)
204b44da627Seric 			if (MATCH_FAMILY(ai->ai_family, i) &&
205b44da627Seric 			    MATCH_SOCKTYPE(ai->ai_socktype, i) &&
206b44da627Seric 			    MATCH_PROTO(ai->ai_protocol, i))
207b44da627Seric 				break;
208b44da627Seric 		if (matches[i].family == -1) {
209b44da627Seric 			ar->ar_gai_errno = EAI_BADHINTS;
2104a508230Seric 			async_set_state(as, ASR_STATE_HALT);
211b44da627Seric 			break;
212b44da627Seric 		}
213b44da627Seric 
21448756ee8Seric 		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP)
21548756ee8Seric 			as->as.ai.port_udp = get_port(as->as.ai.servname, "udp",
21648756ee8Seric 			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
21748756ee8Seric 		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP)
21848756ee8Seric 			as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp",
21948756ee8Seric 			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
22048756ee8Seric 		if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 ||
2218b2098d5Seric 		    (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) ||
2228b2098d5Seric 		    (ai->ai_protocol && (as->as.ai.port_udp == -1 ||
2238b2098d5Seric 					 as->as.ai.port_tcp == -1))) {
224b44da627Seric 			ar->ar_gai_errno = EAI_SERVICE;
2254a508230Seric 			async_set_state(as, ASR_STATE_HALT);
226b44da627Seric 			break;
227b44da627Seric 		}
228b44da627Seric 
2294a508230Seric 		ar->ar_gai_errno = 0;
2304a508230Seric 
231fada2b0bSflorian 		if (!(ai->ai_flags & AI_NUMERICHOST))
232373da8abSflorian 			is_localhost = _asr_is_localhost(as->as.ai.hostname);
233373da8abSflorian 		/*
234373da8abSflorian 		 * If hostname is NULL, "localhost" or falls within the
235373da8abSflorian 		 * ".localhost." domain, use local address.
236373da8abSflorian 		 * RFC 6761, 6.3:
237373da8abSflorian 		 * 3. Name resolution APIs and libraries SHOULD recognize
238373da8abSflorian 		 * localhost names as special and SHOULD always return the IP
239373da8abSflorian 		 * loopback address for address queries and negative responses
240373da8abSflorian 		 * for all other query types.  Name resolution APIs SHOULD NOT
241373da8abSflorian 		 * send queries for localhost names to their configured caching
242373da8abSflorian 		 * DNS server(s).
243373da8abSflorian 		 */
244373da8abSflorian 		if (as->as.ai.hostname == NULL || is_localhost) {
245b44da627Seric 			for (family = iter_family(as, 1);
246b44da627Seric 			    family != -1;
247b44da627Seric 			    family = iter_family(as, 0)) {
248b44da627Seric 				/*
249b44da627Seric 				 * We could use statically built sockaddrs for
250b44da627Seric 				 * those, rather than parsing over and over.
251b44da627Seric 				 */
252b44da627Seric 				if (family == PF_INET)
253373da8abSflorian 					str = (ai->ai_flags & AI_PASSIVE &&
254373da8abSflorian 					    !is_localhost) ? "0.0.0.0" :
255373da8abSflorian 					    "127.0.0.1";
256b44da627Seric 				else /* PF_INET6 */
257373da8abSflorian 					str = (ai->ai_flags & AI_PASSIVE &&
258373da8abSflorian 					    !is_localhost) ? "::" : "::1";
259b44da627Seric 				 /* This can't fail */
260253ef892Sderaadt 				_asr_sockaddr_from_str(&sa.sa, family, str);
261ab51fa82Sflorian 				if ((r = addrinfo_add(as, &sa.sa,
262ab51fa82Sflorian 				    "localhost."))) {
263b44da627Seric 					ar->ar_gai_errno = r;
264b44da627Seric 					break;
265b44da627Seric 				}
266b44da627Seric 			}
267b44da627Seric 			if (ar->ar_gai_errno == 0 && as->as_count == 0) {
268b44da627Seric 				ar->ar_gai_errno = EAI_NODATA;
269b44da627Seric 			}
2704a508230Seric 			async_set_state(as, ASR_STATE_HALT);
271b44da627Seric 			break;
272b44da627Seric 		}
273b44da627Seric 
274b44da627Seric 		/* Try numeric addresses first */
275b44da627Seric 		for (family = iter_family(as, 1);
276b44da627Seric 		    family != -1;
277b44da627Seric 		    family = iter_family(as, 0)) {
278b44da627Seric 
279253ef892Sderaadt 			if (_asr_sockaddr_from_str(&sa.sa, family,
280b44da627Seric 			    as->as.ai.hostname) == -1)
281b44da627Seric 				continue;
282b44da627Seric 
283*d1f9129bSflorian 			if ((r = addrinfo_add(as, &sa.sa, as->as.ai.hostname)))
284b44da627Seric 				ar->ar_gai_errno = r;
2854a508230Seric 			break;
2864a508230Seric 		}
2874a508230Seric 		if (ar->ar_gai_errno || as->as_count) {
288b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
289b44da627Seric 			break;
290b44da627Seric 		}
291b44da627Seric 
292b44da627Seric 		if (ai->ai_flags & AI_NUMERICHOST) {
293ff1bfb95Schrisz 			ar->ar_gai_errno = EAI_NONAME;
294b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
295b44da627Seric 			break;
296b44da627Seric 		}
297b44da627Seric 
2981b04c78cSflorian 		/* make sure there are no funny characters in hostname */
2991b04c78cSflorian 		if (!hnok_lenient(as->as.ai.hostname)) {
3001b04c78cSflorian 			ar->ar_gai_errno = EAI_FAIL;
3011b04c78cSflorian 			async_set_state(as, ASR_STATE_HALT);
3021b04c78cSflorian 			break;
3031b04c78cSflorian 		}
3041b04c78cSflorian 
305c5c8c49bSeric 		async_set_state(as, ASR_STATE_NEXT_DB);
306c5c8c49bSeric 		break;
307b44da627Seric 
308c5c8c49bSeric 	case ASR_STATE_NEXT_DB:
309253ef892Sderaadt 		if (_asr_iter_db(as) == -1) {
3101e1dfc0cSeric 			async_set_state(as, ASR_STATE_NOT_FOUND);
311c5c8c49bSeric 			break;
312c5c8c49bSeric 		}
313c5c8c49bSeric 		as->as_family_idx = 0;
314c5c8c49bSeric 		async_set_state(as, ASR_STATE_SAME_DB);
315c5c8c49bSeric 		break;
316c5c8c49bSeric 
317c5c8c49bSeric 	case ASR_STATE_NEXT_FAMILY:
318c5c8c49bSeric 		as->as_family_idx += 1;
319c5c8c49bSeric 		if (as->as.ai.hints.ai_family != AF_UNSPEC ||
320c5c8c49bSeric 		    AS_FAMILY(as) == -1) {
321c5c8c49bSeric 			/* The family was specified, or we have tried all
322c5c8c49bSeric 			 * families with this DB.
323b44da627Seric 			 */
324c5c8c49bSeric 			if (as->as_count) {
325c5c8c49bSeric 				ar->ar_gai_errno = 0;
326c5c8c49bSeric 				async_set_state(as, ASR_STATE_HALT);
327c5c8c49bSeric 			} else
3283e41c455Seric 				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
3293e41c455Seric 			break;
3303e41c455Seric 		}
3313e41c455Seric 		async_set_state(as, ASR_STATE_SAME_DB);
3323e41c455Seric 		break;
3333e41c455Seric 
3343e41c455Seric 	case ASR_STATE_NEXT_DOMAIN:
3353e41c455Seric 		/* domain search is only for dns */
3363e41c455Seric 		if (AS_DB(as) != ASR_DB_DNS) {
337c5c8c49bSeric 			async_set_state(as, ASR_STATE_NEXT_DB);
338c5c8c49bSeric 			break;
339c5c8c49bSeric 		}
3403e41c455Seric 		as->as_family_idx = 0;
3413e41c455Seric 
3423e41c455Seric 		free(as->as.ai.fqdn);
3433e41c455Seric 		as->as.ai.fqdn = NULL;
344253ef892Sderaadt 		r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn));
3453e41c455Seric 		if (r == -1) {
3463e41c455Seric 			async_set_state(as, ASR_STATE_NEXT_DB);
3473e41c455Seric 			break;
3483e41c455Seric 		}
3493e41c455Seric 		if (r == 0) {
3503e41c455Seric 			ar->ar_gai_errno = EAI_FAIL;
3513e41c455Seric 			async_set_state(as, ASR_STATE_HALT);
3523e41c455Seric 			break;
3533e41c455Seric 		}
3543e41c455Seric 		as->as.ai.fqdn = strdup(fqdn);
3553e41c455Seric 		if (as->as.ai.fqdn == NULL) {
3563e41c455Seric 			ar->ar_gai_errno = EAI_MEMORY;
3573e41c455Seric 			async_set_state(as, ASR_STATE_HALT);
3586c455d5cSeric 			break;
3593e41c455Seric 		}
3603e41c455Seric 
361c5c8c49bSeric 		async_set_state(as, ASR_STATE_SAME_DB);
362c5c8c49bSeric 		break;
363c5c8c49bSeric 
364c5c8c49bSeric 	case ASR_STATE_SAME_DB:
3651e1dfc0cSeric 		/* query the current DB again */
366c5c8c49bSeric 		switch (AS_DB(as)) {
367c5c8c49bSeric 		case ASR_DB_DNS:
3683e41c455Seric 			if (as->as.ai.fqdn == NULL) {
3693e41c455Seric 				/* First try, initialize domain iteration */
3703e41c455Seric 				as->as_dom_flags = 0;
3713e41c455Seric 				as->as_dom_step = DOM_INIT;
3723e41c455Seric 				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
3733e41c455Seric 				break;
3743e41c455Seric 			}
3753e41c455Seric 
376c5c8c49bSeric 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
377c5c8c49bSeric 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
3783e41c455Seric 
3798b59b78cSjca 			if (family == AF_INET &&
380abe78e02Sjca 			    as->as_flags & ASYNC_NO_INET) {
3818b59b78cSjca 				async_set_state(as, ASR_STATE_NEXT_FAMILY);
3828b59b78cSjca 				break;
3838b59b78cSjca 			} else if (family == AF_INET6 &&
384abe78e02Sjca 			    as->as_flags & ASYNC_NO_INET6) {
3858b59b78cSjca 				async_set_state(as, ASR_STATE_NEXT_FAMILY);
3868b59b78cSjca 				break;
3878b59b78cSjca 			}
3888b59b78cSjca 
389f6f51dadSeric 			as->as_subq = _res_query_async_ctx(as->as.ai.fqdn,
3903e41c455Seric 			    C_IN, (family == AF_INET6) ? T_AAAA : T_A,
391b44da627Seric 			    as->as_ctx);
3923e41c455Seric 
393f6f51dadSeric 			if (as->as_subq == NULL) {
394c5c8c49bSeric 				if (errno == ENOMEM)
395b44da627Seric 					ar->ar_gai_errno = EAI_MEMORY;
396c5c8c49bSeric 				else
397c5c8c49bSeric 					ar->ar_gai_errno = EAI_FAIL;
398b44da627Seric 				async_set_state(as, ASR_STATE_HALT);
399b44da627Seric 				break;
400b44da627Seric 			}
401c5c8c49bSeric 			async_set_state(as, ASR_STATE_SUBQUERY);
402b44da627Seric 			break;
403b44da627Seric 
404c5c8c49bSeric 		case ASR_DB_FILE:
405d2d7f9c9Seric 			f = fopen(_PATH_HOSTS, "re");
406c5c8c49bSeric 			if (f == NULL) {
407c5c8c49bSeric 				async_set_state(as, ASR_STATE_NEXT_DB);
408c5c8c49bSeric 				break;
409c5c8c49bSeric 			}
410c5c8c49bSeric 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
411c5c8c49bSeric 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
412b44da627Seric 
413c5c8c49bSeric 			r = addrinfo_from_file(as, family, f);
414c5c8c49bSeric 			if (r == -1) {
415c5c8c49bSeric 				if (errno == ENOMEM)
416c5c8c49bSeric 					ar->ar_gai_errno = EAI_MEMORY;
417c5c8c49bSeric 				else
418c5c8c49bSeric 					ar->ar_gai_errno = EAI_FAIL;
419c5c8c49bSeric 				async_set_state(as, ASR_STATE_HALT);
420c5c8c49bSeric 			} else
421c5c8c49bSeric 				async_set_state(as, ASR_STATE_NEXT_FAMILY);
422c5c8c49bSeric 			fclose(f);
423c5c8c49bSeric 			break;
424c5c8c49bSeric 
425c5c8c49bSeric 		default:
426c5c8c49bSeric 			async_set_state(as, ASR_STATE_NEXT_DB);
427c5c8c49bSeric 		}
428c5c8c49bSeric 		break;
429c5c8c49bSeric 
430c5c8c49bSeric 	case ASR_STATE_SUBQUERY:
431f6f51dadSeric 		if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
432b44da627Seric 			return (ASYNC_COND);
4335be03f8fSeric 
434f6f51dadSeric 		as->as_subq = NULL;
435c5c8c49bSeric 
436c5c8c49bSeric 		if (ar->ar_datalen == -1) {
4371e1dfc0cSeric 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
438b44da627Seric 			break;
439d95d6a55Seric 		}
440d95d6a55Seric 
441c5c8c49bSeric 		r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen);
442c5c8c49bSeric 		if (r == -1) {
443c5c8c49bSeric 			if (errno == ENOMEM)
444c5c8c49bSeric 				ar->ar_gai_errno = EAI_MEMORY;
445c5c8c49bSeric 			else
446c5c8c49bSeric 				ar->ar_gai_errno = EAI_FAIL;
447d95d6a55Seric 			async_set_state(as, ASR_STATE_HALT);
448c5c8c49bSeric 		} else
449c5c8c49bSeric 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
450c5c8c49bSeric 		free(ar->ar_data);
451d95d6a55Seric 		break;
452b44da627Seric 
453b44da627Seric 	case ASR_STATE_NOT_FOUND:
454c5c8c49bSeric 		/* No result found. Maybe we can try again. */
455abe78e02Sjca 		if (as->as_flags & ASYNC_AGAIN)
456b44da627Seric 			ar->ar_gai_errno = EAI_AGAIN;
457c5c8c49bSeric 		else
458b44da627Seric 			ar->ar_gai_errno = EAI_NODATA;
459b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
460b44da627Seric 		break;
461b44da627Seric 
462b44da627Seric 	case ASR_STATE_HALT:
463b44da627Seric 		if (ar->ar_gai_errno == 0) {
464b44da627Seric 			ar->ar_count = as->as_count;
465b44da627Seric 			ar->ar_addrinfo = as->as.ai.aifirst;
466b44da627Seric 			as->as.ai.aifirst = NULL;
467b44da627Seric 		} else {
468b44da627Seric 			ar->ar_count = 0;
469b44da627Seric 			ar->ar_addrinfo = NULL;
470b44da627Seric 		}
471b44da627Seric 		return (ASYNC_DONE);
472b44da627Seric 
473b44da627Seric 	default:
474b44da627Seric 		ar->ar_errno = EOPNOTSUPP;
475b44da627Seric 		ar->ar_gai_errno = EAI_SYSTEM;
476b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
477b44da627Seric 		break;
478b44da627Seric 	}
479b44da627Seric 	goto next;
480b44da627Seric }
481b44da627Seric 
482b44da627Seric /*
4832c53affbSjmc  * Retrieve the port number for the service name "servname" and
484b44da627Seric  * the protocol "proto".
485b44da627Seric  */
486b44da627Seric static int
487b44da627Seric get_port(const char *servname, const char *proto, int numonly)
488b44da627Seric {
489b44da627Seric 	struct servent		se;
490b44da627Seric 	struct servent_data	sed;
491172d89a7Seric 	int			port;
492b44da627Seric 	const char		*e;
493b44da627Seric 
494b44da627Seric 	if (servname == NULL)
495b44da627Seric 		return (0);
496b44da627Seric 
497b44da627Seric 	e = NULL;
498b44da627Seric 	port = strtonum(servname, 0, USHRT_MAX, &e);
499b44da627Seric 	if (e == NULL)
500b44da627Seric 		return (port);
501b44da627Seric 	if (errno == ERANGE)
502b44da627Seric 		return (-2); /* invalid */
503b44da627Seric 	if (numonly)
504b44da627Seric 		return (-2);
505b44da627Seric 
506172d89a7Seric 	port = -1;
507b44da627Seric 	memset(&sed, 0, sizeof(sed));
508172d89a7Seric 	if (getservbyname_r(servname, proto, &se, &sed) != -1)
509b44da627Seric 		port = ntohs(se.s_port);
510b44da627Seric 	endservent_r(&sed);
511b44da627Seric 
512b44da627Seric 	return (port);
513b44da627Seric }
514b44da627Seric 
515b44da627Seric /*
516b44da627Seric  * Iterate over the address families that are to be queried. Use the
517b44da627Seric  * list on the async context, unless a specific family was given in hints.
518b44da627Seric  */
519b44da627Seric static int
5205be03f8fSeric iter_family(struct asr_query *as, int first)
521b44da627Seric {
522b44da627Seric 	if (first) {
523b44da627Seric 		as->as_family_idx = 0;
524b44da627Seric 		if (as->as.ai.hints.ai_family != PF_UNSPEC)
525b44da627Seric 			return as->as.ai.hints.ai_family;
526b44da627Seric 		return AS_FAMILY(as);
527b44da627Seric 	}
528b44da627Seric 
529b44da627Seric 	if (as->as.ai.hints.ai_family != PF_UNSPEC)
530b44da627Seric 		return (-1);
531b44da627Seric 
532b44da627Seric 	as->as_family_idx++;
533b44da627Seric 
534b44da627Seric 	return AS_FAMILY(as);
535b44da627Seric }
536b44da627Seric 
537b44da627Seric /*
538b44da627Seric  * Use the sockaddr at "sa" to extend the result list on the "as" context,
539b44da627Seric  * with the specified canonical name "cname". This function adds one
540b44da627Seric  * entry per protocol/socktype match.
541b44da627Seric  */
542b44da627Seric static int
5435be03f8fSeric addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname)
544b44da627Seric {
545b44da627Seric 	struct addrinfo		*ai;
54699210259Seric 	int			 i, port, proto;
547b44da627Seric 
548b44da627Seric 	for (i = 0; matches[i].family != -1; i++) {
549b44da627Seric 		if (matches[i].family != sa->sa_family ||
550b44da627Seric 		    !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) ||
551b44da627Seric 		    !MATCH_PROTO(as->as.ai.hints.ai_protocol, i))
552b44da627Seric 			continue;
553b44da627Seric 
55499210259Seric 		proto = as->as.ai.hints.ai_protocol;
55599210259Seric 		if (!proto)
55699210259Seric 			proto = matches[i].protocol;
55799210259Seric 
55899210259Seric 		if (proto == IPPROTO_TCP)
559b44da627Seric 			port = as->as.ai.port_tcp;
56099210259Seric 		else if (proto == IPPROTO_UDP)
561b44da627Seric 			port = as->as.ai.port_udp;
562b44da627Seric 		else
563b44da627Seric 			port = 0;
564b44da627Seric 
56548756ee8Seric 		/* servname specified, but not defined for this protocol */
56648756ee8Seric 		if (port == -1)
56748756ee8Seric 			continue;
56848756ee8Seric 
569b44da627Seric 		ai = calloc(1, sizeof(*ai) + sa->sa_len);
570b44da627Seric 		if (ai == NULL)
571b44da627Seric 			return (EAI_MEMORY);
572b44da627Seric 		ai->ai_family = sa->sa_family;
573b44da627Seric 		ai->ai_socktype = matches[i].socktype;
57499210259Seric 		ai->ai_protocol = proto;
575024ee4aaSeric 		ai->ai_flags = as->as.ai.hints.ai_flags;
576b44da627Seric 		ai->ai_addrlen = sa->sa_len;
577b44da627Seric 		ai->ai_addr = (void *)(ai + 1);
578b44da627Seric 		if (cname &&
579b44da627Seric 		    as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) {
580b44da627Seric 			if ((ai->ai_canonname = strdup(cname)) == NULL) {
581b44da627Seric 				free(ai);
582b44da627Seric 				return (EAI_MEMORY);
583b44da627Seric 			}
584b44da627Seric 		}
585b44da627Seric 		memmove(ai->ai_addr, sa, sa->sa_len);
586b44da627Seric 		if (sa->sa_family == PF_INET)
587b44da627Seric 			((struct sockaddr_in *)ai->ai_addr)->sin_port =
588b44da627Seric 			    htons(port);
589b44da627Seric 		else if (sa->sa_family == PF_INET6)
590b44da627Seric 			((struct sockaddr_in6 *)ai->ai_addr)->sin6_port =
591b44da627Seric 			    htons(port);
592b44da627Seric 
593b44da627Seric 		if (as->as.ai.aifirst == NULL)
594b44da627Seric 			as->as.ai.aifirst = ai;
595b44da627Seric 		if (as->as.ai.ailast)
596b44da627Seric 			as->as.ai.ailast->ai_next = ai;
597b44da627Seric 		as->as.ai.ailast = ai;
598b44da627Seric 		as->as_count += 1;
599b44da627Seric 	}
600b44da627Seric 
601b44da627Seric 	return (0);
602b44da627Seric }
603c5c8c49bSeric 
604c5c8c49bSeric static int
6055be03f8fSeric addrinfo_from_file(struct asr_query *as, int family, FILE *f)
606c5c8c49bSeric {
607f108579bSeric 	char		*tokens[MAXTOKEN], *c, buf[BUFSIZ + 1];
608c5c8c49bSeric 	int		 n, i;
609c5c8c49bSeric 	union {
610c5c8c49bSeric 		struct sockaddr		sa;
611c5c8c49bSeric 		struct sockaddr_in	sain;
612c5c8c49bSeric 		struct sockaddr_in6	sain6;
613c5c8c49bSeric 	} u;
614c5c8c49bSeric 
615c5c8c49bSeric 	for (;;) {
616253ef892Sderaadt 		n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf));
617c5c8c49bSeric 		if (n == -1)
618c5c8c49bSeric 			break; /* ignore errors reading the file */
619c5c8c49bSeric 
620c5c8c49bSeric 		for (i = 1; i < n; i++) {
62120f7c2a3Seric 			if (strcasecmp(as->as.ai.hostname, tokens[i]))
622c5c8c49bSeric 				continue;
623253ef892Sderaadt 			if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
624c5c8c49bSeric 				continue;
625c5c8c49bSeric 			break;
626c5c8c49bSeric 		}
627c5c8c49bSeric 		if (i == n)
628c5c8c49bSeric 			continue;
629c5c8c49bSeric 
6301e1dfc0cSeric 		if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
631c5c8c49bSeric 			c = tokens[1];
632c5c8c49bSeric 		else
633c5c8c49bSeric 			c = NULL;
634c5c8c49bSeric 
635c5c8c49bSeric 		if (addrinfo_add(as, &u.sa, c))
636c5c8c49bSeric 			return (-1); /* errno set */
637c5c8c49bSeric 	}
638c5c8c49bSeric 	return (0);
639c5c8c49bSeric }
640c5c8c49bSeric 
641c5c8c49bSeric static int
6425be03f8fSeric addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen)
643c5c8c49bSeric {
644f90bf415Seric 	struct asr_unpack	 p;
645f90bf415Seric 	struct asr_dns_header	 h;
646f90bf415Seric 	struct asr_dns_query	 q;
647f90bf415Seric 	struct asr_dns_rr	 rr;
648c5c8c49bSeric 	int			 i;
649c5c8c49bSeric 	union {
650c5c8c49bSeric 		struct sockaddr		sa;
651c5c8c49bSeric 		struct sockaddr_in	sain;
652c5c8c49bSeric 		struct sockaddr_in6	sain6;
653c5c8c49bSeric 	} u;
654c5c8c49bSeric 	char		 buf[MAXDNAME], *c;
655c5c8c49bSeric 
656253ef892Sderaadt 	_asr_unpack_init(&p, pkt, pktlen);
657253ef892Sderaadt 	_asr_unpack_header(&p, &h);
658c5c8c49bSeric 	for (; h.qdcount; h.qdcount--)
659253ef892Sderaadt 		_asr_unpack_query(&p, &q);
660c5c8c49bSeric 
661c5c8c49bSeric 	for (i = 0; i < h.ancount; i++) {
662253ef892Sderaadt 		_asr_unpack_rr(&p, &rr);
663c5c8c49bSeric 		if (rr.rr_type != q.q_type ||
664c5c8c49bSeric 		    rr.rr_class != q.q_class)
665c5c8c49bSeric 			continue;
666c5c8c49bSeric 
667c5c8c49bSeric 		memset(&u, 0, sizeof u);
668c5c8c49bSeric 		if (rr.rr_type == T_A) {
669c5c8c49bSeric 			u.sain.sin_len = sizeof u.sain;
670c5c8c49bSeric 			u.sain.sin_family = AF_INET;
671c5c8c49bSeric 			u.sain.sin_addr = rr.rr.in_a.addr;
672c5c8c49bSeric 			u.sain.sin_port = 0;
673c5c8c49bSeric 		} else if (rr.rr_type == T_AAAA) {
674c5c8c49bSeric 			u.sain6.sin6_len = sizeof u.sain6;
675c5c8c49bSeric 			u.sain6.sin6_family = AF_INET6;
676c5c8c49bSeric 			u.sain6.sin6_addr = rr.rr.in_aaaa.addr6;
677c5c8c49bSeric 			u.sain6.sin6_port = 0;
678c5c8c49bSeric 		} else
679c5c8c49bSeric 			continue;
680c5c8c49bSeric 
681c5c8c49bSeric 		if (as->as.ai.hints.ai_flags & AI_CANONNAME) {
682253ef892Sderaadt 			_asr_strdname(rr.rr_dname, buf, sizeof buf);
683c5c8c49bSeric 			buf[strlen(buf) - 1] = '\0';
684*d1f9129bSflorian 			c = res_hnok(buf) ? buf : as->as.ai.hostname;
685c5c8c49bSeric 		} else if (as->as.ai.hints.ai_flags & AI_FQDN)
686c5c8c49bSeric 			c = as->as.ai.fqdn;
687c5c8c49bSeric 		else
688c5c8c49bSeric 			c = NULL;
689c5c8c49bSeric 
690c5c8c49bSeric 		if (addrinfo_add(as, &u.sa, c))
691c5c8c49bSeric 			return (-1); /* errno set */
692c5c8c49bSeric 	}
693c5c8c49bSeric 	return (0);
694c5c8c49bSeric }
695c5c8c49bSeric 
6969c0437c0Sjca static int
6979c0437c0Sjca addrconfig_setup(struct asr_query *as)
6989c0437c0Sjca {
6999c0437c0Sjca 	struct ifaddrs		*ifa, *ifa0;
70052b8ecd4Sflorian 	struct if_data		*ifa_data;
701d6498094Sjca 	struct sockaddr_in	*sinp;
7029c0437c0Sjca 	struct sockaddr_in6	*sin6p;
70352b8ecd4Sflorian 	int			 rtable, ifa_rtable = -1;
7049c0437c0Sjca 
705cbd3ae78Seric 	if (getifaddrs(&ifa0) == -1)
7069c0437c0Sjca 		return (-1);
7079c0437c0Sjca 
70852b8ecd4Sflorian 	rtable = getrtable();
70952b8ecd4Sflorian 
710abe78e02Sjca 	as->as_flags |= ASYNC_NO_INET | ASYNC_NO_INET6;
7119c0437c0Sjca 
7129c0437c0Sjca 	for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
7139c0437c0Sjca 		if (ifa->ifa_addr == NULL)
7149c0437c0Sjca 			continue;
7159c0437c0Sjca 
7169c0437c0Sjca 		switch (ifa->ifa_addr->sa_family) {
71752b8ecd4Sflorian 		case PF_LINK:
71852b8ecd4Sflorian 			/* AF_LINK comes before inet / inet6 on an interface */
71952b8ecd4Sflorian 			ifa_data = (struct if_data *)ifa->ifa_data;
72052b8ecd4Sflorian 			ifa_rtable = ifa_data->ifi_rdomain;
72152b8ecd4Sflorian 			break;
7229c0437c0Sjca 		case PF_INET:
72352b8ecd4Sflorian 			if (ifa_rtable != rtable)
72452b8ecd4Sflorian 				continue;
72552b8ecd4Sflorian 
726d6498094Sjca 			sinp = (struct sockaddr_in *)ifa->ifa_addr;
727d6498094Sjca 
728873f6271Sjca 			if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
729d6498094Sjca 				continue;
730d6498094Sjca 
731abe78e02Sjca 			as->as_flags &= ~ASYNC_NO_INET;
7329c0437c0Sjca 			break;
7339c0437c0Sjca 		case PF_INET6:
73452b8ecd4Sflorian 			if (ifa_rtable != rtable)
73552b8ecd4Sflorian 				continue;
73652b8ecd4Sflorian 
7379c0437c0Sjca 			sin6p = (struct sockaddr_in6 *)ifa->ifa_addr;
7389c0437c0Sjca 
739d6498094Sjca 			if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr))
740d6498094Sjca 				continue;
741d6498094Sjca 
7429c0437c0Sjca 			if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr))
7439c0437c0Sjca 				continue;
7449c0437c0Sjca 
745abe78e02Sjca 			as->as_flags &= ~ASYNC_NO_INET6;
7469c0437c0Sjca 			break;
7479c0437c0Sjca 		}
7489c0437c0Sjca 	}
7499c0437c0Sjca 
7509c0437c0Sjca 	freeifaddrs(ifa0);
7519c0437c0Sjca 
7529c0437c0Sjca 	return (0);
7539c0437c0Sjca }
754