GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/asr/getaddrinfo_async.c Lines: 111 362 30.7 %
Date: 2017-11-07 Branches: 71 295 24.1 %

Line Branch Exec Source
1
/*	$OpenBSD: getaddrinfo_async.c,v 1.54 2017/02/27 10:44:46 jca 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 <net/if.h>
24
#include <netdb.h>
25
26
#include <asr.h>
27
#include <errno.h>
28
#include <ifaddrs.h>
29
#include <resolv.h>
30
#include <stdlib.h>
31
#include <string.h>
32
#include <unistd.h>
33
#include <limits.h>
34
35
#include "asr_private.h"
36
37
struct match {
38
	int family;
39
	int socktype;
40
	int protocol;
41
};
42
43
static int getaddrinfo_async_run(struct asr_query *, struct asr_result *);
44
static int get_port(const char *, const char *, int);
45
static int iter_family(struct asr_query *, int);
46
static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *);
47
static int addrinfo_from_file(struct asr_query *, int,  FILE *);
48
static int addrinfo_from_pkt(struct asr_query *, char *, size_t);
49
static int addrconfig_setup(struct asr_query *);
50
51
static const struct match matches[] = {
52
	{ PF_INET,	SOCK_DGRAM,	IPPROTO_UDP	},
53
	{ PF_INET,	SOCK_STREAM,	IPPROTO_TCP	},
54
	{ PF_INET,	SOCK_RAW,	0		},
55
	{ PF_INET6,	SOCK_DGRAM,	IPPROTO_UDP	},
56
	{ PF_INET6,	SOCK_STREAM,	IPPROTO_TCP	},
57
	{ PF_INET6,	SOCK_RAW,	0		},
58
	{ -1,		0,		0,		},
59
};
60
61
#define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC)
62
#define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0)
63
/* Do not match SOCK_RAW unless explicitely specified */
64
#define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \
65
				matches[(b)].socktype != SOCK_RAW))
66
67
enum {
68
	DOM_INIT,
69
	DOM_DOMAIN,
70
	DOM_DONE
71
};
72
73
struct asr_query *
74
getaddrinfo_async(const char *hostname, const char *servname,
75
	const struct addrinfo *hints, void *asr)
76
{
77
	struct asr_ctx		*ac;
78
	struct asr_query	*as;
79
80

102
	if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0)
81
12
		ac = _asr_use_resolver(asr);
82
	else
83
22
		ac = _asr_no_resolver();
84
34
	if ((as = _asr_async_new(ac, ASR_GETADDRINFO)) == NULL)
85
		goto abort; /* errno set */
86
34
	as->as_run = getaddrinfo_async_run;
87
88
34
	if (hostname) {
89
34
		if ((as->as.ai.hostname = strdup(hostname)) == NULL)
90
			goto abort; /* errno set */
91
	}
92

68
	if (servname && (as->as.ai.servname = strdup(servname)) == NULL)
93
		goto abort; /* errno set */
94
34
	if (hints)
95
34
		memmove(&as->as.ai.hints, hints, sizeof *hints);
96
	else {
97
		memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints);
98
		as->as.ai.hints.ai_family = PF_UNSPEC;
99
		as->as.ai.hints.ai_flags = AI_ADDRCONFIG;
100
	}
101
102
34
	_asr_ctx_unref(ac);
103
34
	return (as);
104
    abort:
105
	if (as)
106
		_asr_async_free(as);
107
	_asr_ctx_unref(ac);
108
	return (NULL);
