xref: /openbsd-src/lib/libc/asr/res_search_async.c (revision 46ab48037a854f55e538941cf0b61f61d515d06f)
1*46ab4803Seric /*	$OpenBSD: res_search_async.c,v 1.2 2012/09/09 09:42:06 eric 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  */
17b44da627Seric 
18b44da627Seric #include <sys/types.h>
19b44da627Seric #include <sys/uio.h>
20b44da627Seric 
21b44da627Seric #include <arpa/nameser.h>
22b44da627Seric 
23b44da627Seric #include <err.h>
24b44da627Seric #include <errno.h>
25b44da627Seric #include <stdlib.h>
26b44da627Seric #include <string.h>
27b44da627Seric #include <unistd.h>
28b44da627Seric 
29b44da627Seric /*
30b44da627Seric  * TODO:
31b44da627Seric  *
32b44da627Seric  * - make it possible to reuse ibuf if it was NULL when first called,
33b44da627Seric  *   to avoid reallocating buffers everytime.
34b44da627Seric  */
35b44da627Seric 
36b44da627Seric #include "asr.h"
37b44da627Seric #include "asr_private.h"
38b44da627Seric 
39b44da627Seric static int res_search_async_run(struct async *, struct async_res *);
40b44da627Seric 
41b44da627Seric /*
42b44da627Seric  * Unlike res_query_async(), this function returns a valid packet only if
43b44da627Seric  * h_errno is NETDB_SUCCESS.
44b44da627Seric  */
45b44da627Seric struct async *
46b44da627Seric res_search_async(const char *name, int class, int type, unsigned char *ans,
47b44da627Seric 	int anslen, struct asr *asr)
48b44da627Seric {
49b44da627Seric 	struct asr_ctx	*ac;
50b44da627Seric 	struct async	*as;
51*46ab4803Seric 
52*46ab4803Seric 	DPRINT("asr: res_search_async(\"%s\", %i, %i)\n", name, class, type);
53*46ab4803Seric 
54b44da627Seric 	ac = asr_use_resolver(asr);
55b44da627Seric 	as = res_search_async_ctx(name, class, type, ans, anslen, ac);
56b44da627Seric 	asr_ctx_unref(ac);
57b44da627Seric 
58b44da627Seric 	return (as);
59b44da627Seric }
60b44da627Seric 
61b44da627Seric struct async *
62b44da627Seric res_search_async_ctx(const char *name, int class, int type, unsigned char *ans,
63b44da627Seric 	int anslen, struct asr_ctx *ac)
64b44da627Seric {
65b44da627Seric 	struct async	*as;
66b44da627Seric 
67*46ab4803Seric 	DPRINT("asr: res_search_async_ctx(\"%s\", %i, %i)\n", name, class, type);
68b44da627Seric 
69b44da627Seric 	if ((as = async_new(ac, ASR_SEARCH)) == NULL)
70b44da627Seric 		goto err; /* errno set */
71b44da627Seric 	as->as_run  = res_search_async_run;
72b44da627Seric 	if ((as->as.search.name = strdup(name)) == NULL)
73b44da627Seric 		goto err; /* errno set */
74b44da627Seric 
75b44da627Seric 	if (ans) {
76b44da627Seric 		as->as.search.flags |= ASYNC_EXTIBUF;
77b44da627Seric 		as->as.search.ibuf = ans;
78b44da627Seric 		as->as.search.ibufsize = anslen;
79b44da627Seric 	} else {
80b44da627Seric 		as->as.search.ibuf = NULL;
81b44da627Seric 		as->as.search.ibufsize = 0;
82b44da627Seric 	}
83b44da627Seric 	as->as.search.ibuflen = 0;
84b44da627Seric 
85b44da627Seric 	as->as.search.class = class;
86b44da627Seric 	as->as.search.type = type;
87b44da627Seric 
88b44da627Seric 	return (as);
89b44da627Seric     err:
90b44da627Seric 	if (as)
91b44da627Seric 		async_free(as);
92b44da627Seric 	return (NULL);
93b44da627Seric }
94b44da627Seric 
95b44da627Seric #define HERRNO_UNSET	-2
96b44da627Seric 
97b44da627Seric static int
98b44da627Seric res_search_async_run(struct async *as, struct async_res *ar)
99b44da627Seric {
100b44da627Seric 	int	r;
101b44da627Seric 	char	fqdn[MAXDNAME];
102b44da627Seric 
103b44da627Seric     next:
104b44da627Seric 	switch(as->as_state) {
105b44da627Seric 
106b44da627Seric 	case ASR_STATE_INIT:
107b44da627Seric 
108b44da627Seric 		as->as.search.saved_h_errno = HERRNO_UNSET;
109b44da627Seric 		async_set_state(as, ASR_STATE_NEXT_DOMAIN);
110b44da627Seric 		break;
111b44da627Seric 
112b44da627Seric 	case ASR_STATE_NEXT_DOMAIN:
113b44da627Seric 
114b44da627Seric 		/* Reset flags to be able to identify the case in STATE_SUBQUERY. */
115b44da627Seric 		as->as_dom_flags = 0;
116b44da627Seric 
117b44da627Seric 		r = asr_iter_domain(as, as->as.search.name, fqdn, sizeof(fqdn));
118b44da627Seric 		if (r == -1) {
119b44da627Seric 			async_set_state(as, ASR_STATE_NOT_FOUND);
120b44da627Seric 			break;
121b44da627Seric 		}
122b44da627Seric 		if (r > sizeof(fqdn)) {
123b44da627Seric 			ar->ar_errno = EINVAL;
124b44da627Seric 			ar->ar_h_errno = NO_RECOVERY;
125b44da627Seric 			ar->ar_datalen = -1;
126b44da627Seric 			ar->ar_data = NULL;
127b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
128b44da627Seric 			break;
129b44da627Seric 		}
130b44da627Seric 		as->as.search.subq = res_query_async_ctx(fqdn,
131b44da627Seric 		    as->as.search.class, as->as.search.type,
132b44da627Seric 		    as->as.search.ibuf, as->as.search.ibufsize, as->as_ctx);
133b44da627Seric 		if (as->as.search.subq == NULL) {
134b44da627Seric 			ar->ar_errno = errno;
135b44da627Seric 			if (errno == EINVAL)
136b44da627Seric 				ar->ar_h_errno = NO_RECOVERY;
137b44da627Seric 			else
138b44da627Seric 				ar->ar_h_errno = NETDB_INTERNAL;
139b44da627Seric 			ar->ar_datalen = -1;
140b44da627Seric 			ar->ar_data = NULL;
141b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
142b44da627Seric 			break;
143b44da627Seric 		}
144b44da627Seric 		async_set_state(as, ASR_STATE_SUBQUERY);
145b44da627Seric 		break;
146b44da627Seric 
147b44da627Seric 	case ASR_STATE_SUBQUERY:
148b44da627Seric 
149b44da627Seric 		if ((r = async_run(as->as.search.subq, ar)) == ASYNC_COND)
150b44da627Seric 			return (ASYNC_COND);
151b44da627Seric 		as->as.search.subq = NULL;
152b44da627Seric 
153b44da627Seric 		if (ar->ar_h_errno == NETDB_SUCCESS) {
154b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
155b44da627Seric 			break;
156b44da627Seric 		}
157b44da627Seric 
158b44da627Seric 		/*
159b44da627Seric 		 * The original res_search() does this in the domain search
160b44da627Seric 		 * loop, but only for ECONNREFUSED. I think we can do better
161b44da627Seric 		 * because technically if we get an errno, it means
162b44da627Seric 		 * we couldn't reach any nameserver, so there is no point
163b44da627Seric 		 * in trying further.
164b44da627Seric 		 */
165b44da627Seric 		if (ar->ar_errno) {
166b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
167b44da627Seric 			break;
168b44da627Seric 		}
169b44da627Seric 
170b44da627Seric 		/*
171b44da627Seric 		 * If we don't use an external buffer, the packet was allocated
172b44da627Seric 		 * by the subquery and it must be freed now.
173b44da627Seric 		 */
174b44da627Seric 		if ((as->as.search.flags & ASYNC_EXTIBUF) == 0)
175b44da627Seric 			free(ar->ar_data);
176b44da627Seric 
177b44da627Seric 		/*
178b44da627Seric 		 * The original resolver does something like this, to
179b44da627Seric 		 */
180b44da627Seric 		if (as->as_dom_flags & (ASYNC_DOM_NDOTS | ASYNC_DOM_ASIS))
181b44da627Seric 			as->as.search.saved_h_errno = ar->ar_h_errno;
182b44da627Seric 
183b44da627Seric 		if (as->as_dom_flags & ASYNC_DOM_DOMAIN) {
184b44da627Seric 			if (ar->ar_h_errno == NO_DATA)
185b44da627Seric 				as->as.search.flags |= ASYNC_NODATA;
186b44da627Seric 			else if (ar->ar_h_errno == TRY_AGAIN)
187b44da627Seric 				as->as.search.flags |= ASYNC_AGAIN;
188b44da627Seric 		}
189b44da627Seric 
190b44da627Seric 		async_set_state(as, ASR_STATE_NEXT_DOMAIN);
191b44da627Seric 		break;
192b44da627Seric 
193b44da627Seric 	case ASR_STATE_NOT_FOUND:
194b44da627Seric 
195b44da627Seric 		if (as->as.search.saved_h_errno != HERRNO_UNSET)
196b44da627Seric 			ar->ar_h_errno = as->as.search.saved_h_errno;
197b44da627Seric 		else if (as->as.search.flags & ASYNC_NODATA)
198b44da627Seric 			ar->ar_h_errno = NO_DATA;
199b44da627Seric 		else if (as->as.search.flags & ASYNC_AGAIN)
200b44da627Seric 			ar->ar_h_errno = TRY_AGAIN;
201b44da627Seric 		/*
202b44da627Seric 		 * Else, we got the ar_h_errno value set by res_query_async()
203b44da627Seric 		 * for the last domain.
204b44da627Seric 		 */
205b44da627Seric 		ar->ar_datalen = -1;
206b44da627Seric 		ar->ar_data = NULL;
207b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
208b44da627Seric 		break;
209b44da627Seric 
210b44da627Seric 	case ASR_STATE_HALT:
211b44da627Seric 
212b44da627Seric 		return (ASYNC_DONE);
213b44da627Seric 
214b44da627Seric 	default:
215b44da627Seric 		ar->ar_errno = EOPNOTSUPP;
216b44da627Seric 		ar->ar_h_errno = NETDB_INTERNAL;
217b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
218b44da627Seric                 break;
219b44da627Seric 	}
220b44da627Seric 	goto next;
221b44da627Seric }
222