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