109
34
}
110
DEF_WEAK(getaddrinfo_async);
111
112
static int
113
getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar)
114
{
115
68
	char		 fqdn[MAXDNAME];
116
	const char	*str;
117
	struct addrinfo	*ai;
118
	int		 i, family, r;
119
	FILE		*f;
120
34
	union {
121
		struct sockaddr		sa;
122
		struct sockaddr_in	sain;
123
		struct sockaddr_in6	sain6;
124
	} sa;
125
126
    next:
127


68
	switch (as->as_state) {
128
129
	case ASR_STATE_INIT:
130
131
		/*
132
		 * First, make sure the parameters are valid.
133
		 */
134
135
34
		as->as_count = 0;
136
137

34
		if (as->as.ai.hostname == NULL &&
138
		    as->as.ai.servname == NULL) {
139
			ar->ar_gai_errno = EAI_NONAME;
140
			async_set_state(as, ASR_STATE_HALT);
141
			break;
142
		}
143
144

68
		if (as->as.ai.hostname && as->as.ai.hostname[0] == '\0') {
145
			ar->ar_gai_errno = EAI_NODATA;
146
			async_set_state(as, ASR_STATE_HALT);
147
			break;
148
		}
149
150
34
		ai = &as->as.ai.hints;
151
152

68
		if (ai->ai_addrlen ||
153
34
		    ai->ai_canonname ||
154
34
		    ai->ai_addr ||
155
34
		    ai->ai_next) {
156
			ar->ar_gai_errno = EAI_BADHINTS;
157
			async_set_state(as, ASR_STATE_HALT);
158
			break;
159
		}
160
161

34
		if (ai->ai_flags & ~AI_MASK ||
162
34
		    (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) {
163
			ar->ar_gai_errno = EAI_BADFLAGS;
164
			async_set_state(as, ASR_STATE_HALT);
165
			break;
166
		}
167
168

68
		if (ai->ai_family != PF_UNSPEC &&
169
34
		    ai->ai_family != PF_INET &&
170
34
		    ai->ai_family != PF_INET6) {
171
			ar->ar_gai_errno = EAI_FAMILY;
172
			async_set_state(as, ASR_STATE_HALT);
173
			break;
174
		}
175
176

34
		if (ai->ai_socktype &&
177
34
		    ai->ai_socktype != SOCK_DGRAM  &&
178
		    ai->ai_socktype != SOCK_STREAM &&
179
		    ai->ai_socktype != SOCK_RAW) {
180
			ar->ar_gai_errno = EAI_SOCKTYPE;
181
			async_set_state(as, ASR_STATE_HALT);
182
			break;
183
		}
184
185

34
		if (ai->ai_socktype == SOCK_RAW &&
186
		    get_port(as->as.ai.servname, NULL, 1) != 0) {
187
			ar->ar_gai_errno = EAI_SERVICE;
188
			async_set_state(as, ASR_STATE_HALT);
189
			break;
190
		}
191
192
		/* Restrict result set to configured address families */
193
34
		if (ai->ai_flags & AI_ADDRCONFIG) {
194
			if (addrconfig_setup(as) == -1) {
195
				ar->ar_errno = errno;
196
				ar->ar_gai_errno = EAI_SYSTEM;
197
				async_set_state(as, ASR_STATE_HALT);
198
				break;
199
			}
200
		}
201
202
		/* Make sure there is at least a valid combination */
203
272
		for (i = 0; matches[i].family != -1; i++)
204

238
			if (MATCH_FAMILY(ai->ai_family, i) &&
205

34
			    MATCH_SOCKTYPE(ai->ai_socktype, i) &&
206

68
			    MATCH_PROTO(ai->ai_protocol, i))
207
				break;
208
34
		if (matches[i].family == -1) {
209
			ar->ar_gai_errno = EAI_BADHINTS;
210
			async_set_state(as, ASR_STATE_HALT);
211
			break;
212
		}
213
214

34
		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP)
215
68
			as->as.ai.port_udp = get_port(as->as.ai.servname, "udp",
216
34
			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
217

34
		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP)
218
68
			as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp",
219
34
			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
220

68
		if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 ||
221

34
		    (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) ||
222

34
		    (ai->ai_protocol && (as->as.ai.port_udp == -1 ||
223
					 as->as.ai.port_tcp == -1))) {
224
			ar->ar_gai_errno = EAI_SERVICE;
225
			async_set_state(as, ASR_STATE_HALT);
226
			break;
227
		}
228
229
34
		ar->ar_gai_errno = 0;
230
231
		/* If hostname is NULL, use local address */
232
68
		if (as->as.ai.hostname == NULL) {
233
34
			for (family = iter_family(as, 1);
234
			    family != -1;
235
			    family = iter_family(as, 0)) {
236
				/*
237
				 * We could use statically built sockaddrs for
238
				 * those, rather than parsing over and over.
239
				 */
240
				if (family == PF_INET)
241
					str = (ai->ai_flags & AI_PASSIVE) ? \
242
						"0.0.0.0" : "127.0.0.1";
243
				else /* PF_INET6 */
244
					str = (ai->ai_flags & AI_PASSIVE) ? \
245
						"::" : "::1";
246
				 /* This can't fail */
247
				_asr_sockaddr_from_str(&sa.sa, family, str);
248
				if ((r = addrinfo_add(as, &sa.sa, NULL))) {
249
					ar->ar_gai_errno = r;
250
					break;
251
				}
252
			}
253
			if (ar->ar_gai_errno == 0 && as->as_count == 0) {
254
				ar->ar_gai_errno = EAI_NODATA;
255
			}
256
			async_set_state(as, ASR_STATE_HALT);
257
			break;
258
		}
259
260
		/* Try numeric addresses first */
261
34
		for (family = iter_family(as, 1);
262
34
		    family != -1;
263
		    family = iter_family(as, 0)) {
264
265
102
			if (_asr_sockaddr_from_str(&sa.sa, family,
266
68
			    as->as.ai.hostname) == -1)
267
				continue;
268
269
34
			if ((r = addrinfo_add(as, &sa.sa, NULL)))
270
				ar->ar_gai_errno = r;
271
			break;
272
		}
273

68
		if (ar->ar_gai_errno || as->as_count) {
274
34
			async_set_state(as, ASR_STATE_HALT);
275
34
			break;
276
		}
277
278
		if (ai->ai_flags & AI_NUMERICHOST) {
279
			ar->ar_gai_errno = EAI_NONAME;
280
			async_set_state(as, ASR_STATE_HALT);
281
			break;
282
		}
283
284
		async_set_state(as, ASR_STATE_NEXT_DB);
285
		break;
286
287
	case ASR_STATE_NEXT_DB:
288
		if (_asr_iter_db(as) == -1) {
289
			async_set_state(as, ASR_STATE_NOT_FOUND);
290
			break;
291
		}
292
		as->as_family_idx = 0;
293
		async_set_state(as, ASR_STATE_SAME_DB);
294
		break;
295
296
	case ASR_STATE_NEXT_FAMILY:
297
		as->as_family_idx += 1;
298
		if (as->as.ai.hints.ai_family != AF_UNSPEC ||
299
		    AS_FAMILY(as) == -1) {
300
			/* The family was specified, or we have tried all
301
			 * families with this DB.
302
			 */
303
			if (as->as_count) {
304
				ar->ar_gai_errno = 0;
305
				async_set_state(as, ASR_STATE_HALT);
306
			} else
307
				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
308
			break;
309
		}
310
		async_set_state(as, ASR_STATE_SAME_DB);
311
		break;
312
313
	case ASR_STATE_NEXT_DOMAIN:
314
		/* domain search is only for dns */
315
		if (AS_DB(as) != ASR_DB_DNS) {
316
			async_set_state(as, ASR_STATE_NEXT_DB);
317
			break;
318
		}
319
		as->as_family_idx = 0;
320
321
		free(as->as.ai.fqdn);
322
		as->as.ai.fqdn = NULL;
323
		r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn));
