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