GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/asr/res_send_async.c Lines: 0 334 0.0 %
Date: 2017-11-07 Branches: 0 177 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: res_send_async.c,v 1.36 2017/03/15 15:54:41 deraadt 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/socket.h>
20
#include <sys/uio.h>
21
#include <netinet/in.h>
22
#include <arpa/nameser.h>
23
#include <netdb.h>
24
25
#include <asr.h>
26
#include <errno.h>
27
#include <fcntl.h>
28
#include <poll.h>
29
#include <resolv.h> /* for res_random */
30
#include <stdlib.h>
31
#include <string.h>
32
#include <unistd.h>
33
34
#include "asr_private.h"
35
36
#define OP_QUERY    (0)
37
38
static int res_send_async_run(struct asr_query *, struct asr_result *);
39
static int sockaddr_connect(const struct sockaddr *, int);
40
static int udp_send(struct asr_query *);
41
static int udp_recv(struct asr_query *);
42
static int tcp_write(struct asr_query *);
43
static int tcp_read(struct asr_query *);
44
static int validate_packet(struct asr_query *);
45
static int setup_query(struct asr_query *, const char *, const char *, int, int);
46
static int ensure_ibuf(struct asr_query *, size_t);
47
static int iter_ns(struct asr_query *);
48
49
#define AS_NS_SA(p) ((p)->as_ctx->ac_ns[(p)->as.dns.nsidx - 1])
50
51
52
struct asr_query *
53
res_send_async(const unsigned char *buf, int buflen, void *asr)
54
{
55
	struct asr_ctx		*ac;
56
	struct asr_query	*as;
57
	struct asr_unpack	 p;
58
	struct asr_dns_header	 h;
59
	struct asr_dns_query	 q;
60
61
	DPRINT_PACKET("asr: res_send_async()", buf, buflen);
62
63
	ac = _asr_use_resolver(asr);
64
	if ((as = _asr_async_new(ac, ASR_SEND)) == NULL) {
65
		_asr_ctx_unref(ac);
66
		return (NULL); /* errno set */
67
	}
68
	as->as_run = res_send_async_run;
69
70
	as->as_flags |= ASYNC_EXTOBUF;
71
	as->as.dns.obuf = (unsigned char *)buf;
72
	as->as.dns.obuflen = buflen;
73
	as->as.dns.obufsize = buflen;
74
75
	_asr_unpack_init(&p, buf, buflen);
76
	_asr_unpack_header(&p, &h);
77
	_asr_unpack_query(&p, &q);
78
	if (p.err) {
79
		errno = EINVAL;
80
		goto err;
81
	}
82
	as->as.dns.reqid = h.id;
83
	as->as.dns.type = q.q_type;
84
	as->as.dns.class = q.q_class;
85
	as->as.dns.dname = strdup(q.q_dname);
86
	if (as->as.dns.dname == NULL)
87
		goto err; /* errno set */
88
89
	_asr_ctx_unref(ac);
90
	return (as);
91
    err:
92
	if (as)
93
		_asr_async_free(as);
94
	_asr_ctx_unref(ac);
95
	return (NULL);
96
}
97
DEF_WEAK(res_send_async);
98
99
/*
100
 * Unlike res_query(), this version will actually return the packet
101
 * if it has received a valid one (errno == 0) even if h_errno is
102
 * not NETDB_SUCCESS. So the packet *must* be freed if necessary.
103
 */
104
struct asr_query *
105
res_query_async(const char *name, int class, int type, void *asr)
106
{
107
	struct asr_ctx	 *ac;
108
	struct asr_query *as;
109
110
	DPRINT("asr: res_query_async(\"%s\", %i, %i)\n", name, class, type);
111
112
	ac = _asr_use_resolver(asr);
113
	as = _res_query_async_ctx(name, class, type, ac);
114
	_asr_ctx_unref(ac);
115
116
	return (as);
117
}
118
DEF_WEAK(res_query_async);
119
120
struct asr_query *
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
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), AS_NS_SA(as)->sa_len);
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
303
sockaddr_connect(const struct sockaddr *sa, int socktype)
304
{
305
	int errno_save, sock;
306
307
	if ((sock = socket(sa->sa_family,
308
	    socktype | SOCK_NONBLOCK | SOCK_DNS, 0)) == -1)
309
		goto fail;
310
311
	if (connect(sock, sa, sa->sa_len) == -1) {
312
		/*
313
		 * In the TCP case, the caller will be asked to poll for
314
		 * POLLOUT so that we start writing the packet in tcp_write()
315
		 * when the connection is established, or fail there on error.
316
		 */
317
		if (errno == EINPROGRESS)
318
			return (sock);
319
		goto fail;
320
	}
321
322
	return (sock);
323
324
    fail:
325
326
	if (sock != -1) {
327
		errno_save = errno;
328
		close(sock);
329
		errno = errno_save;
330
	}
331
332
	return (-1);
333
}
334
335
/*
336
 * Prepare the DNS packet for the query type "type", class "class" and domain
337
 * name created by the concatenation on "name" and "dom".
338
 * Return 0 on success, set errno and return -1 on error.
339
 */