324
		if (r == -1) {
325
			async_set_state(as, ASR_STATE_NEXT_DB);
326
			break;
327
		}
328
		if (r == 0) {
329
			ar->ar_gai_errno = EAI_FAIL;
330
			async_set_state(as, ASR_STATE_HALT);
331
			break;
332
		}
333
		as->as.ai.fqdn = strdup(fqdn);
334
		if (as->as.ai.fqdn == NULL) {
335
			ar->ar_gai_errno = EAI_MEMORY;
336
			async_set_state(as, ASR_STATE_HALT);
337
			break;
338
		}
339
340
		async_set_state(as, ASR_STATE_SAME_DB);
341
		break;
342
343
	case ASR_STATE_SAME_DB:
344
		/* query the current DB again */
345
		switch (AS_DB(as)) {
346
		case ASR_DB_DNS:
347
			if (as->as.ai.fqdn == NULL) {
348
				/* First try, initialize domain iteration */
349
				as->as_dom_flags = 0;
350
				as->as_dom_step = DOM_INIT;
351
				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
352
				break;
353
			}
354
355
			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
356
			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
357
358
			if (family == AF_INET &&
359
			    as->as_flags & ASYNC_NO_INET) {
360
				async_set_state(as, ASR_STATE_NEXT_FAMILY);
361
				break;
362
			} else if (family == AF_INET6 &&
363
			    as->as_flags & ASYNC_NO_INET6) {
364
				async_set_state(as, ASR_STATE_NEXT_FAMILY);
365
				break;
366
			}
367
368
			as->as_subq = _res_query_async_ctx(as->as.ai.fqdn,
369
			    C_IN, (family == AF_INET6) ? T_AAAA : T_A,
370
			    as->as_ctx);
371
372
			if (as->as_subq == NULL) {
373
				if (errno == ENOMEM)
374
					ar->ar_gai_errno = EAI_MEMORY;
375
				else
376
					ar->ar_gai_errno = EAI_FAIL;
377
				async_set_state(as, ASR_STATE_HALT);
378
				break;
379
			}
380
			async_set_state(as, ASR_STATE_SUBQUERY);
381
			break;
382
383
		case ASR_DB_FILE:
384
			f = fopen(_PATH_HOSTS, "re");
385
			if (f == NULL) {
386
				async_set_state(as, ASR_STATE_NEXT_DB);
387
				break;
388
			}
389
			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
390
			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
391
392
			r = addrinfo_from_file(as, family, f);
393
			if (r == -1) {
394
				if (errno == ENOMEM)
395
					ar->ar_gai_errno = EAI_MEMORY;
396
				else
397
					ar->ar_gai_errno = EAI_FAIL;
398
				async_set_state(as, ASR_STATE_HALT);
399
			} else
400
				async_set_state(as, ASR_STATE_NEXT_FAMILY);
401
			fclose(f);
402
			break;
403
404
		default:
405
			async_set_state(as, ASR_STATE_NEXT_DB);
406
		}
