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