1 /* $OpenBSD: res_send_async.c,v 1.22 2014/03/26 18:13:15 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 "includes.h"
19
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/uio.h>
23 #include <netinet/in.h>
24 #include <arpa/nameser.h>
25 #include <netdb.h>
26
27 #include <asr.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <poll.h>
31 #include <resolv.h> /* for res_random */
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35
36 #include "asr_private.h"
37
38 #define OP_QUERY (0)
39
40 static int res_send_async_run(struct asr_query *, struct asr_result *);
41 static int sockaddr_connect(const struct sockaddr *, int);
42 static int udp_send(struct asr_query *);
43 static int udp_recv(struct asr_query *);
44 static int tcp_write(struct asr_query *);
45 static int tcp_read(struct asr_query *);
46 static int validate_packet(struct asr_query *);
47 static int setup_query(struct asr_query *, const char *, const char *, int, int);
48 static int ensure_ibuf(struct asr_query *, size_t);
49 static int iter_ns(struct asr_query *);
50
51 #define AS_NS_SA(p) ((p)->as_ctx->ac_ns[(p)->as.dns.nsidx - 1])
52
53
54 struct asr_query *
res_send_async(const unsigned char * buf,int buflen,void * asr)55 res_send_async(const unsigned char *buf, int buflen, void *asr)
56 {
57 struct asr_ctx *ac;
58 struct asr_query *as;
59 struct asr_unpack p;
60 struct asr_dns_header h;
61 struct asr_dns_query q;
62
63 DPRINT_PACKET("asr: res_send_async()", buf, buflen);
64
65 ac = _asr_use_resolver(asr);
66 if ((as = _asr_async_new(ac, ASR_SEND)) == NULL) {
67 _asr_ctx_unref(ac);
68 return (NULL); /* errno set */
69 }
70 as->as_run = res_send_async_run;
71
72 as->as.dns.flags |= ASYNC_EXTOBUF;
73 as->as.dns.obuf = (unsigned char *)buf;
74 as->as.dns.obuflen = buflen;
75 as->as.dns.obufsize = buflen;
76
77 _asr_unpack_init(&p, buf, buflen);
78 _asr_unpack_header(&p, &h);
79 _asr_unpack_query(&p, &q);
80 if (p.err) {
81 errno = EINVAL;
82 goto err;
83 }
84 as->as.dns.reqid = h.id;
85 as->as.dns.type = q.q_type;
86 as->as.dns.class = q.q_class;
87 as->as.dns.dname = strdup(q.q_dname);
88 if (as->as.dns.dname == NULL)
89 goto err; /* errno set */
90
91 _asr_ctx_unref(ac);
92 return (as);
93 err:
94 if (as)
95 _asr_async_free(as);
96 _asr_ctx_unref(ac);
97 return (NULL);
98 }
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
120 struct asr_query *
_res_query_async_ctx(const char * name,int class,int type,struct asr_ctx * a_ctx)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
res_send_async_run(struct asr_query * as,struct asr_result * ar)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), SA_LEN(AS_NS_SA(as)));
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
sockaddr_connect(const struct sockaddr * sa,int socktype)303 sockaddr_connect(const struct sockaddr *sa, int socktype)
304 {
305 int errno_save, flags, sock;
306
307 if ((sock = socket(sa->sa_family, socktype, 0)) == -1)
308 goto fail;
309
310 if ((flags = fcntl(sock, F_GETFL, 0)) == -1)
311 goto fail;
312
313 flags |= O_NONBLOCK;
314
315 if ((flags = fcntl(sock, F_SETFL, flags)) == -1)
316 goto fail;
317
318 if (connect(sock, sa, SA_LEN(sa)) == -1) {
319 /*
320 * In the TCP case, the caller will be asked to poll for
321 * POLLOUT so that we start writing the packet in tcp_write()
322 * when the connection is established, or fail there on error.
323 */
324 if (errno == EINPROGRESS)
325 return (sock);
326 goto fail;
327 }
328
329 return (sock);
330
331 fail:
332
333 if (sock != -1) {
334 errno_save = errno;
335 close(sock);
336 errno = errno_save;
337 }
338
339 return (-1);
340 }
341
342 /*
343 * Prepare the DNS packet for the query type "type", class "class" and domain
344 * name created by the concatenation on "name" and "dom".
345 * Return 0 on success, set errno and return -1 on error.
346 */
347 static int
setup_query(struct asr_query * as,const char * name,const char * dom,int class,int type)348 setup_query(struct asr_query *as, const char *name, const char *dom,
349 int class, int type)
350 {
351 struct asr_pack p;
352 struct asr_dns_header h;
353 char fqdn[MAXDNAME];
354 char dname[MAXDNAME];
355
356 if (as->as.dns.flags & ASYNC_EXTOBUF) {
357 errno = EINVAL;
358 DPRINT("attempting to write in user packet");
359 return (-1);
360 }
361
362 if (_asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) {
363 errno = EINVAL;
364 DPRINT("asr_make_fqdn: name too long\n");
365 return (-1);
366 }
367
368 if (_asr_dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) {
369 errno = EINVAL;
370 DPRINT("asr_dname_from_fqdn: invalid\n");
371 return (-1);
372 }
373
374 if (as->as.dns.obuf == NULL) {
375 as->as.dns.obufsize = PACKETSZ;
376 as->as.dns.obuf = malloc(as->as.dns.obufsize);
377 if (as->as.dns.obuf == NULL)
378 return (-1); /* errno set */
379 }
380 as->as.dns.obuflen = 0;
381
382 memset(&h, 0, sizeof h);
383 h.id = res_randomid();
384 if (as->as_ctx->ac_options & RES_RECURSE)
385 h.flags |= RD_MASK;
386 h.qdcount = 1;
387
388 _asr_pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize);
389 _asr_pack_header(&p, &h);
390 _asr_pack_query(&p, type, class, dname);
391 if (p.err) {
392 DPRINT("error packing query");
393 errno = EINVAL;
394 return (-1);
395 }
396
397 /* Remember the parameters. */
398 as->as.dns.reqid = h.id;
399 as->as.dns.type = type;
400 as->as.dns.class = class;
401 if (as->as.dns.dname)
402 free(as->as.dns.dname);
403 as->as.dns.dname = strdup(dname);
404 if (as->as.dns.dname == NULL) {
405 DPRINT("strdup");
406 return (-1); /* errno set */
407 }
408 as->as.dns.obuflen = p.offset;
409
410 DPRINT_PACKET("asr_setup_query", as->as.dns.obuf, as->as.dns.obuflen);
411
412 return (0);
413 }
414
415 /*
416 * Create a connect UDP socket and send the output packet.
417 *
418 * Return 0 on success, or -1 on error (errno set).
419 */
420 static int
udp_send(struct asr_query * as)421 udp_send(struct asr_query *as)
422 {
423 ssize_t n;
424 int save_errno;
425 #ifdef DEBUG
426 char buf[256];
427 #endif
428
429 DPRINT("asr: [%p] connecting to %s UDP\n", as,
430 _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf));
431
432 as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM);
433 if (as->as_fd == -1)
434 return (-1); /* errno set */
435
436 n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0);
437 if (n == -1) {
438 save_errno = errno;
439 close(as->as_fd);
440 errno = save_errno;
441 as->as_fd = -1;
442 return (-1);
443 }
444
445 return (0);
446 }
447
448 /*
449 * Try to receive a valid packet from the current UDP socket.
450 *
451 * Return 0 if a full packet could be read, or -1 on error (errno set).
452 */
453 static int
udp_recv(struct asr_query * as)454 udp_recv(struct asr_query *as)
455 {
456 ssize_t n;
457 int save_errno;
458
459 if (ensure_ibuf(as, PACKETSZ) == -1) {
460 save_errno = errno;
461 close(as->as_fd);
462 errno = save_errno;
463 as->as_fd = -1;
464 return (-1);
465 }
466
467 n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0);
468 save_errno = errno;
469 close(as->as_fd);
470 errno = save_errno;
471 as->as_fd = -1;
472 if (n == -1)
473 return (-1);
474
475 as->as.dns.ibuflen = n;
476
477 DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen);
478
479 if (validate_packet(as) == -1)
480 return (-1); /* errno set */
481
482 return (0);
483 }
484
485 /*
486 * Write the output packet to the TCP socket.
487 *
488 * Return 0 when all bytes have been sent, 1 there is no buffer space on the
489 * socket or it is not connected yet, or -1 on error (errno set).
490 */
491 static int
tcp_write(struct asr_query * as)492 tcp_write(struct asr_query *as)
493 {
494 struct msghdr msg;
495 struct iovec iov[2];
496 uint16_t len;
497 ssize_t n;
498 size_t offset;
499 int i;
500 #ifdef DEBUG
501 char buf[256];
502 #endif
503
504 /* First try to connect if not already */
505 if (as->as_fd == -1) {
506 DPRINT("asr: [%p] connecting to %s TCP\n", as,
507 _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf));
508 as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM);
509 if (as->as_fd == -1)
510 return (-1); /* errno set */
511 /*
512 * Some systems (MacOS X) have SO_NOSIGPIPE instead of MSG_NOSIGNAL.
513 * If neither is available the system is probably broken. We might
514 * want to detect this at configure time.
515 */
516 #ifdef SO_NOSIGPIPE
517 i = 1;
518 if (setsockopt(as->as_fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&i,
519 sizeof(i)) == -1)
520 return (-1); /* errno set */
521 #endif
522 as->as.dns.datalen = 0; /* bytes sent */
523 return (1);
524 }
525
526 i = 0;
527
528 /* Prepend de packet length if not sent already. */
529 if (as->as.dns.datalen < sizeof(len)) {
530 offset = 0;
531 len = htons(as->as.dns.obuflen);
532 iov[i].iov_base = (char *)(&len) + as->as.dns.datalen;
533 iov[i].iov_len = sizeof(len) - as->as.dns.datalen;
534 i++;
535 } else
536 offset = as->as.dns.datalen - sizeof(len);
537
538 iov[i].iov_base = as->as.dns.obuf + offset;
539 iov[i].iov_len = as->as.dns.obuflen - offset;
540 i++;
541
542 memset(&msg, 0, sizeof msg);
543 msg.msg_iov = iov;
544 msg.msg_iovlen = i;
545
546 send_again:
547 /* See above. */
548 #ifndef MSG_NOSIGNAL
549 #define MSG_NOSIGNAL 0
550 #endif
551 n = sendmsg(as->as_fd, &msg, MSG_NOSIGNAL);
552 if (n == -1) {
553 if (errno == EINTR)
554 goto send_again;
555 goto close; /* errno set */
556 }
557
558 as->as.dns.datalen += n;
559
560 if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) {
561 /* All sent. Prepare for TCP read */
562 as->as.dns.datalen = 0;
563 return (0);
564 }
565
566 /* More data to write */
567 return (1);
568
569 close:
570 close(as->as_fd);
571 as->as_fd = -1;
572 return (-1);
573 }
574
575 /*
576 * Try to read a valid packet from the current TCP socket.
577 *
578 * Return 0 if a full packet could be read, 1 if more data is needed and the
579 * socket must be read again, or -1 on error (errno set).
580 */
581 static int
tcp_read(struct asr_query * as)582 tcp_read(struct asr_query *as)
583 {
584 ssize_t n;
585 size_t offset, len;
586 char *pos;
587 int save_errno, nfds;
588 struct pollfd pfd;
589
590 /* We must read the packet len first */
591 if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) {
592
593 pos = (char *)(&as->as.dns.pktlen) + as->as.dns.datalen;
594 len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen;
595
596 n = read(as->as_fd, pos, len);
597 if (n == -1)
598 goto close; /* errno set */
599
600 as->as.dns.datalen += n;
601 if (as->as.dns.datalen < sizeof(as->as.dns.pktlen))
602 return (1); /* need more data */
603
604 as->as.dns.ibuflen = ntohs(as->as.dns.pktlen);
605 if (ensure_ibuf(as, as->as.dns.ibuflen) == -1)
606 goto close; /* errno set */
607
608 pfd.fd = as->as_fd;
609 pfd.events = POLLIN;
610 poll_again:
611 nfds = poll(&pfd, 1, 0);
612 if (nfds == -1) {
613 if (errno == EINTR)
614 goto poll_again;
615 goto close; /* errno set */
616 }
617 if (nfds == 0)
618 return (1); /* no more data available */
619 }
620
621 offset = as->as.dns.datalen - sizeof(as->as.dns.pktlen);
622 pos = as->as.dns.ibuf + offset;
623 len = as->as.dns.ibuflen - offset;
624
625 read_again:
626 n = read(as->as_fd, pos, len);
627 if (n == -1) {
628 if (errno == EINTR)
629 goto read_again;
630 goto close; /* errno set */
631 }
632 if (n == 0) {
633 errno = ECONNRESET;
634 goto close;
635 }
636 as->as.dns.datalen += n;
637
638 /* See if we got all the advertised bytes. */
639 if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen))
640 return (1);
641
642 DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen);
643
644 if (validate_packet(as) == -1)
645 goto close; /* errno set */
646
647 errno = 0;
648 close:
649 save_errno = errno;
650 close(as->as_fd);
651 errno = save_errno;
652 as->as_fd = -1;
653 return (errno ? -1 : 0);
654 }
655
656 /*
657 * Make sure the input buffer is at least "n" bytes long, and allocate or
658 * extend it if necessary. Return 0 on success, or set errno and return -1.
659 */
660 static int
ensure_ibuf(struct asr_query * as,size_t n)661 ensure_ibuf(struct asr_query *as, size_t n)
662 {
663 char *t;
664
665 if (as->as.dns.ibuf == NULL) {
666 as->as.dns.ibuf = malloc(n);
667 if (as->as.dns.ibuf == NULL)
668 return (-1); /* errno set */
669 as->as.dns.ibufsize = n;
670 return (0);
671 }
672
673 if (as->as.dns.ibufsize >= n)
674 return (0);
675
676 t = realloc(as->as.dns.ibuf, n);
677 if (t == NULL)
678 return (-1); /* errno set */
679 as->as.dns.ibuf = t;
680 as->as.dns.ibufsize = n;
681
682 return (0);
683 }
684
685 /*
686 * Check if the received packet is valid.
687 * Return 0 on success, or set errno and return -1.
688 */
689 static int
validate_packet(struct asr_query * as)690 validate_packet(struct asr_query *as)
691 {
692 struct asr_unpack p;
693 struct asr_dns_header h;
694 struct asr_dns_query q;
695 struct asr_dns_rr rr;
696 int r;
697
698 _asr_unpack_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen);
699
700 _asr_unpack_header(&p, &h);
701 if (p.err)
702 goto inval;
703
704 if (h.id != as->as.dns.reqid) {
705 DPRINT("incorrect reqid\n");
706 goto inval;
707 }
708 if (h.qdcount != 1)
709 goto inval;
710 /* Should be zero, we could allow this */
711 if ((h.flags & Z_MASK) != 0)
712 goto inval;
713 /* Actually, it depends on the request but we only use OP_QUERY */
714 if (OPCODE(h.flags) != OP_QUERY)
715 goto inval;
716 /* Must be a response */
717 if ((h.flags & QR_MASK) == 0)
718 goto inval;
719
720 as->as.dns.rcode = RCODE(h.flags);
721 as->as.dns.ancount = h.ancount;
722
723 _asr_unpack_query(&p, &q);
724 if (p.err)
725 goto inval;
726
727 if (q.q_type != as->as.dns.type ||
728 q.q_class != as->as.dns.class ||
729 strcasecmp(q.q_dname, as->as.dns.dname)) {
730 DPRINT("incorrect type/class/dname '%s' != '%s'\n",
731 q.q_dname, as->as.dns.dname);
732 goto inval;
733 }
734
735 /* Check for truncation */
736 if (h.flags & TC_MASK) {
737 errno = EOVERFLOW;
738 return (-1);
739 }
740
741 /* Validate the rest of the packet */
742 for (r = h.ancount + h.nscount + h.arcount; r; r--)
743 _asr_unpack_rr(&p, &rr);
744
745 if (p.err || (p.offset != as->as.dns.ibuflen))
746 goto inval;
747
748 return (0);
749
750 inval:
751 errno = EINVAL;
752 return (-1);
753 }
754
755 /*
756 * Set the async context nameserver index to the next nameserver, cycling
757 * over the list until the maximum retry counter is reached. Return 0 on
758 * success, or -1 if all nameservers were used.
759 */
760 static int
iter_ns(struct asr_query * as)761 iter_ns(struct asr_query *as)
762 {
763 for (;;) {
764 if (as->as.dns.nsloop >= as->as_ctx->ac_nsretries)
765 return (-1);
766
767 as->as.dns.nsidx += 1;
768 if (as->as.dns.nsidx <= as->as_ctx->ac_nscount)
769 break;
770 as->as.dns.nsidx = 0;
771 as->as.dns.nsloop++;
772 DPRINT("asr: iter_ns(): cycle %i\n", as->as.dns.nsloop);
773 }
774
775 as->as_timeout = 1000 * (as->as_ctx->ac_nstimeout << as->as.dns.nsloop);
776 if (as->as.dns.nsloop > 0)
777 as->as_timeout /= as->as_ctx->ac_nscount;
778 if (as->as_timeout < 1000)
779 as->as_timeout = 1000;
780
781 return (0);
782 }
783