File: | src/lib/libc/asr/res_search_async.c |
Warning: | line 141, column 8 Although the value stored to 'r' is used in the enclosing expression, the value is never actually read from 'r' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: res_search_async.c,v 1.21 2017/02/27 10:44:46 jca 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/socket.h> |
20 | #include <sys/uio.h> |
21 | #include <arpa/nameser.h> |
22 | #include <netdb.h> |
23 | |
24 | #include <asr.h> |
25 | #include <errno(*__errno()).h> |
26 | #include <resolv.h> |
27 | #include <stdlib.h> |
28 | #include <string.h> |
29 | #include <unistd.h> |
30 | |
31 | #include "asr_private.h" |
32 | |
33 | static int res_search_async_run(struct asr_query *, struct asr_result *); |
34 | static size_t domcat(const char *, const char *, char *, size_t); |
35 | |
36 | /* |
37 | * Unlike res_query_async(), this function returns a valid packet only if |
38 | * h_errno is NETDB_SUCCESS. |
39 | */ |
40 | struct asr_query * |
41 | res_search_async(const char *name, int class, int type, void *asr) |
42 | { |
43 | struct asr_ctx *ac; |
44 | struct asr_query *as; |
45 | |
46 | DPRINT("asr: res_search_async(\"%s\", %i, %i)\n", name, class, type); |
47 | |
48 | ac = _asr_use_resolver(asr); |
49 | as = _res_search_async_ctx(name, class, type, ac); |
50 | _asr_ctx_unref(ac); |
51 | |
52 | return (as); |
53 | } |
54 | DEF_WEAK(res_search_async)__asm__(".weak " "res_search_async" " ; " "res_search_async" " = " "_libc_res_search_async"); |
55 | |
56 | struct asr_query * |
57 | _res_search_async_ctx(const char *name, int class, int type, struct asr_ctx *ac) |
58 | { |
59 | struct asr_query *as; |
60 | |
61 | DPRINT("asr: res_search_async_ctx(\"%s\", %i, %i)\n", name, class, |
62 | type); |
63 | |
64 | if ((as = _asr_async_new(ac, ASR_SEARCH)) == NULL((void *)0)) |
65 | goto err; /* errno set */ |
66 | as->as_run = res_search_async_run; |
67 | if ((as->as.search.name = strdup(name)) == NULL((void *)0)) |
68 | goto err; /* errno set */ |
69 | |
70 | as->as.search.class = class; |
71 | as->as.search.type = type; |
72 | |
73 | return (as); |
74 | err: |
75 | if (as) |
76 | _asr_async_free(as); |
77 | return (NULL((void *)0)); |
78 | } |
79 | |
80 | #define HERRNO_UNSET-2 -2 |
81 | |
82 | static int |
83 | res_search_async_run(struct asr_query *as, struct asr_result *ar) |
84 | { |
85 | int r; |
86 | char fqdn[MAXDNAME1025]; |
87 | |
88 | next: |
89 | switch (as->as_state) { |
90 | |
91 | case ASR_STATE_INIT: |
92 | |
93 | if (as->as.search.name[0] == '\0') { |
94 | ar->ar_h_errno = NO_DATA4; |
95 | async_set_state(as, ASR_STATE_HALT)do { ; (as)->as_state = (ASR_STATE_HALT); } while (0); |
96 | break; |
97 | } |
98 | |
99 | as->as.search.saved_h_errno = HERRNO_UNSET-2; |
100 | async_set_state(as, ASR_STATE_NEXT_DOMAIN)do { ; (as)->as_state = (ASR_STATE_NEXT_DOMAIN); } while ( 0); |
101 | break; |
102 | |
103 | case ASR_STATE_NEXT_DOMAIN: |
104 | /* |
105 | * Reset flags to be able to identify the case in |
106 | * STATE_SUBQUERY. |
107 | */ |
108 | as->as_dom_flags = 0; |
109 | |
110 | r = _asr_iter_domain(as, as->as.search.name, fqdn, sizeof(fqdn)); |
111 | if (r == -1) { |
112 | async_set_state(as, ASR_STATE_NOT_FOUND)do { ; (as)->as_state = (ASR_STATE_NOT_FOUND); } while (0); |
113 | break; |
114 | } |
115 | if (r == 0) { |
116 | ar->ar_errno = EINVAL22; |
117 | ar->ar_h_errno = NO_RECOVERY3; |
118 | ar->ar_datalen = -1; |
119 | ar->ar_data = NULL((void *)0); |
120 | async_set_state(as, ASR_STATE_HALT)do { ; (as)->as_state = (ASR_STATE_HALT); } while (0); |
121 | break; |
122 | } |
123 | as->as_subq = _res_query_async_ctx(fqdn, |
124 | as->as.search.class, as->as.search.type, as->as_ctx); |
125 | if (as->as_subq == NULL((void *)0)) { |
126 | ar->ar_errno = errno(*__errno()); |
127 | if (errno(*__errno()) == EINVAL22) |
128 | ar->ar_h_errno = NO_RECOVERY3; |
129 | else |
130 | ar->ar_h_errno = NETDB_INTERNAL-1; |
131 | ar->ar_datalen = -1; |
132 | ar->ar_data = NULL((void *)0); |
133 | async_set_state(as, ASR_STATE_HALT)do { ; (as)->as_state = (ASR_STATE_HALT); } while (0); |
134 | break; |
135 | } |
136 | async_set_state(as, ASR_STATE_SUBQUERY)do { ; (as)->as_state = (ASR_STATE_SUBQUERY); } while (0); |
137 | break; |
138 | |
139 | case ASR_STATE_SUBQUERY: |
140 | |
141 | if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND0) |
Although the value stored to 'r' is used in the enclosing expression, the value is never actually read from 'r' | |
142 | return (ASYNC_COND0); |
143 | as->as_subq = NULL((void *)0); |
144 | |
145 | if (ar->ar_h_errno == NETDB_SUCCESS0) { |
146 | async_set_state(as, ASR_STATE_HALT)do { ; (as)->as_state = (ASR_STATE_HALT); } while (0); |
147 | break; |
148 | } |
149 | |
150 | /* |
151 | * The original res_search() does this in the domain search |
152 | * loop, but only for ECONNREFUSED. I think we can do better |
153 | * because technically if we get an errno, it means |
154 | * we couldn't reach any nameserver, so there is no point |
155 | * in trying further. |
156 | */ |
157 | if (ar->ar_errno) { |
158 | async_set_state(as, ASR_STATE_HALT)do { ; (as)->as_state = (ASR_STATE_HALT); } while (0); |
159 | break; |
160 | } |
161 | |
162 | free(ar->ar_data); |
163 | |
164 | /* |
165 | * The original resolver does something like this. |
166 | */ |
167 | if (as->as_dom_flags & ASYNC_DOM_NDOTS0x00000002) |
168 | as->as.search.saved_h_errno = ar->ar_h_errno; |
169 | |
170 | if (as->as_dom_flags & ASYNC_DOM_DOMAIN0x00000004) { |
171 | if (ar->ar_h_errno == NO_DATA4) |
172 | as->as_flags |= ASYNC_NODATA0x00000100; |
173 | else if (ar->ar_h_errno == TRY_AGAIN2) |
174 | as->as_flags |= ASYNC_AGAIN0x00000200; |
175 | } |
176 | |
177 | async_set_state(as, ASR_STATE_NEXT_DOMAIN)do { ; (as)->as_state = (ASR_STATE_NEXT_DOMAIN); } while ( 0); |
178 | break; |
179 | |
180 | case ASR_STATE_NOT_FOUND: |
181 | |
182 | if (as->as.search.saved_h_errno != HERRNO_UNSET-2) |
183 | ar->ar_h_errno = as->as.search.saved_h_errno; |
184 | else if (as->as_flags & ASYNC_NODATA0x00000100) |
185 | ar->ar_h_errno = NO_DATA4; |
186 | else if (as->as_flags & ASYNC_AGAIN0x00000200) |
187 | ar->ar_h_errno = TRY_AGAIN2; |
188 | /* |
189 | * Else, we got the ar_h_errno value set by res_query_async() |
190 | * for the last domain. |
191 | */ |
192 | ar->ar_datalen = -1; |
193 | ar->ar_data = NULL((void *)0); |
194 | async_set_state(as, ASR_STATE_HALT)do { ; (as)->as_state = (ASR_STATE_HALT); } while (0); |
195 | break; |
196 | |
197 | case ASR_STATE_HALT: |
198 | |
199 | return (ASYNC_DONE1); |
200 | |
201 | default: |
202 | ar->ar_errno = EOPNOTSUPP45; |
203 | ar->ar_h_errno = NETDB_INTERNAL-1; |
204 | async_set_state(as, ASR_STATE_HALT)do { ; (as)->as_state = (ASR_STATE_HALT); } while (0); |
205 | break; |
206 | } |
207 | goto next; |
208 | } |
209 | |
210 | /* |
211 | * Concatenate a name and a domain name. The result has no trailing dot. |
212 | * Return the resulting string length, or 0 in case of error. |
213 | */ |
214 | static size_t |
215 | domcat(const char *name, const char *domain, char *buf, size_t buflen) |
216 | { |
217 | size_t r; |
218 | |
219 | r = _asr_make_fqdn(name, domain, buf, buflen); |
220 | if (r == 0) |
221 | return (0); |
222 | buf[r - 1] = '\0'; |
223 | |
224 | return (r - 1); |
225 | } |
226 | |
227 | enum { |
228 | DOM_INIT, |
229 | DOM_DOMAIN, |
230 | DOM_DONE |
231 | }; |
232 | |
233 | /* |
234 | * Implement the search domain strategy. |
235 | * |
236 | * This function works as a generator that constructs complete domains in |
237 | * buffer "buf" of size "len" for the given host name "name", according to the |
238 | * search rules defined by the resolving context. It is supposed to be called |
239 | * multiple times (with the same name) to generate the next possible domain |
240 | * name, if any. |
241 | * |
242 | * It returns -1 if all possibilities have been exhausted, 0 if there was an |
243 | * error generating the next name, or the resulting name length. |
244 | */ |
245 | int |
246 | _asr_iter_domain(struct asr_query *as, const char *name, char * buf, size_t len) |
247 | { |
248 | const char *c; |
249 | int dots; |
250 | |
251 | switch (as->as_dom_step) { |
252 | |
253 | case DOM_INIT: |
254 | /* First call */ |
255 | |
256 | /* |
257 | * If "name" is an FQDN, that's the only result and we |
258 | * don't try anything else. |
259 | */ |
260 | if (strlen(name) && name[strlen(name) - 1] == '.') { |
261 | DPRINT("asr: iter_domain(\"%s\") fqdn\n", name); |
262 | as->as_dom_flags |= ASYNC_DOM_FQDN0x00000001; |
263 | as->as_dom_step = DOM_DONE; |
264 | return (domcat(name, NULL((void *)0), buf, len)); |
265 | } |
266 | |
267 | /* |
268 | * Otherwise, we iterate through the specified search domains. |
269 | */ |
270 | as->as_dom_step = DOM_DOMAIN; |
271 | as->as_dom_idx = 0; |
272 | |
273 | /* |
274 | * If "name" as enough dots, use it as-is first, as indicated |
275 | * in resolv.conf(5). |
276 | */ |
277 | dots = 0; |
278 | for (c = name; *c; c++) |
279 | dots += (*c == '.'); |
280 | if (dots >= as->as_ctx->ac_ndots) { |
281 | DPRINT("asr: iter_domain(\"%s\") ndots\n", name); |
282 | as->as_dom_flags |= ASYNC_DOM_NDOTS0x00000002; |
283 | if (strlcpy(buf, name, len) >= len) |
284 | return (0); |
285 | return (strlen(buf)); |
286 | } |
287 | /* Otherwise, starts using the search domains */ |
288 | /* FALLTHROUGH */ |
289 | |
290 | case DOM_DOMAIN: |
291 | if (as->as_dom_idx < as->as_ctx->ac_domcount && |
292 | (as->as_ctx->ac_options & RES_DNSRCH0x00000200 || ( |
293 | as->as_ctx->ac_options & RES_DEFNAMES0x00000080 && |
294 | as->as_dom_idx == 0 && |
295 | strchr(name, '.') == NULL((void *)0)))) { |
296 | DPRINT("asr: iter_domain(\"%s\") domain \"%s\"\n", |
297 | name, as->as_ctx->ac_dom[as->as_dom_idx]); |
298 | as->as_dom_flags |= ASYNC_DOM_DOMAIN0x00000004; |
299 | return (domcat(name, |
300 | as->as_ctx->ac_dom[as->as_dom_idx++], buf, len)); |
301 | } |
302 | |
303 | /* No more domain to try. */ |
304 | |
305 | as->as_dom_step = DOM_DONE; |
306 | |
307 | /* |
308 | * If the name was not tried as an absolute name before, |
309 | * do it now. |
310 | */ |
311 | if (!(as->as_dom_flags & ASYNC_DOM_NDOTS0x00000002)) { |
312 | DPRINT("asr: iter_domain(\"%s\") as is\n", name); |
313 | as->as_dom_flags |= ASYNC_DOM_ASIS0x00000008; |
314 | if (strlcpy(buf, name, len) >= len) |
315 | return (0); |
316 | return (strlen(buf)); |
317 | } |
318 | /* Otherwise, we are done. */ |
319 | |
320 | case DOM_DONE: |
321 | default: |
322 | DPRINT("asr: iter_domain(\"%s\") done\n", name); |
323 | return (-1); |
324 | } |
325 | } |