340
static int
341
setup_query(struct asr_query *as, const char *name, const char *dom,
342
	int class, int type)
343
{
344
	struct asr_pack		p;
345
	struct asr_dns_header	h;
346
	char			fqdn[MAXDNAME];
347
	char			dname[MAXDNAME];
348
349
	if (as->as_flags & ASYNC_EXTOBUF) {
350
		errno = EINVAL;
351
		DPRINT("attempting to write in user packet");
352
		return (-1);
353
	}
354
355
	if (_asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) {
356
		errno = EINVAL;
357
		DPRINT("asr_make_fqdn: name too long\n");
358
		return (-1);
359
	}
360
361
	if (_asr_dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) {
362
		errno = EINVAL;
363
		DPRINT("asr_dname_from_fqdn: invalid\n");
364
		return (-1);
365
	}
366
367
	if (as->as.dns.obuf == NULL) {
368
		as->as.dns.obufsize = PACKETSZ;
369
		as->as.dns.obuf = malloc(as->as.dns.obufsize);
370
		if (as->as.dns.obuf == NULL)
371
			return (-1); /* errno set */
372
	}
373
	as->as.dns.obuflen = 0;
374
375
	memset(&h, 0, sizeof h);
376
	h.id = res_randomid();
377
	if (as->as_ctx->ac_options & RES_RECURSE)
378
		h.flags |= RD_MASK;
379
	h.qdcount = 1;
380
	if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
381
		h.arcount = 1;
382
383
	_asr_pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize);
384
	_asr_pack_header(&p, &h);
385
	_asr_pack_query(&p, type, class, dname);
386
	if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
387
		_asr_pack_edns0(&p, MAXPACKETSZ,
388
		    as->as_ctx->ac_options & RES_USE_DNSSEC);
389
	if (p.err) {
390
		DPRINT("error packing query");
391
		errno = EINVAL;
392
		return (-1);
393
	}
394
395
	/* Remember the parameters. */
396
	as->as.dns.reqid = h.id;
397
	as->as.dns.type = type;
398
	as->as.dns.class = class;
399
	if (as->as.dns.dname)
400
		free(as->as.dns.dname);
401
	as->as.dns.dname = strdup(dname);
402
	if (as->as.dns.dname == NULL) {
403
		DPRINT("strdup");
404
		return (-1); /* errno set */
405
	}
406
	as->as.dns.obuflen = p.offset;
407
408
	DPRINT_PACKET("asr_setup_query", as->as.dns.obuf, as->as.dns.obuflen);
409
410
	return (0);
411
}
412
413
/*
414
 * Create a connect UDP socket and send the output packet.
415
 *
416
 * Return 0 on success, or -1 on error (errno set).
417
 */
418
static int
419
udp_send(struct asr_query *as)
420
{
421
	ssize_t	n;
422
	int	save_errno;
423
#ifdef DEBUG
424
	char		buf[256];
425
#endif
426
427
	DPRINT("asr: [%p] connecting to %s UDP\n", as,
428
	    _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf));
429
430
	as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM);
431
	if (as->as_fd == -1)
432
		return (-1); /* errno set */
433
434
	n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0);
435
	if (n == -1) {
436
		save_errno = errno;
437
		close(as->as_fd);
438
		errno = save_errno;
439
		as->as_fd = -1;
440
		return (-1);
441
	}
442
443
	return (0);
444
}
445
446
/*
447
 * Try to receive a valid packet from the current UDP socket.
448
 *
449
 * Return 0 if a full packet could be read, or -1 on error (errno set).
450
 */
451
static int
452
udp_recv(struct asr_query *as)
453
{
454
	ssize_t		 n;
455
	int		 save_errno;
456
457
	if (ensure_ibuf(as, MAXPACKETSZ) == -1) {
458
		save_errno = errno;
459
		close(as->as_fd);
460
		errno = save_errno;
461
		as->as_fd = -1;
462
		return (-1);
463
	}
464
465
	n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0);
466
	save_errno = errno;
467
	close(as->as_fd);
468
	errno = save_errno;
469
	as->as_fd = -1;
470
	if (n == -1)
471
		return (-1);
472
473
	as->as.dns.ibuflen = n;
474
475
	DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen);
476
477
	if (validate_packet(as) == -1)
478
		return (-1); /* errno set */
479
480
	return (0);
