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