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