407
		break;
408
409
	case ASR_STATE_SUBQUERY:
410
		if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
411
			return (ASYNC_COND);
412
413
		as->as_subq = NULL;
414
415
		if (ar->ar_datalen == -1) {
416
			async_set_state(as, ASR_STATE_NEXT_FAMILY);
417
			break;
418
		}
419
420
		r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen);
421
		if (r == -1) {
422
			if (errno == ENOMEM)
423
				ar->ar_gai_errno = EAI_MEMORY;
424
			else
425
				ar->ar_gai_errno = EAI_FAIL;
426
			async_set_state(as, ASR_STATE_HALT);
427
		} else
428
			async_set_state(as, ASR_STATE_NEXT_FAMILY);
429
		free(ar->ar_data);
430
		break;
431
432
	case ASR_STATE_NOT_FOUND:
433
		/* No result found. Maybe we can try again. */
434
		if (as->as_flags & ASYNC_AGAIN)
435
			ar->ar_gai_errno = EAI_AGAIN;
436
		else
437
			ar->ar_gai_errno = EAI_NODATA;
438
		async_set_state(as, ASR_STATE_HALT);
439
		break;
440
441
	case ASR_STATE_HALT:
442
34
		if (ar->ar_gai_errno == 0) {
443
34
			ar->ar_count = as->as_count;
444
34
			ar->ar_addrinfo = as->as.ai.aifirst;
445
			as->as.ai.aifirst = NULL;
446
34
		} else {
447
			ar->ar_count = 0;
448
			ar->ar_addrinfo = NULL;
449
		}
450
34
		return (ASYNC_DONE);
451
452
	default:
453
		ar->ar_errno = EOPNOTSUPP;
454
		ar->ar_gai_errno = EAI_SYSTEM;
455
		async_set_state(as, ASR_STATE_HALT);
456
		break;
457
	}
458
	goto next;
459
34
}
460
461
/*
462
 * Retreive the port number for the service name "servname" and
463
 * the protocol "proto".
464
 */
