GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/asr/res_search_async.c Lines: 0 103 0.0 %
Date: 2017-11-13 Branches: 0 69 0.0 %

Line Branch Exec Source
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.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);
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)
65
		goto err; /* errno set */
66
	as->as_run  = res_search_async_run;
67
	if ((as->as.search.name = strdup(name)) == NULL)
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);
78
}
79
80
#define HERRNO_UNSET	-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[MAXDNAME];
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_DATA;
95
			async_set_state(as, ASR_STATE_HALT);
96
			break;
97
		}
98
99
		as->as.search.saved_h_errno = HERRNO_UNSET;
100
		async_set_state(as, ASR_STATE_NEXT_DOMAIN);
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);
113
			break;
114
		}
115
		if (r == 0) {
116
			ar->ar_errno = EINVAL;
117
			ar->ar_h_errno = NO_RECOVERY;
118
			ar->ar_datalen = -1;
119
			ar->ar_data = NULL;
120
			async_set_state(as, ASR_STATE_HALT);
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) {
126
			ar->ar_errno = errno;
127
			if (errno == EINVAL)
128
				ar->ar_h_errno = NO_RECOVERY;
129
			else
130
				ar->ar_h_errno = NETDB_INTERNAL;
131
			ar->ar_datalen = -1;
132
			ar->ar_data = NULL;
133
			async_set_state(as, ASR_STATE_HALT);
134
			break;
135
		}
136
		async_set_state(as, ASR_STATE_SUBQUERY);
137
		break;
138
139
	case ASR_STATE_SUBQUERY:
140
141
		if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
142
			return (ASYNC_COND);
143
		as->as_subq = NULL;
144
145
		if (ar->ar_h_errno == NETDB_SUCCESS) {
146
			async_set_state(as, ASR_STATE_HALT);
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);
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_NDOTS)
168
			as->as.search.saved_h_errno = ar->ar_h_errno;
169
170
		if (as->as_dom_flags & ASYNC_DOM_DOMAIN) {
171
			if (ar->ar_h_errno == NO_DATA)
172
				as->as_flags |= ASYNC_NODATA;
173
			else if (ar->ar_h_errno == TRY_AGAIN)
174
				as->as_flags |= ASYNC_AGAIN;
175
		}
176
177
		async_set_state(as, ASR_STATE_NEXT_DOMAIN);
178
		break;
179
180
	case ASR_STATE_NOT_FOUND:
181
182
		if (as->as.search.saved_h_errno != HERRNO_UNSET)
183
			ar->ar_h_errno = as->as.search.saved_h_errno;
184
		else if (as->as_flags & ASYNC_NODATA)
185
			ar->ar_h_errno = NO_DATA;
186
		else if (as->as_flags & ASYNC_AGAIN)
187
			ar->ar_h_errno = TRY_AGAIN;
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;
194
		async_set_state(as, ASR_STATE_HALT);
195
		break;
196
197
	case ASR_STATE_HALT:
198
199
		return (ASYNC_DONE);
200
201
	default:
202
		ar->ar_errno = EOPNOTSUPP;
203
		ar->ar_h_errno = NETDB_INTERNAL;
204
		async_set_state(as, ASR_STATE_HALT);
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_FQDN;
263
			as->as_dom_step = DOM_DONE;
264
			return (domcat(name, NULL, 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_NDOTS;
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_DNSRCH || (
293
			as->as_ctx->ac_options & RES_DEFNAMES &&
294
			as->as_dom_idx == 0 &&
295
			strchr(name, '.') == NULL))) {
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_DOMAIN;
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_NDOTS)) {
312
			DPRINT("asr: iter_domain(\"%s\") as is\n", name);
313
			as->as_dom_flags |= ASYNC_DOM_ASIS;
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
}