1 |
|
|
/* $OpenBSD: res_send_async.c,v 1.36 2017/03/15 15:54:41 deraadt 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 <netinet/in.h> |
22 |
|
|
#include <arpa/nameser.h> |
23 |
|
|
#include <netdb.h> |
24 |
|
|
|
25 |
|
|
#include <asr.h> |
26 |
|
|
#include <errno.h> |
27 |
|
|
#include <fcntl.h> |
28 |
|
|
#include <poll.h> |
29 |
|
|
#include <resolv.h> /* for res_random */ |
30 |
|
|
#include <stdlib.h> |
31 |
|
|
#include <string.h> |
32 |
|
|
#include <unistd.h> |
33 |
|
|
|
34 |
|
|
#include "asr_private.h" |
35 |
|
|
|
36 |
|
|
#define OP_QUERY (0) |
37 |
|
|
|
38 |
|
|
static int res_send_async_run(struct asr_query *, struct asr_result *); |
39 |
|
|
static int sockaddr_connect(const struct sockaddr *, int); |
40 |
|
|
static int udp_send(struct asr_query *); |
41 |
|
|
static int udp_recv(struct asr_query *); |
42 |
|
|
static int tcp_write(struct asr_query *); |
43 |
|
|
static int tcp_read(struct asr_query *); |
44 |
|
|
static int validate_packet(struct asr_query *); |
45 |
|
|
static int setup_query(struct asr_query *, const char *, const char *, int, int); |
46 |
|
|
static int ensure_ibuf(struct asr_query *, size_t); |
47 |
|
|
static int iter_ns(struct asr_query *); |
48 |
|
|
|
49 |
|
|
#define AS_NS_SA(p) ((p)->as_ctx->ac_ns[(p)->as.dns.nsidx - 1]) |
50 |
|
|
|
51 |
|
|
|
52 |
|
|
struct asr_query * |
53 |
|
|
res_send_async(const unsigned char *buf, int buflen, void *asr) |
54 |
|
|
{ |
55 |
|
|
struct asr_ctx *ac; |
56 |
|
|
struct asr_query *as; |
57 |
|
|
struct asr_unpack p; |
58 |
|
|
struct asr_dns_header h; |
59 |
|
|
struct asr_dns_query q; |
60 |
|
|
|
61 |
|
|
DPRINT_PACKET("asr: res_send_async()", buf, buflen); |
62 |
|
|
|
63 |
|
|
ac = _asr_use_resolver(asr); |
64 |
|
|
if ((as = _asr_async_new(ac, ASR_SEND)) == NULL) { |
65 |
|
|
_asr_ctx_unref(ac); |
66 |
|
|
return (NULL); /* errno set */ |
67 |
|
|
} |
68 |
|
|
as->as_run = res_send_async_run; |
69 |
|
|
|
70 |
|
|
as->as_flags |= ASYNC_EXTOBUF; |
71 |
|
|
as->as.dns.obuf = (unsigned char *)buf; |
72 |
|
|
as->as.dns.obuflen = buflen; |
73 |
|
|
as->as.dns.obufsize = buflen; |
74 |
|
|
|
75 |
|
|
_asr_unpack_init(&p, buf, buflen); |
76 |
|
|
_asr_unpack_header(&p, &h); |
77 |
|
|
_asr_unpack_query(&p, &q); |
78 |
|
|
if (p.err) { |
79 |
|
|
errno = EINVAL; |
80 |
|
|
goto err; |
81 |
|
|
} |
82 |
|
|
as->as.dns.reqid = h.id; |
83 |
|
|
as->as.dns.type = q.q_type; |
84 |
|
|
as->as.dns.class = q.q_class; |
85 |
|
|
as->as.dns.dname = strdup(q.q_dname); |
86 |
|
|
if (as->as.dns.dname == NULL) |
87 |
|
|
goto err; /* errno set */ |
88 |
|
|
|
89 |
|
|
_asr_ctx_unref(ac); |
90 |
|
|
return (as); |
91 |
|
|
err: |
92 |
|
|
if (as) |
93 |
|
|
_asr_async_free(as); |
94 |
|
|
_asr_ctx_unref(ac); |
95 |
|
|
return (NULL); |
96 |
|
|
} |
97 |
|
|
DEF_WEAK(res_send_async); |
98 |
|
|
|
99 |
|
|
/* |
100 |
|
|
* Unlike res_query(), this version will actually return the packet |
101 |
|
|
* if it has received a valid one (errno == 0) even if h_errno is |
102 |
|
|
* not NETDB_SUCCESS. So the packet *must* be freed if necessary. |
103 |
|
|
*/ |
104 |
|
|
struct asr_query * |
105 |
|
|
res_query_async(const char *name, int class, int type, void *asr) |
106 |
|
|
{ |
107 |
|
|
struct asr_ctx *ac; |
108 |
|
|
struct asr_query *as; |
109 |
|
|
|
110 |
|
|
DPRINT("asr: res_query_async(\"%s\", %i, %i)\n", name, class, type); |
111 |
|
|
|
112 |
|
|
ac = _asr_use_resolver(asr); |
113 |
|
|
as = _res_query_async_ctx(name, class, type, ac); |
114 |
|
|
_asr_ctx_unref(ac); |
115 |
|
|
|
116 |
|
|
return (as); |
117 |
|
|
} |
118 |
|
|
DEF_WEAK(res_query_async); |
119 |
|
|
|
120 |
|
|
struct asr_query * |
121 |
|
|
_res_query_async_ctx(const char *name, int class, int type, struct asr_ctx *a_ctx) |
122 |
|
|
{ |
123 |
|
|
struct asr_query *as; |
124 |
|
|
|
125 |
|
|
DPRINT("asr: res_query_async_ctx(\"%s\", %i, %i)\n", name, class, type); |
126 |
|
|
|
127 |
|
|
if ((as = _asr_async_new(a_ctx, ASR_SEND)) == NULL) |
128 |
|
|
return (NULL); /* errno set */ |
129 |
|
|
as->as_run = res_send_async_run; |
130 |
|
|
|
131 |
|
|
/* This adds a "." to name if it doesn't already has one. |
132 |
|
|
* That's how res_query() behaves (through res_mkquery"). |
133 |
|
|
*/ |
134 |
|
|
if (setup_query(as, name, NULL, class, type) == -1) |
135 |
|
|
goto err; /* errno set */ |
136 |
|
|
|
137 |
|
|
return (as); |
138 |
|
|
|
139 |
|
|
err: |
140 |
|
|
if (as) |
141 |
|
|
_asr_async_free(as); |
142 |
|
|
|
143 |
|
|
return (NULL); |
144 |
|
|
} |
145 |
|
|
|
146 |
|
|
static int |
147 |
|
|
res_send_async_run(struct asr_query *as, struct asr_result *ar) |
148 |
|
|
{ |
149 |
|
|
next: |
150 |
|
|
switch (as->as_state) { |
151 |
|
|
|
152 |
|
|
case ASR_STATE_INIT: |
153 |
|
|
|
154 |
|
|
if (as->as_ctx->ac_nscount == 0) { |
155 |
|
|
ar->ar_errno = ECONNREFUSED; |
156 |
|
|
async_set_state(as, ASR_STATE_HALT); |
157 |
|
|
break; |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
async_set_state(as, ASR_STATE_NEXT_NS); |
161 |
|
|
break; |
162 |
|
|
|
163 |
|
|
case ASR_STATE_NEXT_NS: |
164 |
|
|
|
165 |
|
|
if (iter_ns(as) == -1) { |
166 |
|
|
ar->ar_errno = ETIMEDOUT; |
167 |
|
|
async_set_state(as, ASR_STATE_HALT); |
168 |
|
|
break; |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
if (as->as_ctx->ac_options & RES_USEVC || |
172 |
|
|
as->as.dns.obuflen > PACKETSZ) |
173 |
|
|
async_set_state(as, ASR_STATE_TCP_WRITE); |
174 |
|
|
else |
175 |
|
|
async_set_state(as, ASR_STATE_UDP_SEND); |
176 |
|
|
break; |
177 |
|
|
|
178 |
|
|
case ASR_STATE_UDP_SEND: |
179 |
|
|
|
180 |
|
|
if (udp_send(as) == -1) { |
181 |
|
|
async_set_state(as, ASR_STATE_NEXT_NS); |
182 |
|
|
break; |
183 |
|
|
} |
184 |
|
|
async_set_state(as, ASR_STATE_UDP_RECV); |
185 |
|
|
ar->ar_cond = ASR_WANT_READ; |
186 |
|
|
ar->ar_fd = as->as_fd; |
187 |
|
|
ar->ar_timeout = as->as_timeout; |
188 |
|
|
return (ASYNC_COND); |
189 |
|
|
break; |
190 |
|
|
|
191 |
|
|
case ASR_STATE_UDP_RECV: |
192 |
|
|
|
193 |
|
|
if (udp_recv(as) == -1) { |
194 |
|
|
if (errno == ENOMEM) { |
195 |
|
|
ar->ar_errno = errno; |
196 |
|
|
async_set_state(as, ASR_STATE_HALT); |
197 |
|
|
break; |
198 |
|
|
} |
199 |
|
|
if (errno != EOVERFLOW) { |
200 |
|
|
/* Fail or timeout */ |
201 |
|
|
async_set_state(as, ASR_STATE_NEXT_NS); |
202 |
|
|
break; |
203 |
|
|
} |
204 |
|
|
if (as->as_ctx->ac_options & RES_IGNTC) |
205 |
|
|
async_set_state(as, ASR_STATE_PACKET); |
206 |
|
|
else |
207 |
|
|
async_set_state(as, ASR_STATE_TCP_WRITE); |
208 |
|
|
} else |
209 |
|
|
async_set_state(as, ASR_STATE_PACKET); |
210 |
|
|
break; |
211 |
|
|
|
212 |
|
|
case ASR_STATE_TCP_WRITE: |
213 |
|
|
|
214 |
|
|
switch (tcp_write(as)) { |
215 |
|
|
case -1: /* fail or timeout */ |
216 |
|
|
async_set_state(as, ASR_STATE_NEXT_NS); |
217 |
|
|
break; |
218 |
|
|
case 0: |
219 |
|
|
async_set_state(as, ASR_STATE_TCP_READ); |
220 |
|
|
ar->ar_cond = ASR_WANT_READ; |
221 |
|
|
ar->ar_fd = as->as_fd; |
222 |
|
|
ar->ar_timeout = as->as_timeout; |
223 |
|
|
return (ASYNC_COND); |
224 |
|
|
case 1: |
225 |
|
|
ar->ar_cond = ASR_WANT_WRITE; |
226 |
|
|
ar->ar_fd = as->as_fd; |
227 |
|
|
ar->ar_timeout = as->as_timeout; |
228 |
|
|
return (ASYNC_COND); |
229 |
|
|
} |
230 |
|
|
break; |
231 |
|
|
|
232 |
|
|
case ASR_STATE_TCP_READ: |
233 |
|
|
|
234 |
|
|
switch (tcp_read(as)) { |
235 |
|
|
case -1: /* Fail or timeout */ |
236 |
|
|
if (errno == ENOMEM) { |
237 |
|
|
ar->ar_errno = errno; |
238 |
|
|
async_set_state(as, ASR_STATE_HALT); |
239 |
|
|
} else |
240 |
|
|
async_set_state(as, ASR_STATE_NEXT_NS); |
241 |
|
|
break; |
242 |
|
|
case 0: |
243 |
|
|
async_set_state(as, ASR_STATE_PACKET); |
244 |
|
|
break; |
245 |
|
|
case 1: |
246 |
|
|
ar->ar_cond = ASR_WANT_READ; |
247 |
|
|
ar->ar_fd = as->as_fd; |
248 |
|
|
ar->ar_timeout = as->as_timeout; |
249 |
|
|
return (ASYNC_COND); |
250 |
|
|
} |
251 |
|
|
break; |
252 |
|
|
|
253 |
|
|
case ASR_STATE_PACKET: |
254 |
|
|
|
255 |
|
|
memmove(&ar->ar_ns, AS_NS_SA(as), AS_NS_SA(as)->sa_len); |
256 |
|
|
ar->ar_datalen = as->as.dns.ibuflen; |
257 |
|
|
ar->ar_data = as->as.dns.ibuf; |
258 |
|
|
as->as.dns.ibuf = NULL; |
259 |
|
|
ar->ar_errno = 0; |
260 |
|
|
ar->ar_rcode = as->as.dns.rcode; |
261 |
|
|
async_set_state(as, ASR_STATE_HALT); |
262 |
|
|
break; |
263 |
|
|
|
264 |
|
|
case ASR_STATE_HALT: |
265 |
|
|
|
266 |
|
|
if (ar->ar_errno) { |
267 |
|
|
ar->ar_h_errno = TRY_AGAIN; |
268 |
|
|
ar->ar_count = 0; |
269 |
|
|
ar->ar_datalen = -1; |
270 |
|
|
ar->ar_data = NULL; |
271 |
|
|
} else if (as->as.dns.ancount) { |
272 |
|
|
ar->ar_h_errno = NETDB_SUCCESS; |
273 |
|
|
ar->ar_count = as->as.dns.ancount; |
274 |
|
|
} else { |
275 |
|
|
ar->ar_count = 0; |
276 |
|
|
switch (as->as.dns.rcode) { |
277 |
|
|
case NXDOMAIN: |
278 |
|
|
ar->ar_h_errno = HOST_NOT_FOUND; |
279 |
|
|
break; |
280 |
|
|
case SERVFAIL: |
281 |
|
|
ar->ar_h_errno = TRY_AGAIN; |
282 |
|
|
break; |
283 |
|
|
case NOERROR: |
284 |
|
|
ar->ar_h_errno = NO_DATA; |
285 |
|
|
break; |
286 |
|
|
default: |
287 |
|
|
ar->ar_h_errno = NO_RECOVERY; |
288 |
|
|
} |
289 |
|
|
} |
290 |
|
|
return (ASYNC_DONE); |
291 |
|
|
|
292 |
|
|
default: |
293 |
|
|
|
294 |
|
|
ar->ar_errno = EOPNOTSUPP; |
295 |
|
|
ar->ar_h_errno = NETDB_INTERNAL; |
296 |
|
|
async_set_state(as, ASR_STATE_HALT); |
297 |
|
|
break; |
298 |
|
|
} |
299 |
|
|
goto next; |
300 |
|
|
} |
301 |
|
|
|
302 |
|
|
static int |
303 |
|
|
sockaddr_connect(const struct sockaddr *sa, int socktype) |
304 |
|
|
{ |
305 |
|
|
int errno_save, sock; |
306 |
|
|
|
307 |
|
|
if ((sock = socket(sa->sa_family, |
308 |
|
|
socktype | SOCK_NONBLOCK | SOCK_DNS, 0)) == -1) |
309 |
|
|
goto fail; |
310 |
|
|
|
311 |
|
|
if (connect(sock, sa, sa->sa_len) == -1) { |
312 |
|
|
/* |
313 |
|
|
* In the TCP case, the caller will be asked to poll for |
314 |
|
|
* POLLOUT so that we start writing the packet in tcp_write() |
315 |
|
|
* when the connection is established, or fail there on error. |
316 |
|
|
*/ |
317 |
|
|
if (errno == EINPROGRESS) |
318 |
|
|
return (sock); |
319 |
|
|
goto fail; |
320 |
|
|
} |
321 |
|
|
|
322 |
|
|
return (sock); |
323 |
|
|
|
324 |
|
|
fail: |
325 |
|
|
|
326 |
|
|
if (sock != -1) { |
327 |
|
|
errno_save = errno; |
328 |
|
|
close(sock); |
329 |
|
|
errno = errno_save; |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
return (-1); |
333 |
|
|
} |
334 |
|
|
|
335 |
|
|
/* |
336 |
|
|
* Prepare the DNS packet for the query type "type", class "class" and domain |
337 |
|
|
* name created by the concatenation on "name" and "dom". |
338 |
|
|
* Return 0 on success, set errno and return -1 on error. |
339 |
|
|
*/ |
340 |
|
|
static int |
341 |
|
|
setup_query(struct asr_query *as, const char *name, const char *dom, |
342 |
|
|
int class, int type) |
343 |
|
|
{ |
344 |
|
|
struct asr_pack p; |
345 |
|
|
struct asr_dns_header h; |
346 |
|
|
char fqdn[MAXDNAME]; |
347 |
|
|
char dname[MAXDNAME]; |
348 |
|
|
|
349 |
|
|
if (as->as_flags & ASYNC_EXTOBUF) { |
350 |
|
|
errno = EINVAL; |
351 |
|
|
DPRINT("attempting to write in user packet"); |
352 |
|
|
return (-1); |
353 |
|
|
} |
354 |
|
|
|
355 |
|
|
if (_asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) { |
356 |
|
|
errno = EINVAL; |
357 |
|
|
DPRINT("asr_make_fqdn: name too long\n"); |
358 |
|
|
return (-1); |
359 |
|
|
} |
360 |
|
|
|
361 |
|
|
if (_asr_dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) { |
362 |
|
|
errno = EINVAL; |
363 |
|
|
DPRINT("asr_dname_from_fqdn: invalid\n"); |
364 |
|
|
return (-1); |
365 |
|
|
} |
366 |
|
|
|
367 |
|
|
if (as->as.dns.obuf == NULL) { |
368 |
|
|
as->as.dns.obufsize = PACKETSZ; |
369 |
|
|
as->as.dns.obuf = malloc(as->as.dns.obufsize); |
370 |
|
|
if (as->as.dns.obuf == NULL) |
371 |
|
|
return (-1); /* errno set */ |
372 |
|
|
} |
373 |
|
|
as->as.dns.obuflen = 0; |
374 |
|
|
|
375 |
|
|
memset(&h, 0, sizeof h); |
376 |
|
|
h.id = res_randomid(); |
377 |
|
|
if (as->as_ctx->ac_options & RES_RECURSE) |
378 |
|
|
h.flags |= RD_MASK; |
379 |
|
|
h.qdcount = 1; |
380 |
|
|
if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) |
381 |
|
|
h.arcount = 1; |
382 |
|
|
|
383 |
|
|
_asr_pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize); |
384 |
|
|
_asr_pack_header(&p, &h); |
385 |
|
|
_asr_pack_query(&p, type, class, dname); |
386 |
|
|
if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) |
387 |
|
|
_asr_pack_edns0(&p, MAXPACKETSZ, |
388 |
|
|
as->as_ctx->ac_options & RES_USE_DNSSEC); |
389 |
|
|
if (p.err) { |
390 |
|
|
DPRINT("error packing query"); |
391 |
|
|
errno = EINVAL; |
392 |
|
|
return (-1); |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
/* Remember the parameters. */ |
396 |
|
|
as->as.dns.reqid = h.id; |
397 |
|
|
as->as.dns.type = type; |
398 |
|
|
as->as.dns.class = class; |
399 |
|
|
if (as->as.dns.dname) |
400 |
|
|
free(as->as.dns.dname); |
401 |
|
|
as->as.dns.dname = strdup(dname); |
402 |
|
|
if (as->as.dns.dname == NULL) { |
403 |
|
|
DPRINT("strdup"); |
404 |
|
|
return (-1); /* errno set */ |
405 |
|
|
} |
406 |
|
|
as->as.dns.obuflen = p.offset; |
407 |
|
|
|
408 |
|
|
DPRINT_PACKET("asr_setup_query", as->as.dns.obuf, as->as.dns.obuflen); |
409 |
|
|
|
410 |
|
|
return (0); |
411 |
|
|
} |
412 |
|
|
|
413 |
|
|
/* |
414 |
|
|
* Create a connect UDP socket and send the output packet. |
415 |
|
|
* |
416 |
|
|
* Return 0 on success, or -1 on error (errno set). |
417 |
|
|
*/ |
418 |
|
|
static int |
419 |
|
|
udp_send(struct asr_query *as) |
420 |
|
|
{ |
421 |
|
|
ssize_t n; |
422 |
|
|
int save_errno; |
423 |
|
|
#ifdef DEBUG |
424 |
|
|
char buf[256]; |
425 |
|
|
#endif |
426 |
|
|
|
427 |
|
|
DPRINT("asr: [%p] connecting to %s UDP\n", as, |
428 |
|
|
_asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); |
429 |
|
|
|
430 |
|
|
as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM); |
431 |
|
|
if (as->as_fd == -1) |
432 |
|
|
return (-1); /* errno set */ |
433 |
|
|
|
434 |
|
|
n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0); |
435 |
|
|
if (n == -1) { |
436 |
|
|
save_errno = errno; |
437 |
|
|
close(as->as_fd); |
438 |
|
|
errno = save_errno; |
439 |
|
|
as->as_fd = -1; |
440 |
|
|
return (-1); |
441 |
|
|
} |
442 |
|
|
|
443 |
|
|
return (0); |
444 |
|
|
} |
445 |
|
|
|
446 |
|
|
/* |
447 |
|
|
* Try to receive a valid packet from the current UDP socket. |
448 |
|
|
* |
449 |
|
|
* Return 0 if a full packet could be read, or -1 on error (errno set). |
450 |
|
|
*/ |
451 |
|
|
static int |
452 |
|
|
udp_recv(struct asr_query *as) |
453 |
|
|
{ |
454 |
|
|
ssize_t n; |
455 |
|
|
int save_errno; |
456 |
|
|
|
457 |
|
|
if (ensure_ibuf(as, MAXPACKETSZ) == -1) { |
458 |
|
|
save_errno = errno; |
459 |
|
|
close(as->as_fd); |
460 |
|
|
errno = save_errno; |
461 |
|
|
as->as_fd = -1; |
462 |
|
|
return (-1); |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0); |
466 |
|
|
save_errno = errno; |
467 |
|
|
close(as->as_fd); |
468 |
|
|
errno = save_errno; |
469 |
|
|
as->as_fd = -1; |
470 |
|
|
if (n == -1) |
471 |
|
|
return (-1); |
472 |
|
|
|
473 |
|
|
as->as.dns.ibuflen = n; |
474 |
|
|
|
475 |
|
|
DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen); |
476 |
|
|
|
477 |
|
|
if (validate_packet(as) == -1) |
478 |
|
|
return (-1); /* errno set */ |
479 |
|
|
|
480 |
|
|
return (0); |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
/* |
484 |
|
|
* Write the output packet to the TCP socket. |
485 |
|
|
* |
486 |
|
|
* Return 0 when all bytes have been sent, 1 there is no buffer space on the |
487 |
|
|
* socket or it is not connected yet, or -1 on error (errno set). |
488 |
|
|
*/ |
489 |
|
|
static int |
490 |
|
|
tcp_write(struct asr_query *as) |
491 |
|
|
{ |
492 |
|
|
struct msghdr msg; |
493 |
|
|
struct iovec iov[2]; |
494 |
|
|
uint16_t len; |
495 |
|
|
ssize_t n; |
496 |
|
|
size_t offset; |
497 |
|
|
int i; |
498 |
|
|
#ifdef DEBUG |
499 |
|
|
char buf[256]; |
500 |
|
|
#endif |
501 |
|
|
|
502 |
|
|
/* First try to connect if not already */ |
503 |
|
|
if (as->as_fd == -1) { |
504 |
|
|
DPRINT("asr: [%p] connecting to %s TCP\n", as, |
505 |
|
|
_asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); |
506 |
|
|
as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM); |
507 |
|
|
if (as->as_fd == -1) |
508 |
|
|
return (-1); /* errno set */ |
509 |
|
|
as->as.dns.datalen = 0; /* bytes sent */ |
510 |
|
|
return (1); |
511 |
|
|
} |
512 |
|
|
|
513 |
|
|
i = 0; |
514 |
|
|
|
515 |
|
|
/* Prepend de packet length if not sent already. */ |
516 |
|
|
if (as->as.dns.datalen < sizeof(len)) { |
517 |
|
|
offset = 0; |
518 |
|
|
len = htons(as->as.dns.obuflen); |
519 |
|
|
iov[i].iov_base = (char *)(&len) + as->as.dns.datalen; |
520 |
|
|
iov[i].iov_len = sizeof(len) - as->as.dns.datalen; |
521 |
|
|
i++; |
522 |
|
|
} else |
523 |
|
|
offset = as->as.dns.datalen - sizeof(len); |
524 |
|
|
|
525 |
|
|
iov[i].iov_base = as->as.dns.obuf + offset; |
526 |
|
|
iov[i].iov_len = as->as.dns.obuflen - offset; |
527 |
|
|
i++; |
528 |
|
|
|
529 |
|
|
memset(&msg, 0, sizeof msg); |
530 |
|
|
msg.msg_iov = iov; |
531 |
|
|
msg.msg_iovlen = i; |
532 |
|
|
|
533 |
|
|
send_again: |
534 |
|
|
n = sendmsg(as->as_fd, &msg, MSG_NOSIGNAL); |
535 |
|
|
if (n == -1) { |
536 |
|
|
if (errno == EINTR) |
537 |
|
|
goto send_again; |
538 |
|
|
goto close; /* errno set */ |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
as->as.dns.datalen += n; |
542 |
|
|
|
543 |
|
|
if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) { |
544 |
|
|
/* All sent. Prepare for TCP read */ |
545 |
|
|
as->as.dns.datalen = 0; |
546 |
|
|
return (0); |
547 |
|
|
} |
548 |
|
|
|
549 |
|
|
/* More data to write */ |
550 |
|
|
return (1); |
551 |
|
|
|
552 |
|
|
close: |
553 |
|
|
close(as->as_fd); |
554 |
|
|
as->as_fd = -1; |
555 |
|
|
return (-1); |
556 |
|
|
} |
557 |
|
|
|
558 |
|
|
/* |
559 |
|
|
* Try to read a valid packet from the current TCP socket. |
560 |
|
|
* |
561 |
|
|
* Return 0 if a full packet could be read, 1 if more data is needed and the |
562 |
|
|
* socket must be read again, or -1 on error (errno set). |
563 |
|
|
*/ |
564 |
|
|
static int |
565 |
|
|
tcp_read(struct asr_query *as) |
566 |
|
|
{ |
567 |
|
|
ssize_t n; |
568 |
|
|
size_t offset, len; |
569 |
|
|
char *pos; |
570 |
|
|
int save_errno, nfds; |
571 |
|
|
struct pollfd pfd; |
572 |
|
|
|
573 |
|
|
/* We must read the packet len first */ |
574 |
|
|
if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) { |
575 |
|
|
|
576 |
|
|
pos = (char *)(&as->as.dns.pktlen) + as->as.dns.datalen; |
577 |
|
|
len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen; |
578 |
|
|
|
579 |
|
|
n = read(as->as_fd, pos, len); |
580 |
|
|
if (n == -1) |
581 |
|
|
goto close; /* errno set */ |
582 |
|
|
|
583 |
|
|
as->as.dns.datalen += n; |
584 |
|
|
if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) |
585 |
|
|
return (1); /* need more data */ |
586 |
|
|
|
587 |
|
|
as->as.dns.ibuflen = ntohs(as->as.dns.pktlen); |
588 |
|
|
if (ensure_ibuf(as, as->as.dns.ibuflen) == -1) |
589 |
|
|
goto close; /* errno set */ |
590 |
|
|
|
591 |
|
|
pfd.fd = as->as_fd; |
592 |
|
|
pfd.events = POLLIN; |
593 |
|
|
poll_again: |
594 |
|
|
nfds = poll(&pfd, 1, 0); |
595 |
|
|
if (nfds == -1) { |
596 |
|
|
if (errno == EINTR) |
597 |
|
|
goto poll_again; |
598 |
|
|
goto close; /* errno set */ |
599 |
|
|
} |
600 |
|
|
if (nfds == 0) |
601 |
|
|
return (1); /* no more data available */ |
602 |
|
|
} |
603 |
|
|
|
604 |
|
|
offset = as->as.dns.datalen - sizeof(as->as.dns.pktlen); |
605 |
|
|
pos = as->as.dns.ibuf + offset; |
606 |
|
|
len = as->as.dns.ibuflen - offset; |
607 |
|
|
|
608 |
|
|
read_again: |
609 |
|
|
n = read(as->as_fd, pos, len); |
610 |
|
|
if (n == -1) { |
611 |
|
|
if (errno == EINTR) |
612 |
|
|
goto read_again; |
613 |
|
|
goto close; /* errno set */ |
614 |
|
|
} |
615 |
|
|
if (n == 0) { |
616 |
|
|
errno = ECONNRESET; |
617 |
|
|
goto close; |
618 |
|
|
} |
619 |
|
|
as->as.dns.datalen += n; |
620 |
|
|
|
621 |
|
|
/* See if we got all the advertised bytes. */ |
622 |
|
|
if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen)) |
623 |
|
|
return (1); |
624 |
|
|
|
625 |
|
|
DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen); |
626 |
|
|
|
627 |
|
|
if (validate_packet(as) == -1) |
628 |
|
|
goto close; /* errno set */ |
629 |
|
|
|
630 |
|
|
errno = 0; |
631 |
|
|
close: |
632 |
|
|
save_errno = errno; |
633 |
|
|
close(as->as_fd); |
634 |
|
|
errno = save_errno; |
635 |
|
|
as->as_fd = -1; |
636 |
|
|
return (errno ? -1 : 0); |
637 |
|
|
} |
638 |
|
|
|
639 |
|
|
/* |
640 |
|
|
* Make sure the input buffer is at least "n" bytes long, and allocate or |
641 |
|
|
* extend it if necessary. Return 0 on success, or set errno and return -1. |
642 |
|
|
*/ |
643 |
|
|
static int |
644 |
|
|
ensure_ibuf(struct asr_query *as, size_t n) |
645 |
|
|
{ |
646 |
|
|
char *t; |
647 |
|
|
|
648 |
|
|
if (as->as.dns.ibufsize >= n) |
649 |
|
|
return (0); |
650 |
|
|
|
651 |
|
|
t = recallocarray(as->as.dns.ibuf, as->as.dns.ibufsize, n, 1); |
652 |
|
|
if (t == NULL) |
653 |
|
|
return (-1); /* errno set */ |
654 |
|
|
as->as.dns.ibuf = t; |
655 |
|
|
as->as.dns.ibufsize = n; |
656 |
|
|
|
657 |
|
|
return (0); |
658 |
|
|
} |
659 |
|
|
|
660 |
|
|
/* |
661 |
|
|
* Check if the received packet is valid. |
662 |
|
|
* Return 0 on success, or set errno and return -1. |
663 |
|
|
*/ |
664 |
|
|
static int |
665 |
|
|
validate_packet(struct asr_query *as) |
666 |
|
|
{ |
667 |
|
|
struct asr_unpack p; |
668 |
|
|
struct asr_dns_header h; |
669 |
|
|
struct asr_dns_query q; |
670 |
|
|
struct asr_dns_rr rr; |
671 |
|
|
int r; |
672 |
|
|
|
673 |
|
|
_asr_unpack_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen); |
674 |
|
|
|
675 |
|
|
_asr_unpack_header(&p, &h); |
676 |
|
|
if (p.err) |
677 |
|
|
goto inval; |
678 |
|
|
|
679 |
|
|
if (h.id != as->as.dns.reqid) { |
680 |
|
|
DPRINT("incorrect reqid\n"); |
681 |
|
|
goto inval; |
682 |
|
|
} |
683 |
|
|
if (h.qdcount != 1) |
684 |
|
|
goto inval; |
685 |
|
|
/* Should be zero, we could allow this */ |
686 |
|
|
if ((h.flags & Z_MASK) != 0) |
687 |
|
|
goto inval; |
688 |
|
|
/* Actually, it depends on the request but we only use OP_QUERY */ |
689 |
|
|
if (OPCODE(h.flags) != OP_QUERY) |
690 |
|
|
goto inval; |
691 |
|
|
/* Must be a response */ |
692 |
|
|
if ((h.flags & QR_MASK) == 0) |
693 |
|
|
goto inval; |
694 |
|
|
|
695 |
|
|
as->as.dns.rcode = RCODE(h.flags); |
696 |
|
|
as->as.dns.ancount = h.ancount; |
697 |
|
|
|
698 |
|
|
_asr_unpack_query(&p, &q); |
699 |
|
|
if (p.err) |
700 |
|
|
goto inval; |
701 |
|
|
|
702 |
|
|
if (q.q_type != as->as.dns.type || |
703 |
|
|
q.q_class != as->as.dns.class || |
704 |
|
|
strcasecmp(q.q_dname, as->as.dns.dname)) { |
705 |
|
|
DPRINT("incorrect type/class/dname '%s' != '%s'\n", |
706 |
|
|
q.q_dname, as->as.dns.dname); |
707 |
|
|
goto inval; |
708 |
|
|
} |
709 |
|
|
|
710 |
|
|
/* Check for truncation */ |
711 |
|
|
if (h.flags & TC_MASK && !(as->as_ctx->ac_options & RES_IGNTC)) { |
712 |
|
|
DPRINT("truncated\n"); |
713 |
|
|
errno = EOVERFLOW; |
714 |
|
|
return (-1); |
715 |
|
|
} |
716 |
|
|
|
717 |
|
|
/* Validate the rest of the packet */ |
718 |
|
|
for (r = h.ancount + h.nscount + h.arcount; r; r--) |
719 |
|
|
_asr_unpack_rr(&p, &rr); |
720 |
|
|
|
721 |
|
|
/* Report any error found when unpacking the RRs. */ |
722 |
|
|
if (p.err) { |
723 |
|
|
DPRINT("unpack: %s\n", strerror(p.err)); |
724 |
|
|
errno = p.err; |
725 |
|
|
return (-1); |
726 |
|
|
} |
727 |
|
|
|
728 |
|
|
if (p.offset != as->as.dns.ibuflen) { |
729 |
|
|
DPRINT("trailing garbage\n"); |
730 |
|
|
errno = EMSGSIZE; |
731 |
|
|
return (-1); |
732 |
|
|
} |
733 |
|
|
|
734 |
|
|
return (0); |
735 |
|
|
|
736 |
|
|
inval: |
737 |
|
|
errno = EINVAL; |
738 |
|
|
return (-1); |
739 |
|
|
} |
740 |
|
|
|
741 |
|
|
/* |
742 |
|
|
* Set the async context nameserver index to the next nameserver, cycling |
743 |
|
|
* over the list until the maximum retry counter is reached. Return 0 on |
744 |
|
|
* success, or -1 if all nameservers were used. |
745 |
|
|
*/ |
746 |
|
|
static int |
747 |
|
|
iter_ns(struct asr_query *as) |
748 |
|
|
{ |
749 |
|
|
for (;;) { |
750 |
|
|
if (as->as.dns.nsloop >= as->as_ctx->ac_nsretries) |
751 |
|
|
return (-1); |
752 |
|
|
|
753 |
|
|
as->as.dns.nsidx += 1; |
754 |
|
|
if (as->as.dns.nsidx <= as->as_ctx->ac_nscount) |
755 |
|
|
break; |
756 |
|
|
as->as.dns.nsidx = 0; |
757 |
|
|
as->as.dns.nsloop++; |
758 |
|
|
DPRINT("asr: iter_ns(): cycle %i\n", as->as.dns.nsloop); |
759 |
|
|
} |
760 |
|
|
|
761 |
|
|
as->as_timeout = 1000 * (as->as_ctx->ac_nstimeout << as->as.dns.nsloop); |
762 |
|
|
if (as->as.dns.nsloop > 0) |
763 |
|
|
as->as_timeout /= as->as_ctx->ac_nscount; |
764 |
|
|
if (as->as_timeout < 1000) |
765 |
|
|
as->as_timeout = 1000; |
766 |
|
|
|
767 |
|
|
return (0); |
768 |
|
|
} |