465
static int
466
get_port(const char *servname, const char *proto, int numonly)
467
{
468
136
	struct servent		se;
469
68
	struct servent_data	sed;
470
	int			port, r;
471
68
	const char		*e;
472
473
68
	if (servname == NULL)
474
		return (0);
475
476
68
	e = NULL;
477
68
	port = strtonum(servname, 0, USHRT_MAX, &e);
478
68
	if (e == NULL)
479
68
		return (port);
480
	if (errno == ERANGE)
481
		return (-2); /* invalid */
482
	if (numonly)
483
		return (-2);
484
485
	memset(&sed, 0, sizeof(sed));
486
	r = getservbyname_r(servname, proto, &se, &sed);
487
	port = ntohs(se.s_port);
488
	endservent_r(&sed);
489
490
	if (r == -1)
491
		return (-1); /* not found */
492
493
	return (port);
494
68
}
495
496
/*
497
 * Iterate over the address families that are to be queried. Use the
498
 * list on the async context, unless a specific family was given in hints.
499
 */
500
static int
501
iter_family(struct asr_query *as, int first)
502
{
503
68
	if (first) {
504
34
		as->as_family_idx = 0;
505
34
		if (as->as.ai.hints.ai_family != PF_UNSPEC)
506
34
			return as->as.ai.hints.ai_family;
507
		return AS_FAMILY(as);
508
	}
509
510
	if (as->as.ai.hints.ai_family != PF_UNSPEC)
511
		return (-1);
512
513
	as->as_family_idx++;
514
515
	return AS_FAMILY(as);
516
34
}
517
518
/*
519
 * Use the sockaddr at "sa" to extend the result list on the "as" context,
520
 * with the specified canonical name "cname". This function adds one
521
 * entry per protocol/socktype match.
522
 */
523
static int
524
addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname)
525
{
526
	struct addrinfo		*ai;
527
	int			 i, port, proto;
528
529
510
	for (i = 0; matches[i].family != -1; i++) {
530

204
		if (matches[i].family != sa->sa_family ||
531

170
		    !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) ||
532

68
		    !MATCH_PROTO(as->as.ai.hints.ai_protocol, i))
533
			continue;
534
535
34
		proto = as->as.ai.hints.ai_protocol;
536
34
		if (!proto)
537
34
			proto = matches[i].protocol;
538
539
34
		if (proto == IPPROTO_TCP)
540
			port = as->as.ai.port_tcp;
541
34
		else if (proto == IPPROTO_UDP)
542
34
			port = as->as.ai.port_udp;
543
		else
544
			port = 0;
545
546
		/* servname specified, but not defined for this protocol */
547
34
		if (port == -1)
548
			continue;
549
550
34
		ai = calloc(1, sizeof(*ai) + sa->sa_len);
551
34
		if (ai == NULL)
552
			return (EAI_MEMORY);
553
34
		ai->ai_family = sa->sa_family;
554
34
		ai->ai_socktype = matches[i].socktype;
555
34
		ai->ai_protocol = proto;
556
34
		ai->ai_flags = as->as.ai.hints.ai_flags;
557
34
		ai->ai_addrlen = sa->sa_len;
558
34
		ai->ai_addr = (void *)(ai + 1);
559

34
		if (cname &&
560
		    as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) {
561
			if ((ai->ai_canonname = strdup(cname)) == NULL) {
562
				free(ai);
563
				return (EAI_MEMORY);
564
			}
565
		}
566
34
		memmove(ai->ai_addr, sa, sa->sa_len);
567
34
		if (sa->sa_family == PF_INET)
568
			((struct sockaddr_in *)ai->ai_addr)->sin_port =
569
			    htons(port);
570
34
		else if (sa->sa_family == PF_INET6)
571
34
			((struct sockaddr_in6 *)ai->ai_addr)->sin6_port =
572
34
			    htons(port);
573
574
34
		if (as->as.ai.aifirst == NULL)
575
34
			as->as.ai.aifirst = ai;
576
34
		if (as->as.ai.ailast)
577
			as->as.ai.ailast->ai_next = ai;
578
34
		as->as.ai.ailast = ai;
579
34
		as->as_count += 1;
580
34
	}
581
582
34
	return (0);
