xref: /openbsd-src/lib/libc/asr/res_search_async.c (revision c5221d4580655bc3139dfff19c6f407e70504097)
1 /*	$OpenBSD: res_search_async.c,v 1.7 2013/04/30 12:02:39 eric Exp $	*/
2 /*
3  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4  *
5  * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/uio.h>
20 #include <arpa/nameser.h>
21 
22 #include <err.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "asr.h"
29 #include "asr_private.h"
30 
31 static int res_search_async_run(struct async *, struct async_res *);
32 
33 /*
34  * Unlike res_query_async(), this function returns a valid packet only if
35  * h_errno is NETDB_SUCCESS.
36  */
37 struct async *
38 res_search_async(const char *name, int class, int type, struct asr *asr)
39 {
40 	struct asr_ctx	*ac;
41 	struct async	*as;
42 
43 	DPRINT("asr: res_search_async(\"%s\", %i, %i)\n", name, class, type);
44 
45 	ac = asr_use_resolver(asr);
46 	as = res_search_async_ctx(name, class, type, ac);
47 	asr_ctx_unref(ac);
48 
49 	return (as);
50 }
51 
52 struct async *
53 res_search_async_ctx(const char *name, int class, int type, struct asr_ctx *ac)
54 {
55 	struct async	*as;
56 
57 	DPRINT("asr: res_search_async_ctx(\"%s\", %i, %i)\n", name, class,
58 	    type);
59 
60 	if ((as = async_new(ac, ASR_SEARCH)) == NULL)
61 		goto err; /* errno set */
62 	as->as_run  = res_search_async_run;
63 	if ((as->as.search.name = strdup(name)) == NULL)
64 		goto err; /* errno set */
65 
66 	as->as.search.class = class;
67 	as->as.search.type = type;
68 
69 	return (as);
70     err:
71 	if (as)
72 		async_free(as);
73 	return (NULL);
74 }
75 
76 #define HERRNO_UNSET	-2
77 
78 static int
79 res_search_async_run(struct async *as, struct async_res *ar)
80 {
81 	int	r;
82 	char	fqdn[MAXDNAME];
83 
84     next:
85 	switch (as->as_state) {
86 
87 	case ASR_STATE_INIT:
88 
89 		as->as.search.saved_h_errno = HERRNO_UNSET;
90 		async_set_state(as, ASR_STATE_NEXT_DOMAIN);
91 		break;
92 
93 	case ASR_STATE_NEXT_DOMAIN:
94 		/*
95 		 * Reset flags to be able to identify the case in
96 		 * STATE_SUBQUERY.
97 		 */
98 		as->as_dom_flags = 0;
99 
100 		r = asr_iter_domain(as, as->as.search.name, fqdn, sizeof(fqdn));
101 		if (r == -1) {
102 			async_set_state(as, ASR_STATE_NOT_FOUND);
103 			break;
104 		}
105 		if (r == 0) {
106 			ar->ar_errno = EINVAL;
107 			ar->ar_h_errno = NO_RECOVERY;
108 			ar->ar_datalen = -1;
109 			ar->ar_data = NULL;
110 			async_set_state(as, ASR_STATE_HALT);
111 			break;
112 		}
113 		as->as.search.subq = res_query_async_ctx(fqdn,
114 		    as->as.search.class, as->as.search.type, as->as_ctx);
115 		if (as->as.search.subq == NULL) {
116 			ar->ar_errno = errno;
117 			if (errno == EINVAL)
118 				ar->ar_h_errno = NO_RECOVERY;
119 			else
120 				ar->ar_h_errno = NETDB_INTERNAL;
121 			ar->ar_datalen = -1;
122 			ar->ar_data = NULL;
123 			async_set_state(as, ASR_STATE_HALT);
124 			break;
125 		}
126 		async_set_state(as, ASR_STATE_SUBQUERY);
127 		break;
128 
129 	case ASR_STATE_SUBQUERY:
130 
131 		if ((r = async_run(as->as.search.subq, ar)) == ASYNC_COND)
132 			return (ASYNC_COND);
133 		as->as.search.subq = NULL;
134 
135 		if (ar->ar_h_errno == NETDB_SUCCESS) {
136 			async_set_state(as, ASR_STATE_HALT);
137 			break;
138 		}
139 
140 		/*
141 		 * The original res_search() does this in the domain search
142 		 * loop, but only for ECONNREFUSED. I think we can do better
143 		 * because technically if we get an errno, it means
144 		 * we couldn't reach any nameserver, so there is no point
145 		 * in trying further.
146 		 */
147 		if (ar->ar_errno) {
148 			async_set_state(as, ASR_STATE_HALT);
149 			break;
150 		}
151 
152 		free(ar->ar_data);
153 
154 		/*
155 		 * The original resolver does something like this.
156 		 */
157 		if (as->as_dom_flags & (ASYNC_DOM_NDOTS | ASYNC_DOM_ASIS))
158 			as->as.search.saved_h_errno = ar->ar_h_errno;
159 
160 		if (as->as_dom_flags & ASYNC_DOM_DOMAIN) {
161 			if (ar->ar_h_errno == NO_DATA)
162 				as->as.search.flags |= ASYNC_NODATA;
163 			else if (ar->ar_h_errno == TRY_AGAIN)
164 				as->as.search.flags |= ASYNC_AGAIN;
165 		}
166 
167 		async_set_state(as, ASR_STATE_NEXT_DOMAIN);
168 		break;
169 
170 	case ASR_STATE_NOT_FOUND:
171 
172 		if (as->as.search.saved_h_errno != HERRNO_UNSET)
173 			ar->ar_h_errno = as->as.search.saved_h_errno;
174 		else if (as->as.search.flags & ASYNC_NODATA)
175 			ar->ar_h_errno = NO_DATA;
176 		else if (as->as.search.flags & ASYNC_AGAIN)
177 			ar->ar_h_errno = TRY_AGAIN;
178 		/*
179 		 * Else, we got the ar_h_errno value set by res_query_async()
180 		 * for the last domain.
181 		 */
182 		ar->ar_datalen = -1;
183 		ar->ar_data = NULL;
184 		async_set_state(as, ASR_STATE_HALT);
185 		break;
186 
187 	case ASR_STATE_HALT:
188 
189 		return (ASYNC_DONE);
190 
191 	default:
192 		ar->ar_errno = EOPNOTSUPP;
193 		ar->ar_h_errno = NETDB_INTERNAL;
194 		async_set_state(as, ASR_STATE_HALT);
195 		break;
196 	}
197 	goto next;
198 }
199