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