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