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