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