481
}
482
483
/*
484
 * Write the output packet to the TCP socket.
485
 *
486
 * Return 0 when all bytes have been sent, 1 there is no buffer space on the
487
 * socket or it is not connected yet, or -1 on error (errno set).
488
 */
489
static int
490
tcp_write(struct asr_query *as)
491
{
492
	struct msghdr	msg;
493
	struct iovec	iov[2];
494
	uint16_t	len;
495
	ssize_t		n;
496
	size_t		offset;
497
	int		i;
498
#ifdef DEBUG
499
	char		buf[256];
500
#endif
501
502
	/* First try to connect if not already */
503
	if (as->as_fd == -1) {
504
		DPRINT("asr: [%p] connecting to %s TCP\n", as,
505
		    _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf));
506
		as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM);
507
		if (as->as_fd == -1)
508
			return (-1); /* errno set */
509
		as->as.dns.datalen = 0; /* bytes sent */
510
		return (1);
511
	}
512
513
	i = 0;
514
515
	/* Prepend de packet length if not sent already. */
516
	if (as->as.dns.datalen < sizeof(len)) {
517
		offset = 0;
518
		len = htons(as->as.dns.obuflen);
519
		iov[i].iov_base = (char *)(&len) + as->as.dns.datalen;
520
		iov[i].iov_len = sizeof(len) - as->as.dns.datalen;
521
		i++;
522
	} else
523
		offset = as->as.dns.datalen - sizeof(len);
524
525
	iov[i].iov_base = as->as.dns.obuf + offset;
526
	iov[i].iov_len = as->as.dns.obuflen - offset;
527
	i++;
528
529
	memset(&msg, 0, sizeof msg);
530
	msg.msg_iov = iov;
531
	msg.msg_iovlen = i;
532
533
    send_again:
534
	n = sendmsg(as->as_fd, &msg, MSG_NOSIGNAL);
535
	if (n == -1) {
536
		if (errno == EINTR)
537
			goto send_again;
538
		goto close; /* errno set */
539
	}
540
541
	as->as.dns.datalen += n;
542
543
	if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) {
544
		/* All sent. Prepare for TCP read */
545
		as->as.dns.datalen = 0;
546
		return (0);
547
	}
548
549
	/* More data to write */
550
	return (1);
551
552
close:
553
	close(as->as_fd);
554
	as->as_fd = -1;
555
	return (-1);
556
}
557
558
/*
559
 * Try to read a valid packet from the current TCP socket.
560
 *
561
 * Return 0 if a full packet could be read, 1 if more data is needed and the
562
 * socket must be read again, or -1 on error (errno set).
563
 */
564
static int
565
tcp_read(struct asr_query *as)
566
{
567
	ssize_t		 n;
568
	size_t		 offset, len;
569
	char		*pos;
570
	int		 save_errno, nfds;
571
	struct pollfd	 pfd;
572
573
	/* We must read the packet len first */
574
	if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) {
575
576
		pos = (char *)(&as->as.dns.pktlen) + as->as.dns.datalen;
577
		len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen;
578
579
		n = read(as->as_fd, pos, len);
580
		if (n == -1)
581
			goto close; /* errno set */
582
583
		as->as.dns.datalen += n;
584
		if (as->as.dns.datalen < sizeof(as->as.dns.pktlen))
585
			return (1); /* need more data */
586
587
		as->as.dns.ibuflen = ntohs(as->as.dns.pktlen);
588
		if (ensure_ibuf(as, as->as.dns.ibuflen) == -1)
589
			goto close; /* errno set */
590
591
		pfd.fd = as->as_fd;
592
		pfd.events = POLLIN;
593
	    poll_again:
594
		nfds = poll(&pfd, 1, 0);
595
		if (nfds == -1) {
596
			if (errno == EINTR)
597
				goto poll_again;
598
			goto close; /* errno set */
599
		}
600
		if (nfds == 0)
601
			return (1); /* no more data available */
602
	}
603
604
	offset = as->as.dns.datalen - sizeof(as->as.dns.pktlen);
605
	pos = as->as.dns.ibuf + offset;
606
	len =  as->as.dns.ibuflen - offset;
607
608
    read_again:
609
	n = read(as->as_fd, pos, len);
610
	if (n == -1) {
611
		if (errno == EINTR)
612
			goto read_again;
613
		goto close; /* errno set */
614
	}
615
	if (n == 0) {
616
		errno = ECONNRESET;
617
		goto close;
618
	}
619
	as->as.dns.datalen += n;
620
621
	/* See if we got all the advertised bytes. */
622
	if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen))
623
		return (1);
624
625
	DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen);
626
627
	if (validate_packet(as) == -1)
628
		goto close; /* errno set */
