xref: /openbsd/lib/libc/asr/res_send_async.c (revision 274d7c50)
1 /*	$OpenBSD: res_send_async.c,v 1.39 2019/09/28 11:21:07 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 <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 	if (as->as_ctx->ac_options & RES_USE_CD)
380 		h.flags |= CD_MASK;
381 	h.qdcount = 1;
382 	if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
383 		h.arcount = 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 (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
389 		_asr_pack_edns0(&p, MAXPACKETSZ,
390 		    as->as_ctx->ac_options & RES_USE_DNSSEC);
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
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
454 udp_recv(struct asr_query *as)
455 {
456 	ssize_t		 n;
457 	int		 save_errno;
458 
459 	if (ensure_ibuf(as, MAXPACKETSZ) == -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
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 		as->as.dns.datalen = 0; /* bytes sent */
512 		return (1);
513 	}
514 
515 	i = 0;
516 
517 	/* Prepend de packet length if not sent already. */
518 	if (as->as.dns.datalen < sizeof(len)) {
519 		offset = 0;
520 		len = htons(as->as.dns.obuflen);
521 		iov[i].iov_base = (char *)(&len) + as->as.dns.datalen;
522 		iov[i].iov_len = sizeof(len) - as->as.dns.datalen;
523 		i++;
524 	} else
525 		offset = as->as.dns.datalen - sizeof(len);
526 
527 	iov[i].iov_base = as->as.dns.obuf + offset;
528 	iov[i].iov_len = as->as.dns.obuflen - offset;
529 	i++;
530 
531 	memset(&msg, 0, sizeof msg);
532 	msg.msg_iov = iov;
533 	msg.msg_iovlen = i;
534 
535     send_again:
536 	n = sendmsg(as->as_fd, &msg, MSG_NOSIGNAL);
537 	if (n == -1) {
538 		if (errno == EINTR)
539 			goto send_again;
540 		goto close; /* errno set */
541 	}
542 
543 	as->as.dns.datalen += n;
544 
545 	if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) {
546 		/* All sent. Prepare for TCP read */
547 		as->as.dns.datalen = 0;
548 		return (0);
549 	}
550 
551 	/* More data to write */
552 	return (1);
553 
554 close:
555 	close(as->as_fd);
556 	as->as_fd = -1;
557 	return (-1);
558 }
559 
560 /*
561  * Try to read a valid packet from the current TCP socket.
562  *
563  * Return 0 if a full packet could be read, 1 if more data is needed and the
564  * socket must be read again, or -1 on error (errno set).
565  */
566 static int
567 tcp_read(struct asr_query *as)
568 {
569 	ssize_t		 n;
570 	size_t		 offset, len;
571 	char		*pos;
572 	int		 save_errno, nfds;
573 	struct pollfd	 pfd;
574 
575 	/* We must read the packet len first */
576 	if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) {
577 
578 		pos = (char *)(&as->as.dns.pktlen) + as->as.dns.datalen;
579 		len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen;
580 
581     read_again0:
582 		n = read(as->as_fd, pos, len);
583 		if (n == -1) {
584 			if (errno == EINTR)
585 				goto read_again0;
586 			goto close; /* errno set */
587 		}
588 		if (n == 0) {
589 			errno = ECONNRESET;
590 			goto close;
591 		}
592 		as->as.dns.datalen += n;
593 		if (as->as.dns.datalen < sizeof(as->as.dns.pktlen))
594 			return (1); /* need more data */
595 
596 		as->as.dns.ibuflen = ntohs(as->as.dns.pktlen);
597 		if (ensure_ibuf(as, as->as.dns.ibuflen) == -1)
598 			goto close; /* errno set */
599 
600 		pfd.fd = as->as_fd;
601 		pfd.events = POLLIN;
602 	    poll_again:
603 		nfds = poll(&pfd, 1, 0);
604 		if (nfds == -1) {
605 			if (errno == EINTR)
606 				goto poll_again;
607 			goto close; /* errno set */
608 		}
609 		if (nfds == 0)
610 			return (1); /* no more data available */
611 	}
612 
613 	offset = as->as.dns.datalen - sizeof(as->as.dns.pktlen);
614 	pos = as->as.dns.ibuf + offset;
615 	len =  as->as.dns.ibuflen - offset;
616 
617     read_again:
618 	n = read(as->as_fd, pos, len);
619 	if (n == -1) {
620 		if (errno == EINTR)
621 			goto read_again;
622 		goto close; /* errno set */
623 	}
624 	if (n == 0) {
625 		errno = ECONNRESET;
626 		goto close;
627 	}
628 	as->as.dns.datalen += n;
629 
630 	/* See if we got all the advertised bytes. */
631 	if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen))
632 		return (1);
633 
634 	DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen);
635 
636 	if (validate_packet(as) == -1)
637 		goto close; /* errno set */
638 
639 	errno = 0;
640 close:
641 	save_errno = errno;
642 	close(as->as_fd);
643 	errno = save_errno;
644 	as->as_fd = -1;
645 	return (errno ? -1 : 0);
646 }
647 
648 /*
649  * Make sure the input buffer is at least "n" bytes long, and allocate or
650  * extend it if necessary. Return 0 on success, or set errno and return -1.
651  */
652 static int
653 ensure_ibuf(struct asr_query *as, size_t n)
654 {
655 	char	*t;
656 
657 	if (as->as.dns.ibufsize >= n)
658 		return (0);
659 
660 	t = recallocarray(as->as.dns.ibuf, as->as.dns.ibufsize, n, 1);
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 && !(as->as_ctx->ac_options & RES_IGNTC)) {
721 		DPRINT("truncated\n");
722 		errno = EOVERFLOW;
723 		return (-1);
724 	}
725 
726 	/* Validate the rest of the packet */
727 	for (r = h.ancount + h.nscount + h.arcount; r; r--)
728 		_asr_unpack_rr(&p, &rr);
729 
730 	/* Report any error found when unpacking the RRs. */
731 	if (p.err) {
732 		DPRINT("unpack: %s\n", strerror(p.err));
733 		errno = p.err;
734 		return (-1);
735 	}
736 
737 	if (p.offset != as->as.dns.ibuflen) {
738 		DPRINT("trailing garbage\n");
739 		errno = EMSGSIZE;
740 		return (-1);
741 	}
742 
743 	return (0);
744 
745     inval:
746 	errno = EINVAL;
747 	return (-1);
748 }
749 
750 /*
751  * Set the async context nameserver index to the next nameserver, cycling
752  * over the list until the maximum retry counter is reached.  Return 0 on
753  * success, or -1 if all nameservers were used.
754  */
755 static int
756 iter_ns(struct asr_query *as)
757 {
758 	for (;;) {
759 		if (as->as.dns.nsloop >= as->as_ctx->ac_nsretries)
760 			return (-1);
761 
762 		as->as.dns.nsidx += 1;
763 		if (as->as.dns.nsidx <= as->as_ctx->ac_nscount)
764 			break;
765 		as->as.dns.nsidx = 0;
766 		as->as.dns.nsloop++;
767 		DPRINT("asr: iter_ns(): cycle %i\n", as->as.dns.nsloop);
768 	}
769 
770 	as->as_timeout = 1000 * (as->as_ctx->ac_nstimeout << as->as.dns.nsloop);
771 	if (as->as.dns.nsloop > 0)
772 		as->as_timeout /= as->as_ctx->ac_nscount;
773 	if (as->as_timeout < 1000)
774 		as->as_timeout = 1000;
775 
776 	return (0);
777 }
778