xref: /openbsd-src/lib/libc/asr/res_send_async.c (revision a34e6b005929e2b8f34fad2ab5a373c8a3ed18f4)
1*a34e6b00Sjca /*	$OpenBSD: res_send_async.c,v 1.41 2022/06/20 06:45:31 jca Exp $	*/
2b44da627Seric /*
3b44da627Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4b44da627Seric  *
5b44da627Seric  * Permission to use, copy, modify, and distribute this software for any
6b44da627Seric  * purpose with or without fee is hereby granted, provided that the above
7b44da627Seric  * copyright notice and this permission notice appear in all copies.
8b44da627Seric  *
9b44da627Seric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b44da627Seric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b44da627Seric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b44da627Seric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b44da627Seric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b44da627Seric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b44da627Seric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b44da627Seric  */
17b44da627Seric 
18b44da627Seric #include <sys/types.h>
19d216d6b1Seric #include <sys/socket.h>
20b44da627Seric #include <sys/uio.h>
21b44da627Seric #include <netinet/in.h>
22b44da627Seric #include <arpa/nameser.h>
23d216d6b1Seric #include <netdb.h>
24b44da627Seric 
25d216d6b1Seric #include <asr.h>
26b44da627Seric #include <errno.h>
27b44da627Seric #include <fcntl.h>
287856d263Seric #include <poll.h>
29b44da627Seric #include <resolv.h> /* for res_random */
30b44da627Seric #include <stdlib.h>
31b44da627Seric #include <string.h>
32b44da627Seric #include <unistd.h>
33b44da627Seric 
34b44da627Seric #include "asr_private.h"
35b44da627Seric 
36b44da627Seric #define OP_QUERY    (0)
37b44da627Seric 
385be03f8fSeric static int res_send_async_run(struct asr_query *, struct asr_result *);
39b44da627Seric static int sockaddr_connect(const struct sockaddr *, int);
405be03f8fSeric static int udp_send(struct asr_query *);
415be03f8fSeric static int udp_recv(struct asr_query *);
425be03f8fSeric static int tcp_write(struct asr_query *);
435be03f8fSeric static int tcp_read(struct asr_query *);
445be03f8fSeric static int validate_packet(struct asr_query *);
45931108e9Sjca static void clear_ad(struct asr_result *);
465be03f8fSeric static int setup_query(struct asr_query *, const char *, const char *, int, int);
475be03f8fSeric static int ensure_ibuf(struct asr_query *, size_t);
485be03f8fSeric static int iter_ns(struct asr_query *);
49b44da627Seric 
50d3064b1fSeric #define AS_NS_SA(p) ((p)->as_ctx->ac_ns[(p)->as.dns.nsidx - 1])
51b44da627Seric 
52b44da627Seric 
535be03f8fSeric struct asr_query *
res_send_async(const unsigned char * buf,int buflen,void * asr)545be03f8fSeric res_send_async(const unsigned char *buf, int buflen, void *asr)
55b44da627Seric {
56b44da627Seric 	struct asr_ctx		*ac;
575be03f8fSeric 	struct asr_query	*as;
58f90bf415Seric 	struct asr_unpack	 p;
59f90bf415Seric 	struct asr_dns_header	 h;
60f90bf415Seric 	struct asr_dns_query	 q;
61b44da627Seric 
6246ab4803Seric 	DPRINT_PACKET("asr: res_send_async()", buf, buflen);
6346ab4803Seric 
64253ef892Sderaadt 	ac = _asr_use_resolver(asr);
65253ef892Sderaadt 	if ((as = _asr_async_new(ac, ASR_SEND)) == NULL) {
66253ef892Sderaadt 		_asr_ctx_unref(ac);
67b44da627Seric 		return (NULL); /* errno set */
68b44da627Seric 	}
69b44da627Seric 	as->as_run = res_send_async_run;
70b44da627Seric 
71abe78e02Sjca 	as->as_flags |= ASYNC_EXTOBUF;
72b44da627Seric 	as->as.dns.obuf = (unsigned char *)buf;
73b44da627Seric 	as->as.dns.obuflen = buflen;
74b44da627Seric 	as->as.dns.obufsize = buflen;
75b44da627Seric 
76253ef892Sderaadt 	_asr_unpack_init(&p, buf, buflen);
77253ef892Sderaadt 	_asr_unpack_header(&p, &h);
78253ef892Sderaadt 	_asr_unpack_query(&p, &q);
79b44da627Seric 	if (p.err) {
80b44da627Seric 		errno = EINVAL;
81b44da627Seric 		goto err;
82b44da627Seric 	}
83b44da627Seric 	as->as.dns.reqid = h.id;
84b44da627Seric 	as->as.dns.type = q.q_type;
85b44da627Seric 	as->as.dns.class = q.q_class;
86b44da627Seric 	as->as.dns.dname = strdup(q.q_dname);
87b44da627Seric 	if (as->as.dns.dname == NULL)
88b44da627Seric 		goto err; /* errno set */
89b44da627Seric 
90253ef892Sderaadt 	_asr_ctx_unref(ac);
91b44da627Seric 	return (as);
92b44da627Seric     err:
93b44da627Seric 	if (as)
94253ef892Sderaadt 		_asr_async_free(as);
95253ef892Sderaadt 	_asr_ctx_unref(ac);
96b44da627Seric 	return (NULL);
97b44da627Seric }
985826fd8cSguenther DEF_WEAK(res_send_async);
99b44da627Seric 
100b44da627Seric /*
101b44da627Seric  * Unlike res_query(), this version will actually return the packet
102b44da627Seric  * if it has received a valid one (errno == 0) even if h_errno is
1035be03f8fSeric  * not NETDB_SUCCESS. So the packet *must* be freed if necessary.
104b44da627Seric  */
1055be03f8fSeric struct asr_query *
res_query_async(const char * name,int class,int type,void * asr)1065be03f8fSeric res_query_async(const char *name, int class, int type, void *asr)
107b44da627Seric {
108b44da627Seric 	struct asr_ctx	 *ac;
1095be03f8fSeric 	struct asr_query *as;
11046ab4803Seric 
11146ab4803Seric 	DPRINT("asr: res_query_async(\"%s\", %i, %i)\n", name, class, type);
11246ab4803Seric 
113253ef892Sderaadt 	ac = _asr_use_resolver(asr);
114253ef892Sderaadt 	as = _res_query_async_ctx(name, class, type, ac);
115253ef892Sderaadt 	_asr_ctx_unref(ac);
116b44da627Seric 
117b44da627Seric 	return (as);
118b44da627Seric }
1195826fd8cSguenther DEF_WEAK(res_query_async);
120b44da627Seric 
1215be03f8fSeric struct asr_query *
_res_query_async_ctx(const char * name,int class,int type,struct asr_ctx * a_ctx)122253ef892Sderaadt _res_query_async_ctx(const char *name, int class, int type, struct asr_ctx *a_ctx)
123b44da627Seric {
1245be03f8fSeric 	struct asr_query	*as;
125b44da627Seric 
12646ab4803Seric 	DPRINT("asr: res_query_async_ctx(\"%s\", %i, %i)\n", name, class, type);
12746ab4803Seric 
128253ef892Sderaadt 	if ((as = _asr_async_new(a_ctx, ASR_SEND)) == NULL)
129b44da627Seric 		return (NULL); /* errno set */
130b44da627Seric 	as->as_run = res_send_async_run;
131b44da627Seric 
132*a34e6b00Sjca 	/* This adds a "." to name if it doesn't already have one.
133*a34e6b00Sjca 	 * That's how res_query() behaves (through res_mkquery).
134b44da627Seric 	 */
135b44da627Seric 	if (setup_query(as, name, NULL, class, type) == -1)
136b44da627Seric 		goto err; /* errno set */
137b44da627Seric 
138b44da627Seric 	return (as);
139b44da627Seric 
140b44da627Seric     err:
141b44da627Seric 	if (as)
142253ef892Sderaadt 		_asr_async_free(as);
143b44da627Seric 
144b44da627Seric 	return (NULL);
145b44da627Seric }
146b44da627Seric 
147b44da627Seric static int
res_send_async_run(struct asr_query * as,struct asr_result * ar)1485be03f8fSeric res_send_async_run(struct asr_query *as, struct asr_result *ar)
149b44da627Seric {
150b44da627Seric     next:
151b44da627Seric 	switch (as->as_state) {
152b44da627Seric 
153b44da627Seric 	case ASR_STATE_INIT:
154b44da627Seric 
155b44da627Seric 		if (as->as_ctx->ac_nscount == 0) {
156b44da627Seric 			ar->ar_errno = ECONNREFUSED;
157b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
158b44da627Seric 			break;
159b44da627Seric 		}
160b44da627Seric 
161b44da627Seric 		async_set_state(as, ASR_STATE_NEXT_NS);
162b44da627Seric 		break;
163b44da627Seric 
164b44da627Seric 	case ASR_STATE_NEXT_NS:
165b44da627Seric 
1665bd9e5c2Seric 		if (iter_ns(as) == -1) {
167b44da627Seric 			ar->ar_errno = ETIMEDOUT;
168b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
169b44da627Seric 			break;
170b44da627Seric 		}
171b44da627Seric 
172b44da627Seric 		if (as->as_ctx->ac_options & RES_USEVC ||
173b44da627Seric 		    as->as.dns.obuflen > PACKETSZ)
174b44da627Seric 			async_set_state(as, ASR_STATE_TCP_WRITE);
175b44da627Seric 		else
176b44da627Seric 			async_set_state(as, ASR_STATE_UDP_SEND);
177b44da627Seric 		break;
178b44da627Seric 
179b44da627Seric 	case ASR_STATE_UDP_SEND:
180b44da627Seric 
181b44da627Seric 		if (udp_send(as) == -1) {
182b44da627Seric 			async_set_state(as, ASR_STATE_NEXT_NS);
183b44da627Seric 			break;
184b44da627Seric 		}
185b44da627Seric 		async_set_state(as, ASR_STATE_UDP_RECV);
1865be03f8fSeric 		ar->ar_cond = ASR_WANT_READ;
187b44da627Seric 		ar->ar_fd = as->as_fd;
188b44da627Seric 		ar->ar_timeout = as->as_timeout;
189b44da627Seric 		return (ASYNC_COND);
190b44da627Seric 		break;
191b44da627Seric 
192b44da627Seric 	case ASR_STATE_UDP_RECV:
193b44da627Seric 
194b44da627Seric 		if (udp_recv(as) == -1) {
195b44da627Seric 			if (errno == ENOMEM) {
196b44da627Seric 				ar->ar_errno = errno;
197b44da627Seric 				async_set_state(as, ASR_STATE_HALT);
198b44da627Seric 				break;
199b44da627Seric 			}
200b44da627Seric 			if (errno != EOVERFLOW) {
201b44da627Seric 				/* Fail or timeout */
202b44da627Seric 				async_set_state(as, ASR_STATE_NEXT_NS);
203b44da627Seric 				break;
204b44da627Seric 			}
205b44da627Seric 			if (as->as_ctx->ac_options & RES_IGNTC)
206b44da627Seric 				async_set_state(as, ASR_STATE_PACKET);
207b44da627Seric 			else
208b44da627Seric 				async_set_state(as, ASR_STATE_TCP_WRITE);
209b44da627Seric 		} else
210b44da627Seric 			async_set_state(as, ASR_STATE_PACKET);
211b44da627Seric 		break;
212b44da627Seric 
213b44da627Seric 	case ASR_STATE_TCP_WRITE:
214b44da627Seric 
215b44da627Seric 		switch (tcp_write(as)) {
216b44da627Seric 		case -1: /* fail or timeout */
217b44da627Seric 			async_set_state(as, ASR_STATE_NEXT_NS);
218b44da627Seric 			break;
219b44da627Seric 		case 0:
220b44da627Seric 			async_set_state(as, ASR_STATE_TCP_READ);
2215be03f8fSeric 			ar->ar_cond = ASR_WANT_READ;
222b44da627Seric 			ar->ar_fd = as->as_fd;
223b44da627Seric 			ar->ar_timeout = as->as_timeout;
224b44da627Seric 			return (ASYNC_COND);
225b44da627Seric 		case 1:
2265be03f8fSeric 			ar->ar_cond = ASR_WANT_WRITE;
227b44da627Seric 			ar->ar_fd = as->as_fd;
228b44da627Seric 			ar->ar_timeout = as->as_timeout;
229b44da627Seric 			return (ASYNC_COND);
230b44da627Seric 		}
231b44da627Seric 		break;
232b44da627Seric 
233b44da627Seric 	case ASR_STATE_TCP_READ:
234b44da627Seric 
235b44da627Seric 		switch (tcp_read(as)) {
236b44da627Seric 		case -1: /* Fail or timeout */
237b44da627Seric 			if (errno == ENOMEM) {
238b44da627Seric 				ar->ar_errno = errno;
239b44da627Seric 				async_set_state(as, ASR_STATE_HALT);
240b44da627Seric 			} else
241b44da627Seric 				async_set_state(as, ASR_STATE_NEXT_NS);
242b44da627Seric 			break;
243b44da627Seric 		case 0:
244b44da627Seric 			async_set_state(as, ASR_STATE_PACKET);
245b44da627Seric 			break;
246b44da627Seric 		case 1:
2475be03f8fSeric 			ar->ar_cond = ASR_WANT_READ;
248b44da627Seric 			ar->ar_fd = as->as_fd;
249b44da627Seric 			ar->ar_timeout = as->as_timeout;
250b44da627Seric 			return (ASYNC_COND);
251b44da627Seric 		}
252b44da627Seric 		break;
253b44da627Seric 
254b44da627Seric 	case ASR_STATE_PACKET:
255b44da627Seric 
2565be03f8fSeric 		memmove(&ar->ar_ns, AS_NS_SA(as), AS_NS_SA(as)->sa_len);
257b44da627Seric 		ar->ar_datalen = as->as.dns.ibuflen;
258b44da627Seric 		ar->ar_data = as->as.dns.ibuf;
259b44da627Seric 		as->as.dns.ibuf = NULL;
260b44da627Seric 		ar->ar_errno = 0;
261b44da627Seric 		ar->ar_rcode = as->as.dns.rcode;
262931108e9Sjca 		if (!(as->as_ctx->ac_options & RES_TRUSTAD))
263931108e9Sjca 			clear_ad(ar);
264b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
265b44da627Seric 		break;
266b44da627Seric 
267b44da627Seric 	case ASR_STATE_HALT:
268b44da627Seric 
269b44da627Seric 		if (ar->ar_errno) {
270b44da627Seric 			ar->ar_h_errno = TRY_AGAIN;
271b44da627Seric 			ar->ar_count = 0;
272b44da627Seric 			ar->ar_datalen = -1;
273b44da627Seric 			ar->ar_data = NULL;
274b44da627Seric 		} else if (as->as.dns.ancount) {
275b44da627Seric 			ar->ar_h_errno = NETDB_SUCCESS;
276b44da627Seric 			ar->ar_count = as->as.dns.ancount;
277b44da627Seric 		} else {
278b44da627Seric 			ar->ar_count = 0;
279b44da627Seric 			switch (as->as.dns.rcode) {
280b44da627Seric 			case NXDOMAIN:
281b44da627Seric 				ar->ar_h_errno = HOST_NOT_FOUND;
282b44da627Seric 				break;
283b44da627Seric 			case SERVFAIL:
284b44da627Seric 				ar->ar_h_errno = TRY_AGAIN;
285b44da627Seric 				break;
286b44da627Seric 			case NOERROR:
287b44da627Seric 				ar->ar_h_errno = NO_DATA;
288b44da627Seric 				break;
289b44da627Seric 			default:
290b44da627Seric 				ar->ar_h_errno = NO_RECOVERY;
291b44da627Seric 			}
292b44da627Seric 		}
293b44da627Seric 		return (ASYNC_DONE);
294b44da627Seric 
295b44da627Seric 	default:
296b44da627Seric 
297b44da627Seric 		ar->ar_errno = EOPNOTSUPP;
298b44da627Seric 		ar->ar_h_errno = NETDB_INTERNAL;
299b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
300b44da627Seric 		break;
301b44da627Seric 	}
302b44da627Seric 	goto next;
303b44da627Seric }
304b44da627Seric 
305b44da627Seric static int
sockaddr_connect(const struct sockaddr * sa,int socktype)306b44da627Seric sockaddr_connect(const struct sockaddr *sa, int socktype)
307b44da627Seric {
308186d27dcSguenther 	int errno_save, sock;
309b44da627Seric 
3109dcd10c8Sderaadt 	if ((sock = socket(sa->sa_family,
3119dcd10c8Sderaadt 	    socktype | SOCK_NONBLOCK | SOCK_DNS, 0)) == -1)
312b44da627Seric 		goto fail;
313b44da627Seric 
3149dcd10c8Sderaadt 	if (connect(sock, sa, sa->sa_len) == -1) {
3154ca3cc6eSeric 		/*
3164ca3cc6eSeric 		 * In the TCP case, the caller will be asked to poll for
3174ca3cc6eSeric 		 * POLLOUT so that we start writing the packet in tcp_write()
3184ca3cc6eSeric 		 * when the connection is established, or fail there on error.
3194ca3cc6eSeric 		 */
320b44da627Seric 		if (errno == EINPROGRESS)
321b44da627Seric 			return (sock);
322b44da627Seric 		goto fail;
323b44da627Seric 	}
324b44da627Seric 
325b44da627Seric 	return (sock);
326b44da627Seric 
327b44da627Seric     fail:
328b44da627Seric 
329b44da627Seric 	if (sock != -1) {
330b44da627Seric 		errno_save = errno;
331b44da627Seric 		close(sock);
332b44da627Seric 		errno = errno_save;
333b44da627Seric 	}
334b44da627Seric 
335b44da627Seric 	return (-1);
336b44da627Seric }
337b44da627Seric 
338b44da627Seric /*
339b44da627Seric  * Prepare the DNS packet for the query type "type", class "class" and domain
340b44da627Seric  * name created by the concatenation on "name" and "dom".
341b44da627Seric  * Return 0 on success, set errno and return -1 on error.
342b44da627Seric  */
343b44da627Seric static int
setup_query(struct asr_query * as,const char * name,const char * dom,int class,int type)3445be03f8fSeric setup_query(struct asr_query *as, const char *name, const char *dom,
345b44da627Seric 	int class, int type)
346b44da627Seric {
347f90bf415Seric 	struct asr_pack		p;
348f90bf415Seric 	struct asr_dns_header	h;
349b44da627Seric 	char			fqdn[MAXDNAME];
350b44da627Seric 	char			dname[MAXDNAME];
351b44da627Seric 
352abe78e02Sjca 	if (as->as_flags & ASYNC_EXTOBUF) {
353b44da627Seric 		errno = EINVAL;
35446ab4803Seric 		DPRINT("attempting to write in user packet");
355b44da627Seric 		return (-1);
356b44da627Seric 	}
357b44da627Seric 
358253ef892Sderaadt 	if (_asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) {
359b44da627Seric 		errno = EINVAL;
36046ab4803Seric 		DPRINT("asr_make_fqdn: name too long\n");
361b44da627Seric 		return (-1);
362b44da627Seric 	}
363b44da627Seric 
364253ef892Sderaadt 	if (_asr_dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) {
365b44da627Seric 		errno = EINVAL;
3665bd9e5c2Seric 		DPRINT("asr_dname_from_fqdn: invalid\n");
367b44da627Seric 		return (-1);
368b44da627Seric 	}
369b44da627Seric 
370b44da627Seric 	if (as->as.dns.obuf == NULL) {
371b44da627Seric 		as->as.dns.obufsize = PACKETSZ;
372b44da627Seric 		as->as.dns.obuf = malloc(as->as.dns.obufsize);
373b44da627Seric 		if (as->as.dns.obuf == NULL)
374b44da627Seric 			return (-1); /* errno set */
375b44da627Seric 	}
376b44da627Seric 	as->as.dns.obuflen = 0;
377b44da627Seric 
378b44da627Seric 	memset(&h, 0, sizeof h);
379b44da627Seric 	h.id = res_randomid();
380b44da627Seric 	if (as->as_ctx->ac_options & RES_RECURSE)
381b44da627Seric 		h.flags |= RD_MASK;
382ca9d64e0Sotto 	if (as->as_ctx->ac_options & RES_USE_CD)
3833d657e16Sotto 		h.flags |= CD_MASK;
384931108e9Sjca 	if (as->as_ctx->ac_options & RES_TRUSTAD)
385931108e9Sjca 		h.flags |= AD_MASK;
386931108e9Sjca 
387b44da627Seric 	h.qdcount = 1;
388d4d39a6fSjca 	if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
3892aa4cd21Sjca 		h.arcount = 1;
390b44da627Seric 
391253ef892Sderaadt 	_asr_pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize);
392253ef892Sderaadt 	_asr_pack_header(&p, &h);
393253ef892Sderaadt 	_asr_pack_query(&p, type, class, dname);
394d4d39a6fSjca 	if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
395d4d39a6fSjca 		_asr_pack_edns0(&p, MAXPACKETSZ,
396d4d39a6fSjca 		    as->as_ctx->ac_options & RES_USE_DNSSEC);
397b44da627Seric 	if (p.err) {
39846ab4803Seric 		DPRINT("error packing query");
399b44da627Seric 		errno = EINVAL;
400b44da627Seric 		return (-1);
401b44da627Seric 	}
402b44da627Seric 
403b44da627Seric 	/* Remember the parameters. */
404b44da627Seric 	as->as.dns.reqid = h.id;
405b44da627Seric 	as->as.dns.type = type;
406b44da627Seric 	as->as.dns.class = class;
407b44da627Seric 	if (as->as.dns.dname)
408b44da627Seric 		free(as->as.dns.dname);
409b44da627Seric 	as->as.dns.dname = strdup(dname);
410b44da627Seric 	if (as->as.dns.dname == NULL) {
41146ab4803Seric 		DPRINT("strdup");
412b44da627Seric 		return (-1); /* errno set */
413b44da627Seric 	}
414b44da627Seric 	as->as.dns.obuflen = p.offset;
415b44da627Seric 
41646ab4803Seric 	DPRINT_PACKET("asr_setup_query", as->as.dns.obuf, as->as.dns.obuflen);
417b44da627Seric 
418b44da627Seric 	return (0);
419b44da627Seric }
420b44da627Seric 
421b44da627Seric /*
422b44da627Seric  * Create a connect UDP socket and send the output packet.
423b44da627Seric  *
424b44da627Seric  * Return 0 on success, or -1 on error (errno set).
425b44da627Seric  */
426b44da627Seric static int
udp_send(struct asr_query * as)4275be03f8fSeric udp_send(struct asr_query *as)
428b44da627Seric {
429b44da627Seric 	ssize_t	n;
430b44da627Seric 	int	save_errno;
431b44da627Seric #ifdef DEBUG
432b44da627Seric 	char		buf[256];
433b44da627Seric #endif
43446ab4803Seric 
43546ab4803Seric 	DPRINT("asr: [%p] connecting to %s UDP\n", as,
436253ef892Sderaadt 	    _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf));
43746ab4803Seric 
438b44da627Seric 	as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM);
439b44da627Seric 	if (as->as_fd == -1)
440b44da627Seric 		return (-1); /* errno set */
441b44da627Seric 
442b44da627Seric 	n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0);
443b44da627Seric 	if (n == -1) {
444b44da627Seric 		save_errno = errno;
445b44da627Seric 		close(as->as_fd);
446b44da627Seric 		errno = save_errno;
447b44da627Seric 		as->as_fd = -1;
448b44da627Seric 		return (-1);
449b44da627Seric 	}
450b44da627Seric 
451b44da627Seric 	return (0);
452b44da627Seric }
453b44da627Seric 
454b44da627Seric /*
455b44da627Seric  * Try to receive a valid packet from the current UDP socket.
456b44da627Seric  *
457b44da627Seric  * Return 0 if a full packet could be read, or -1 on error (errno set).
458b44da627Seric  */
459b44da627Seric static int
udp_recv(struct asr_query * as)4605be03f8fSeric udp_recv(struct asr_query *as)
461b44da627Seric {
462b44da627Seric 	ssize_t		 n;
463b44da627Seric 	int		 save_errno;
464b44da627Seric 
4657c8731c1Skrw 	if (ensure_ibuf(as, MAXPACKETSZ) == -1) {
466b44da627Seric 		save_errno = errno;
467b44da627Seric 		close(as->as_fd);
468b44da627Seric 		errno = save_errno;
469b44da627Seric 		as->as_fd = -1;
470b44da627Seric 		return (-1);
471b44da627Seric 	}
472b44da627Seric 
473b44da627Seric 	n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0);
474b44da627Seric 	save_errno = errno;
475b44da627Seric 	close(as->as_fd);
476b44da627Seric 	errno = save_errno;
477b44da627Seric 	as->as_fd = -1;
478b44da627Seric 	if (n == -1)
479b44da627Seric 		return (-1);
480b44da627Seric 
481b44da627Seric 	as->as.dns.ibuflen = n;
482b44da627Seric 
48346ab4803Seric 	DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen);
484b44da627Seric 
485b44da627Seric 	if (validate_packet(as) == -1)
486b44da627Seric 		return (-1); /* errno set */
487b44da627Seric 
488b44da627Seric 	return (0);
489b44da627Seric }
490b44da627Seric 
491b44da627Seric /*
492b44da627Seric  * Write the output packet to the TCP socket.
493b44da627Seric  *
494b44da627Seric  * Return 0 when all bytes have been sent, 1 there is no buffer space on the
495b44da627Seric  * socket or it is not connected yet, or -1 on error (errno set).
496b44da627Seric  */
497b44da627Seric static int
tcp_write(struct asr_query * as)4985be03f8fSeric tcp_write(struct asr_query *as)
499b44da627Seric {
500162b3743Smatthew 	struct msghdr	msg;
501b44da627Seric 	struct iovec	iov[2];
502b44da627Seric 	uint16_t	len;
503b44da627Seric 	ssize_t		n;
5042f165b8eSeric 	size_t		offset;
5052f165b8eSeric 	int		i;
506b44da627Seric #ifdef DEBUG
507b44da627Seric 	char		buf[256];
508b44da627Seric #endif
509b44da627Seric 
510b44da627Seric 	/* First try to connect if not already */
511b44da627Seric 	if (as->as_fd == -1) {
51246ab4803Seric 		DPRINT("asr: [%p] connecting to %s TCP\n", as,
513253ef892Sderaadt 		    _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf));
514b44da627Seric 		as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM);
515b44da627Seric 		if (as->as_fd == -1)
516b44da627Seric 			return (-1); /* errno set */
5172f165b8eSeric 		as->as.dns.datalen = 0; /* bytes sent */
518b44da627Seric 		return (1);
519b44da627Seric 	}
520b44da627Seric 
521b44da627Seric 	i = 0;
522b44da627Seric 
5232f165b8eSeric 	/* Prepend de packet length if not sent already. */
5242f165b8eSeric 	if (as->as.dns.datalen < sizeof(len)) {
5252f165b8eSeric 		offset = 0;
526b44da627Seric 		len = htons(as->as.dns.obuflen);
5272f165b8eSeric 		iov[i].iov_base = (char *)(&len) + as->as.dns.datalen;
5282f165b8eSeric 		iov[i].iov_len = sizeof(len) - as->as.dns.datalen;
529b44da627Seric 		i++;
5302f165b8eSeric 	} else
5312f165b8eSeric 		offset = as->as.dns.datalen - sizeof(len);
532b44da627Seric 
5332f165b8eSeric 	iov[i].iov_base = as->as.dns.obuf + offset;
5342f165b8eSeric 	iov[i].iov_len = as->as.dns.obuflen - offset;
535b44da627Seric 	i++;
536b44da627Seric 
537162b3743Smatthew 	memset(&msg, 0, sizeof msg);
538162b3743Smatthew 	msg.msg_iov = iov;
539162b3743Smatthew 	msg.msg_iovlen = i;
540162b3743Smatthew 
541eac0da8dSeric     send_again:
542162b3743Smatthew 	n = sendmsg(as->as_fd, &msg, MSG_NOSIGNAL);
543eac0da8dSeric 	if (n == -1) {
544eac0da8dSeric 		if (errno == EINTR)
545eac0da8dSeric 			goto send_again;
546b44da627Seric 		goto close; /* errno set */
547eac0da8dSeric 	}
548b44da627Seric 
5492f165b8eSeric 	as->as.dns.datalen += n;
550b44da627Seric 
5512f165b8eSeric 	if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) {
552b44da627Seric 		/* All sent. Prepare for TCP read */
553b44da627Seric 		as->as.dns.datalen = 0;
554b44da627Seric 		return (0);
555b44da627Seric 	}
556b44da627Seric 
557b44da627Seric 	/* More data to write */
558b44da627Seric 	return (1);
559b44da627Seric 
560b44da627Seric close:
561b44da627Seric 	close(as->as_fd);
562b44da627Seric 	as->as_fd = -1;
563b44da627Seric 	return (-1);
564b44da627Seric }
565b44da627Seric 
566b44da627Seric /*
567b44da627Seric  * Try to read a valid packet from the current TCP socket.
568b44da627Seric  *
569b44da627Seric  * Return 0 if a full packet could be read, 1 if more data is needed and the
570b44da627Seric  * socket must be read again, or -1 on error (errno set).
571b44da627Seric  */
572b44da627Seric static int
tcp_read(struct asr_query * as)5735be03f8fSeric tcp_read(struct asr_query *as)
574b44da627Seric {
575b44da627Seric 	ssize_t		 n;
5767856d263Seric 	size_t		 offset, len;
5777856d263Seric 	char		*pos;
5787856d263Seric 	int		 save_errno, nfds;
5797856d263Seric 	struct pollfd	 pfd;
580b44da627Seric 
581b44da627Seric 	/* We must read the packet len first */
5827856d263Seric 	if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) {
5837856d263Seric 
5847856d263Seric 		pos = (char *)(&as->as.dns.pktlen) + as->as.dns.datalen;
5857856d263Seric 		len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen;
5867856d263Seric 
587449cbdb1Seric     read_again0:
5887856d263Seric 		n = read(as->as_fd, pos, len);
589449cbdb1Seric 		if (n == -1) {
590449cbdb1Seric 			if (errno == EINTR)
591449cbdb1Seric 				goto read_again0;
592b44da627Seric 			goto close; /* errno set */
593449cbdb1Seric 		}
594449cbdb1Seric 		if (n == 0) {
595449cbdb1Seric 			errno = ECONNRESET;
596449cbdb1Seric 			goto close;
597449cbdb1Seric 		}
5987856d263Seric 		as->as.dns.datalen += n;
5997856d263Seric 		if (as->as.dns.datalen < sizeof(as->as.dns.pktlen))
6007856d263Seric 			return (1); /* need more data */
601b44da627Seric 
6027856d263Seric 		as->as.dns.ibuflen = ntohs(as->as.dns.pktlen);
6037856d263Seric 		if (ensure_ibuf(as, as->as.dns.ibuflen) == -1)
604b44da627Seric 			goto close; /* errno set */
6057856d263Seric 
6067856d263Seric 		pfd.fd = as->as_fd;
6077856d263Seric 		pfd.events = POLLIN;
608eac0da8dSeric 	    poll_again:
6097856d263Seric 		nfds = poll(&pfd, 1, 0);
610eac0da8dSeric 		if (nfds == -1) {
611eac0da8dSeric 			if (errno == EINTR)
612eac0da8dSeric 				goto poll_again;
6137856d263Seric 			goto close; /* errno set */
614eac0da8dSeric 		}
6157856d263Seric 		if (nfds == 0)
6167856d263Seric 			return (1); /* no more data available */
617b44da627Seric 	}
618b44da627Seric 
6197856d263Seric 	offset = as->as.dns.datalen - sizeof(as->as.dns.pktlen);
6207856d263Seric 	pos = as->as.dns.ibuf + offset;
6217856d263Seric 	len =  as->as.dns.ibuflen - offset;
6227856d263Seric 
623eac0da8dSeric     read_again:
6247856d263Seric 	n = read(as->as_fd, pos, len);
625eac0da8dSeric 	if (n == -1) {
626eac0da8dSeric 		if (errno == EINTR)
627eac0da8dSeric 			goto read_again;
628b44da627Seric 		goto close; /* errno set */
629eac0da8dSeric 	}
630b44da627Seric 	if (n == 0) {
631b44da627Seric 		errno = ECONNRESET;
632b44da627Seric 		goto close;
633b44da627Seric 	}
6347856d263Seric 	as->as.dns.datalen += n;
635b44da627Seric 
636b44da627Seric 	/* See if we got all the advertised bytes. */
6377856d263Seric 	if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen))
638b44da627Seric 		return (1);
639b44da627Seric 
64046ab4803Seric 	DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen);
64146ab4803Seric 
642b44da627Seric 	if (validate_packet(as) == -1)
643b44da627Seric 		goto close; /* errno set */
644b44da627Seric 
645b44da627Seric 	errno = 0;
646b44da627Seric close:
647b44da627Seric 	save_errno = errno;
648b44da627Seric 	close(as->as_fd);
649b44da627Seric 	errno = save_errno;
650b44da627Seric 	as->as_fd = -1;
651b44da627Seric 	return (errno ? -1 : 0);
652b44da627Seric }
653b44da627Seric 
654b44da627Seric /*
655c5221d45Seric  * Make sure the input buffer is at least "n" bytes long, and allocate or
656c5221d45Seric  * extend it if necessary. Return 0 on success, or set errno and return -1.
657b44da627Seric  */
658b44da627Seric static int
ensure_ibuf(struct asr_query * as,size_t n)6595be03f8fSeric ensure_ibuf(struct asr_query *as, size_t n)
660b44da627Seric {
661b44da627Seric 	char	*t;
662b44da627Seric 
663b44da627Seric 	if (as->as.dns.ibufsize >= n)
664b44da627Seric 		return (0);
665b44da627Seric 
666cff9705aSderaadt 	t = recallocarray(as->as.dns.ibuf, as->as.dns.ibufsize, n, 1);
667b44da627Seric 	if (t == NULL)
668b44da627Seric 		return (-1); /* errno set */
669b44da627Seric 	as->as.dns.ibuf = t;
670b44da627Seric 	as->as.dns.ibufsize = n;
671b44da627Seric 
672b44da627Seric 	return (0);
673b44da627Seric }
674b44da627Seric 
675b44da627Seric /*
676b44da627Seric  * Check if the received packet is valid.
677b44da627Seric  * Return 0 on success, or set errno and return -1.
678b44da627Seric  */
679b44da627Seric static int
validate_packet(struct asr_query * as)6805be03f8fSeric validate_packet(struct asr_query *as)
681b44da627Seric {
682f90bf415Seric 	struct asr_unpack	 p;
683f90bf415Seric 	struct asr_dns_header	 h;
684f90bf415Seric 	struct asr_dns_query	 q;
685f90bf415Seric 	struct asr_dns_rr	 rr;
686b44da627Seric 	int			 r;
687b44da627Seric 
688253ef892Sderaadt 	_asr_unpack_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen);
689b44da627Seric 
690253ef892Sderaadt 	_asr_unpack_header(&p, &h);
691b44da627Seric 	if (p.err)
692b44da627Seric 		goto inval;
693b44da627Seric 
694b44da627Seric 	if (h.id != as->as.dns.reqid) {
69546ab4803Seric 		DPRINT("incorrect reqid\n");
696b44da627Seric 		goto inval;
697b44da627Seric 	}
698b44da627Seric 	if (h.qdcount != 1)
699b44da627Seric 		goto inval;
700b44da627Seric 	/* Should be zero, we could allow this */
701b44da627Seric 	if ((h.flags & Z_MASK) != 0)
702b44da627Seric 		goto inval;
703b44da627Seric 	/* Actually, it depends on the request but we only use OP_QUERY */
704b44da627Seric 	if (OPCODE(h.flags) != OP_QUERY)
705b44da627Seric 		goto inval;
706b44da627Seric 	/* Must be a response */
707b44da627Seric 	if ((h.flags & QR_MASK) == 0)
708b44da627Seric 		goto inval;
709b44da627Seric 
710b44da627Seric 	as->as.dns.rcode = RCODE(h.flags);
711b44da627Seric 	as->as.dns.ancount = h.ancount;
712b44da627Seric 
713253ef892Sderaadt 	_asr_unpack_query(&p, &q);
714b44da627Seric 	if (p.err)
715b44da627Seric 		goto inval;
716b44da627Seric 
717b44da627Seric 	if (q.q_type != as->as.dns.type ||
718b44da627Seric 	    q.q_class != as->as.dns.class ||
719b44da627Seric 	    strcasecmp(q.q_dname, as->as.dns.dname)) {
72046ab4803Seric 		DPRINT("incorrect type/class/dname '%s' != '%s'\n",
721b44da627Seric 		    q.q_dname, as->as.dns.dname);
722b44da627Seric 		goto inval;
723b44da627Seric 	}
724b44da627Seric 
725b44da627Seric 	/* Check for truncation */
72619313708Seric 	if (h.flags & TC_MASK && !(as->as_ctx->ac_options & RES_IGNTC)) {
72719313708Seric 		DPRINT("truncated\n");
728b44da627Seric 		errno = EOVERFLOW;
729b44da627Seric 		return (-1);
730b44da627Seric 	}
731b44da627Seric 
732b44da627Seric 	/* Validate the rest of the packet */
733b44da627Seric 	for (r = h.ancount + h.nscount + h.arcount; r; r--)
734253ef892Sderaadt 		_asr_unpack_rr(&p, &rr);
735b44da627Seric 
73619313708Seric 	/* Report any error found when unpacking the RRs. */
73719313708Seric 	if (p.err) {
73819313708Seric 		DPRINT("unpack: %s\n", strerror(p.err));
73919313708Seric 		errno = p.err;
74019313708Seric 		return (-1);
74119313708Seric 	}
74219313708Seric 
74319313708Seric 	if (p.offset != as->as.dns.ibuflen) {
74419313708Seric 		DPRINT("trailing garbage\n");
74519313708Seric 		errno = EMSGSIZE;
74619313708Seric 		return (-1);
74719313708Seric 	}
748b44da627Seric 
749b44da627Seric 	return (0);
750b44da627Seric 
751b44da627Seric     inval:
752b44da627Seric 	errno = EINVAL;
753b44da627Seric 	return (-1);
754b44da627Seric }
755d3064b1fSeric 
756d3064b1fSeric /*
757931108e9Sjca  * Clear AD flag in the answer.
758931108e9Sjca  */
759931108e9Sjca static void
clear_ad(struct asr_result * ar)760931108e9Sjca clear_ad(struct asr_result *ar)
761931108e9Sjca {
762931108e9Sjca 	struct asr_dns_header	*h;
763931108e9Sjca 	uint16_t		 flags;
764931108e9Sjca 
765931108e9Sjca 	h = (struct asr_dns_header *)ar->ar_data;
766931108e9Sjca 	flags = ntohs(h->flags);
767931108e9Sjca 	flags &= ~(AD_MASK);
768931108e9Sjca 	h->flags = htons(flags);
769931108e9Sjca }
770931108e9Sjca 
771931108e9Sjca /*
772d3064b1fSeric  * Set the async context nameserver index to the next nameserver, cycling
773d3064b1fSeric  * over the list until the maximum retry counter is reached.  Return 0 on
774d3064b1fSeric  * success, or -1 if all nameservers were used.
775d3064b1fSeric  */
7765bd9e5c2Seric static int
iter_ns(struct asr_query * as)7775be03f8fSeric iter_ns(struct asr_query *as)
778d3064b1fSeric {
779d3064b1fSeric 	for (;;) {
780d3064b1fSeric 		if (as->as.dns.nsloop >= as->as_ctx->ac_nsretries)
781d3064b1fSeric 			return (-1);
782d3064b1fSeric 
783d3064b1fSeric 		as->as.dns.nsidx += 1;
784d3064b1fSeric 		if (as->as.dns.nsidx <= as->as_ctx->ac_nscount)
785d3064b1fSeric 			break;
786d3064b1fSeric 		as->as.dns.nsidx = 0;
787d3064b1fSeric 		as->as.dns.nsloop++;
7885bd9e5c2Seric 		DPRINT("asr: iter_ns(): cycle %i\n", as->as.dns.nsloop);
789d3064b1fSeric 	}
790d3064b1fSeric 
791d3064b1fSeric 	as->as_timeout = 1000 * (as->as_ctx->ac_nstimeout << as->as.dns.nsloop);
792d3064b1fSeric 	if (as->as.dns.nsloop > 0)
793d3064b1fSeric 		as->as_timeout /= as->as_ctx->ac_nscount;
794d3064b1fSeric 	if (as->as_timeout < 1000)
795d3064b1fSeric 		as->as_timeout = 1000;
796d3064b1fSeric 
797d3064b1fSeric 	return (0);
798d3064b1fSeric }
799