629
630
	errno = 0;
631
close:
632
	save_errno = errno;
633
	close(as->as_fd);
634
	errno = save_errno;
635
	as->as_fd = -1;
636
	return (errno ? -1 : 0);
637
}
638
639
/*
640
 * Make sure the input buffer is at least "n" bytes long, and allocate or
641
 * extend it if necessary. Return 0 on success, or set errno and return -1.
642
 */
643
static int
644
ensure_ibuf(struct asr_query *as, size_t n)
645
{
646
	char	*t;
647
648
	if (as->as.dns.ibufsize >= n)
649
		return (0);
650
651
	t = recallocarray(as->as.dns.ibuf, as->as.dns.ibufsize, n, 1);
652
	if (t == NULL)
653
		return (-1); /* errno set */
654
	as->as.dns.ibuf = t;
655
	as->as.dns.ibufsize = n;
656
657
	return (0);
658
}
659
660
/*
661
 * Check if the received packet is valid.
662
 * Return 0 on success, or set errno and return -1.
663
 */
664
static int
665
validate_packet(struct asr_query *as)
666
{
667
	struct asr_unpack	 p;
668
	struct asr_dns_header	 h;
669
	struct asr_dns_query	 q;
670
	struct asr_dns_rr	 rr;
671
	int			 r;
672
673
	_asr_unpack_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen);
674
675
	_asr_unpack_header(&p, &h);
676
	if (p.err)
677
		goto inval;
678
679
	if (h.id != as->as.dns.reqid) {
680
		DPRINT("incorrect reqid\n");
681
		goto inval;
682
	}
683
	if (h.qdcount != 1)
684
		goto inval;
685
	/* Should be zero, we could allow this */
686
	if ((h.flags & Z_MASK) != 0)
687
		goto inval;
688
	/* Actually, it depends on the request but we only use OP_QUERY */
689
	if (OPCODE(h.flags) != OP_QUERY)
690
		goto inval;
691
	/* Must be a response */
692
	if ((h.flags & QR_MASK) == 0)
693
		goto inval;
694
695
	as->as.dns.rcode = RCODE(h.flags);
696
	as->as.dns.ancount = h.ancount;
697
698
	_asr_unpack_query(&p, &q);
699
	if (p.err)
700
		goto inval;
701
702
	if (q.q_type != as->as.dns.type ||
703
	    q.q_class != as->as.dns.class ||
704
	    strcasecmp(q.q_dname, as->as.dns.dname)) {
705
		DPRINT("incorrect type/class/dname '%s' != '%s'\n",
706
		    q.q_dname, as->as.dns.dname);
707
		goto inval;
708
	}
709
710
	/* Check for truncation */
711
	if (h.flags & TC_MASK && !(as->as_ctx->ac_options & RES_IGNTC)) {
712
		DPRINT("truncated\n");
713
		errno = EOVERFLOW;
714
		return (-1);
715
	}
716
717
	/* Validate the rest of the packet */
718
	for (r = h.ancount + h.nscount + h.arcount; r; r--)
719
		_asr_unpack_rr(&p, &rr);
720
721
	/* Report any error found when unpacking the RRs. */
722
	if (p.err) {
723
		DPRINT("unpack: %s\n", strerror(p.err));
724
		errno = p.err;
725
		return (-1);
726
	}
727
728
	if (p.offset != as->as.dns.ibuflen) {
729
		DPRINT("trailing garbage\n");
730
		errno = EMSGSIZE;
731
		return (-1);
732
	}
733
734
	return (0);
735
736
    inval:
737
	errno = EINVAL;
738
	return (-1);
739
}
740
741
/*
742
 * Set the async context nameserver index to the next nameserver, cycling
743
 * over the list until the maximum retry counter is reached.  Return 0 on
744
 * success, or -1 if all nameservers were used.
745
 */
746
static int
747
iter_ns(struct asr_query *as)
748
{
749
	for (;;) {
750
		if (as->as.dns.nsloop >= as->as_ctx->ac_nsretries)
751
			return (-1);
752
753
		as->as.dns.nsidx += 1;
754
		if (as->as.dns.nsidx <= as->as_ctx->ac_nscount)
755
			break;
756
		as->as.dns.nsidx = 0;
757
		as->as.dns.nsloop++;
758
		DPRINT("asr: iter_ns(): cycle %i\n", as->as.dns.nsloop);
759
	}
760
761
	as->as_timeout = 1000 * (as->as_ctx->ac_nstimeout << as->as.dns.nsloop);
762
	if (as->as.dns.nsloop > 0)
763
		as->as_timeout /= as->as_ctx->ac_nscount;
764
	if (as->as_timeout < 1000)
765
		as->as_timeout = 1000;
766
767
	return (0);
768
}