583
34
}
584
585
static int
586
addrinfo_from_file(struct asr_query *as, int family, FILE *f)
587
{
588
	char		*tokens[MAXTOKEN], *c, buf[BUFSIZ + 1];
589
	int		 n, i;
590
	union {
591
		struct sockaddr		sa;
592
		struct sockaddr_in	sain;
593
		struct sockaddr_in6	sain6;
594
	} u;
595
596
	for (;;) {
597
		n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf));
598
		if (n == -1)
599
			break; /* ignore errors reading the file */
600
601
		for (i = 1; i < n; i++) {
602
			if (strcasecmp(as->as.ai.hostname, tokens[i]))
603
				continue;
604
			if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
605
				continue;
606
			break;
607
		}
608
		if (i == n)
609
			continue;
610
611
		if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
612
			c = tokens[1];
613
		else
614
			c = NULL;
615
616
		if (addrinfo_add(as, &u.sa, c))
617
			return (-1); /* errno set */
618
	}
619
	return (0);
620
}
621
622
static int
623
addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen)
624
{
625
	struct asr_unpack	 p;
626
	struct asr_dns_header	 h;
627
	struct asr_dns_query	 q;
628
	struct asr_dns_rr	 rr;
629
	int			 i;
630
	union {
631
		struct sockaddr		sa;
632
		struct sockaddr_in	sain;
633
		struct sockaddr_in6	sain6;
634
	} u;
635
	char		 buf[MAXDNAME], *c;
636
637
	_asr_unpack_init(&p, pkt, pktlen);
638
	_asr_unpack_header(&p, &h);
639
	for (; h.qdcount; h.qdcount--)
640
		_asr_unpack_query(&p, &q);
641
642
	for (i = 0; i < h.ancount; i++) {
643
		_asr_unpack_rr(&p, &rr);
644
		if (rr.rr_type != q.q_type ||
645
		    rr.rr_class != q.q_class)
646
			continue;
647
648
		memset(&u, 0, sizeof u);
649
		if (rr.rr_type == T_A) {
650
			u.sain.sin_len = sizeof u.sain;
651
			u.sain.sin_family = AF_INET;
652
			u.sain.sin_addr = rr.rr.in_a.addr;
653
			u.sain.sin_port = 0;
654
		} else if (rr.rr_type == T_AAAA) {
655
			u.sain6.sin6_len = sizeof u.sain6;
656
			u.sain6.sin6_family = AF_INET6;
657
			u.sain6.sin6_addr = rr.rr.in_aaaa.addr6;
658
			u.sain6.sin6_port = 0;
659
		} else
660
			continue;
661
662
		if (as->as.ai.hints.ai_flags & AI_CANONNAME) {
663
			_asr_strdname(rr.rr_dname, buf, sizeof buf);
664
			buf[strlen(buf) - 1] = '\0';
665
			c = res_hnok(buf) ? buf : NULL;
666
		} else if (as->as.ai.hints.ai_flags & AI_FQDN)
667
			c = as->as.ai.fqdn;
668
		else
669
			c = NULL;
670
671
		if (addrinfo_add(as, &u.sa, c))
672
			return (-1); /* errno set */
673
	}
674
	return (0);
675
}
676
677
static int
678
addrconfig_setup(struct asr_query *as)
679
{
680
	struct ifaddrs		*ifa, *ifa0;
681
	struct sockaddr_in	*sinp;
682
	struct sockaddr_in6	*sin6p;
683
684
	if (getifaddrs(&ifa0) == -1)
685
		return (-1);
686
687
	as->as_flags |= ASYNC_NO_INET | ASYNC_NO_INET6;
688
689
	for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
690
		if (ifa->ifa_addr == NULL)
691
			continue;
692
693
		switch (ifa->ifa_addr->sa_family) {
694
		case PF_INET:
695
			sinp = (struct sockaddr_in *)ifa->ifa_addr;
696
697
			if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
698
				continue;
699
700
			as->as_flags &= ~ASYNC_NO_INET;
701
			break;
702
		case PF_INET6:
703
			sin6p = (struct sockaddr_in6 *)ifa->ifa_addr;
704
705
			if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr))
706
				continue;
707
708
			if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr))
709
				continue;
710
711
			as->as_flags &= ~ASYNC_NO_INET6;
712
			break;
713
		}
714
	}
715
716
	freeifaddrs(ifa0);
717
718
	return (0);
719
}