1 /* $OpenBSD: getaddrinfo_async.c,v 1.63 2024/08/21 05:53:10 florian 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 explicitly 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 *
getaddrinfo_async(const char * hostname,const char * servname,const struct addrinfo * hints,void * asr)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 if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0)
81 ac = _asr_use_resolver(asr);
82 else
83 ac = _asr_no_resolver();
84 if ((as = _asr_async_new(ac, ASR_GETADDRINFO)) == NULL)
85 goto abort; /* errno set */
86 as->as_run = getaddrinfo_async_run;
87
88 if (hostname) {
89 if ((as->as.ai.hostname = strdup(hostname)) == NULL)
90 goto abort; /* errno set */
91 }
92 if (servname && (as->as.ai.servname = strdup(servname)) == NULL)
93 goto abort; /* errno set */
94 if (hints)
95 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 _asr_ctx_unref(ac);
103 return (as);
104 abort:
105 if (as)
106 _asr_async_free(as);
107 _asr_ctx_unref(ac);
108 return (NULL);
109 }
110 DEF_WEAK(getaddrinfo_async);
111
112 static int
getaddrinfo_async_run(struct asr_query * as,struct asr_result * ar)113 getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar)
114 {
115 char fqdn[MAXDNAME];
116 const char *str;
117 struct addrinfo *ai;
118 int i, family, r, is_localhost = 0;
119 FILE *f;
120 union {
121 struct sockaddr sa;
122 struct sockaddr_in sain;
123 struct sockaddr_in6 sain6;
124 } sa;
125
126 next:
127 switch (as->as_state) {
128
129 case ASR_STATE_INIT:
130
131 /*
132 * First, make sure the parameters are valid.
133 */
134
135 as->as_count = 0;
136
137 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 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 ai = &as->as.ai.hints;
151
152 if (ai->ai_addrlen ||
153 ai->ai_canonname ||
154 ai->ai_addr ||
155 ai->ai_next) {
156 ar->ar_gai_errno = EAI_BADHINTS;
157 async_set_state(as, ASR_STATE_HALT);
158 break;
159 }
160
161 if (ai->ai_flags & ~AI_MASK ||
162 (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 if (ai->ai_family != PF_UNSPEC &&
169 ai->ai_family != PF_INET &&
170 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 if (ai->ai_socktype &&
177 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 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 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 for (i = 0; matches[i].family != -1; i++)
204 if (MATCH_FAMILY(ai->ai_family, i) &&
205 MATCH_SOCKTYPE(ai->ai_socktype, i) &&
206 MATCH_PROTO(ai->ai_protocol, i))
207 break;
208 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 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP)
215 as->as.ai.port_udp = get_port(as->as.ai.servname, "udp",
216 as->as.ai.hints.ai_flags & AI_NUMERICSERV);
217 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP)
218 as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp",
219 as->as.ai.hints.ai_flags & AI_NUMERICSERV);
220 if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 ||
221 (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) ||
222 (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 ar->ar_gai_errno = 0;
230
231 if (!(ai->ai_flags & AI_NUMERICHOST))
232 is_localhost = _asr_is_localhost(as->as.ai.hostname);
233 /*
234 * If hostname is NULL, "localhost" or falls within the
235 * ".localhost." domain, use local address.
236 * RFC 6761, 6.3:
237 * 3. Name resolution APIs and libraries SHOULD recognize
238 * localhost names as special and SHOULD always return the IP
239 * loopback address for address queries and negative responses
240 * for all other query types. Name resolution APIs SHOULD NOT
241 * send queries for localhost names to their configured caching
242 * DNS server(s).
243 */
244 if (as->as.ai.hostname == NULL || is_localhost) {
245 for (family = iter_family(as, 1);
246 family != -1;
247 family = iter_family(as, 0)) {
248 /*
249 * We could use statically built sockaddrs for
250 * those, rather than parsing over and over.
251 */
252 if (family == PF_INET)
253 str = (ai->ai_flags & AI_PASSIVE &&
254 !is_localhost) ? "0.0.0.0" :
255 "127.0.0.1";
256 else /* PF_INET6 */
257 str = (ai->ai_flags & AI_PASSIVE &&
258 !is_localhost) ? "::" : "::1";
259 /* This can't fail */
260 _asr_sockaddr_from_str(&sa.sa, family, str);
261 if ((r = addrinfo_add(as, &sa.sa,
262 "localhost."))) {
263 ar->ar_gai_errno = r;
264 break;
265 }
266 }
267 if (ar->ar_gai_errno == 0 && as->as_count == 0) {
268 ar->ar_gai_errno = EAI_NODATA;
269 }
270 async_set_state(as, ASR_STATE_HALT);
271 break;
272 }
273
274 /* Try numeric addresses first */
275 for (family = iter_family(as, 1);
276 family != -1;
277 family = iter_family(as, 0)) {
278
279 if (_asr_sockaddr_from_str(&sa.sa, family,
280 as->as.ai.hostname) == -1)
281 continue;
282
283 if ((r = addrinfo_add(as, &sa.sa, as->as.ai.hostname)))
284 ar->ar_gai_errno = r;
285 break;
286 }
287 if (ar->ar_gai_errno || as->as_count) {
288 async_set_state(as, ASR_STATE_HALT);
289 break;
290 }
291
292 if (ai->ai_flags & AI_NUMERICHOST) {
293 ar->ar_gai_errno = EAI_NONAME;
294 async_set_state(as, ASR_STATE_HALT);
295 break;
296 }
297
298 /* make sure there are no funny characters in hostname */
299 if (!hnok_lenient(as->as.ai.hostname)) {
300 ar->ar_gai_errno = EAI_FAIL;
301 async_set_state(as, ASR_STATE_HALT);
302 break;
303 }
304
305 async_set_state(as, ASR_STATE_NEXT_DB);
306 break;
307
308 case ASR_STATE_NEXT_DB:
309 if (_asr_iter_db(as) == -1) {
310 async_set_state(as, ASR_STATE_NOT_FOUND);
311 break;
312 }
313 as->as_family_idx = 0;
314 async_set_state(as, ASR_STATE_SAME_DB);
315 break;
316
317 case ASR_STATE_NEXT_FAMILY:
318 as->as_family_idx += 1;
319 if (as->as.ai.hints.ai_family != AF_UNSPEC ||
320 AS_FAMILY(as) == -1) {
321 /* The family was specified, or we have tried all
322 * families with this DB.
323 */
324 if (as->as_count) {
325 ar->ar_gai_errno = 0;
326 async_set_state(as, ASR_STATE_HALT);
327 } else
328 async_set_state(as, ASR_STATE_NEXT_DOMAIN);
329 break;
330 }
331 async_set_state(as, ASR_STATE_SAME_DB);
332 break;
333
334 case ASR_STATE_NEXT_DOMAIN:
335 /* domain search is only for dns */
336 if (AS_DB(as) != ASR_DB_DNS) {
337 async_set_state(as, ASR_STATE_NEXT_DB);
338 break;
339 }
340 as->as_family_idx = 0;
341
342 free(as->as.ai.fqdn);
343 as->as.ai.fqdn = NULL;
344 r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn));
345 if (r == -1) {
346 async_set_state(as, ASR_STATE_NEXT_DB);
347 break;
348 }
349 if (r == 0) {
350 ar->ar_gai_errno = EAI_FAIL;
351 async_set_state(as, ASR_STATE_HALT);
352 break;
353 }
354 as->as.ai.fqdn = strdup(fqdn);
355 if (as->as.ai.fqdn == NULL) {
356 ar->ar_gai_errno = EAI_MEMORY;
357 async_set_state(as, ASR_STATE_HALT);
358 break;
359 }
360
361 async_set_state(as, ASR_STATE_SAME_DB);
362 break;
363
364 case ASR_STATE_SAME_DB:
365 /* query the current DB again */
366 switch (AS_DB(as)) {
367 case ASR_DB_DNS:
368 if (as->as.ai.fqdn == NULL) {
369 /* First try, initialize domain iteration */
370 as->as_dom_flags = 0;
371 as->as_dom_step = DOM_INIT;
372 async_set_state(as, ASR_STATE_NEXT_DOMAIN);
373 break;
374 }
375
376 family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
377 AS_FAMILY(as) : as->as.ai.hints.ai_family;
378
379 if (family == AF_INET &&
380 as->as_flags & ASYNC_NO_INET) {
381 async_set_state(as, ASR_STATE_NEXT_FAMILY);
382 break;
383 } else if (family == AF_INET6 &&
384 as->as_flags & ASYNC_NO_INET6) {
385 async_set_state(as, ASR_STATE_NEXT_FAMILY);
386 break;
387 }
388
389 as->as_subq = _res_query_async_ctx(as->as.ai.fqdn,
390 C_IN, (family == AF_INET6) ? T_AAAA : T_A,
391 as->as_ctx);
392
393 if (as->as_subq == NULL) {
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 break;
400 }
401 async_set_state(as, ASR_STATE_SUBQUERY);
402 break;
403
404 case ASR_DB_FILE:
405 f = fopen(_PATH_HOSTS, "re");
406 if (f == NULL) {
407 async_set_state(as, ASR_STATE_NEXT_DB);
408 break;
409 }
410 family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
411 AS_FAMILY(as) : as->as.ai.hints.ai_family;
412
413 r = addrinfo_from_file(as, family, f);
414 if (r == -1) {
415 if (errno == ENOMEM)
416 ar->ar_gai_errno = EAI_MEMORY;
417 else
418 ar->ar_gai_errno = EAI_FAIL;
419 async_set_state(as, ASR_STATE_HALT);
420 } else
421 async_set_state(as, ASR_STATE_NEXT_FAMILY);
422 fclose(f);
423 break;
424
425 default:
426 async_set_state(as, ASR_STATE_NEXT_DB);
427 }
428 break;
429
430 case ASR_STATE_SUBQUERY:
431 if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
432 return (ASYNC_COND);
433
434 as->as_subq = NULL;
435
436 if (ar->ar_datalen == -1) {
437 async_set_state(as, ASR_STATE_NEXT_FAMILY);
438 break;
439 }
440
441 r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen);
442 if (r == -1) {
443 if (errno == ENOMEM)
444 ar->ar_gai_errno = EAI_MEMORY;
445 else
446 ar->ar_gai_errno = EAI_FAIL;
447 async_set_state(as, ASR_STATE_HALT);
448 } else
449 async_set_state(as, ASR_STATE_NEXT_FAMILY);
450 free(ar->ar_data);
451 break;
452
453 case ASR_STATE_NOT_FOUND:
454 /* No result found. Maybe we can try again. */
455 if (as->as_flags & ASYNC_AGAIN)
456 ar->ar_gai_errno = EAI_AGAIN;
457 else
458 ar->ar_gai_errno = EAI_NODATA;
459 async_set_state(as, ASR_STATE_HALT);
460 break;
461
462 case ASR_STATE_HALT:
463 if (ar->ar_gai_errno == 0) {
464 ar->ar_count = as->as_count;
465 ar->ar_addrinfo = as->as.ai.aifirst;
466 as->as.ai.aifirst = NULL;
467 } else {
468 ar->ar_count = 0;
469 ar->ar_addrinfo = NULL;
470 }
471 return (ASYNC_DONE);
472
473 default:
474 ar->ar_errno = EOPNOTSUPP;
475 ar->ar_gai_errno = EAI_SYSTEM;
476 async_set_state(as, ASR_STATE_HALT);
477 break;
478 }
479 goto next;
480 }
481
482 /*
483 * Retrieve the port number for the service name "servname" and
484 * the protocol "proto".
485 */
486 static int
get_port(const char * servname,const char * proto,int numonly)487 get_port(const char *servname, const char *proto, int numonly)
488 {
489 struct servent se;
490 struct servent_data sed;
491 int port;
492 const char *e;
493
494 if (servname == NULL)
495 return (0);
496
497 e = NULL;
498 port = strtonum(servname, 0, USHRT_MAX, &e);
499 if (e == NULL)
500 return (port);
501 if (errno == ERANGE)
502 return (-2); /* invalid */
503 if (numonly)
504 return (-2);
505
506 port = -1;
507 memset(&sed, 0, sizeof(sed));
508 if (getservbyname_r(servname, proto, &se, &sed) != -1)
509 port = ntohs(se.s_port);
510 endservent_r(&sed);
511
512 return (port);
513 }
514
515 /*
516 * Iterate over the address families that are to be queried. Use the
517 * list on the async context, unless a specific family was given in hints.
518 */
519 static int
iter_family(struct asr_query * as,int first)520 iter_family(struct asr_query *as, int first)
521 {
522 if (first) {
523 as->as_family_idx = 0;
524 if (as->as.ai.hints.ai_family != PF_UNSPEC)
525 return as->as.ai.hints.ai_family;
526 return AS_FAMILY(as);
527 }
528
529 if (as->as.ai.hints.ai_family != PF_UNSPEC)
530 return (-1);
531
532 as->as_family_idx++;
533
534 return AS_FAMILY(as);
535 }
536
537 /*
538 * Use the sockaddr at "sa" to extend the result list on the "as" context,
539 * with the specified canonical name "cname". This function adds one
540 * entry per protocol/socktype match.
541 */
542 static int
addrinfo_add(struct asr_query * as,const struct sockaddr * sa,const char * cname)543 addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname)
544 {
545 struct addrinfo *ai;
546 int i, port, proto;
547
548 for (i = 0; matches[i].family != -1; i++) {
549 if (matches[i].family != sa->sa_family ||
550 !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) ||
551 !MATCH_PROTO(as->as.ai.hints.ai_protocol, i))
552 continue;
553
554 proto = as->as.ai.hints.ai_protocol;
555 if (!proto)
556 proto = matches[i].protocol;
557
558 if (proto == IPPROTO_TCP)
559 port = as->as.ai.port_tcp;
560 else if (proto == IPPROTO_UDP)
561 port = as->as.ai.port_udp;
562 else
563 port = 0;
564
565 /* servname specified, but not defined for this protocol */
566 if (port == -1)
567 continue;
568
569 ai = calloc(1, sizeof(*ai) + sa->sa_len);
570 if (ai == NULL)
571 return (EAI_MEMORY);
572 ai->ai_family = sa->sa_family;
573 ai->ai_socktype = matches[i].socktype;
574 ai->ai_protocol = proto;
575 ai->ai_flags = as->as.ai.hints.ai_flags;
576 ai->ai_addrlen = sa->sa_len;
577 ai->ai_addr = (void *)(ai + 1);
578 if (cname &&
579 as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) {
580 if ((ai->ai_canonname = strdup(cname)) == NULL) {
581 free(ai);
582 return (EAI_MEMORY);
583 }
584 }
585 memmove(ai->ai_addr, sa, sa->sa_len);
586 if (sa->sa_family == PF_INET)
587 ((struct sockaddr_in *)ai->ai_addr)->sin_port =
588 htons(port);
589 else if (sa->sa_family == PF_INET6)
590 ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port =
591 htons(port);
592
593 if (as->as.ai.aifirst == NULL)
594 as->as.ai.aifirst = ai;
595 if (as->as.ai.ailast)
596 as->as.ai.ailast->ai_next = ai;
597 as->as.ai.ailast = ai;
598 as->as_count += 1;
599 }
600
601 return (0);
602 }
603
604 static int
addrinfo_from_file(struct asr_query * as,int family,FILE * f)605 addrinfo_from_file(struct asr_query *as, int family, FILE *f)
606 {
607 char *tokens[MAXTOKEN], *c, buf[BUFSIZ + 1];
608 int n, i;
609 union {
610 struct sockaddr sa;
611 struct sockaddr_in sain;
612 struct sockaddr_in6 sain6;
613 } u;
614
615 for (;;) {
616 n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf));
617 if (n == -1)
618 break; /* ignore errors reading the file */
619
620 for (i = 1; i < n; i++) {
621 if (strcasecmp(as->as.ai.hostname, tokens[i]))
622 continue;
623 if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
624 continue;
625 break;
626 }
627 if (i == n)
628 continue;
629
630 if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
631 c = tokens[1];
632 else
633 c = NULL;
634
635 if (addrinfo_add(as, &u.sa, c))
636 return (-1); /* errno set */
637 }
638 return (0);
639 }
640
641 static int
addrinfo_from_pkt(struct asr_query * as,char * pkt,size_t pktlen)642 addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen)
643 {
644 struct asr_unpack p;
645 struct asr_dns_header h;
646 struct asr_dns_query q;
647 struct asr_dns_rr rr;
648 int i;
649 union {
650 struct sockaddr sa;
651 struct sockaddr_in sain;
652 struct sockaddr_in6 sain6;
653 } u;
654 char buf[MAXDNAME], *c;
655
656 _asr_unpack_init(&p, pkt, pktlen);
657 _asr_unpack_header(&p, &h);
658 for (; h.qdcount; h.qdcount--)
659 _asr_unpack_query(&p, &q);
660
661 for (i = 0; i < h.ancount; i++) {
662 _asr_unpack_rr(&p, &rr);
663 if (rr.rr_type != q.q_type ||
664 rr.rr_class != q.q_class)
665 continue;
666
667 memset(&u, 0, sizeof u);
668 if (rr.rr_type == T_A) {
669 u.sain.sin_len = sizeof u.sain;
670 u.sain.sin_family = AF_INET;
671 u.sain.sin_addr = rr.rr.in_a.addr;
672 u.sain.sin_port = 0;
673 } else if (rr.rr_type == T_AAAA) {
674 u.sain6.sin6_len = sizeof u.sain6;
675 u.sain6.sin6_family = AF_INET6;
676 u.sain6.sin6_addr = rr.rr.in_aaaa.addr6;
677 u.sain6.sin6_port = 0;
678 } else
679 continue;
680
681 if (as->as.ai.hints.ai_flags & AI_CANONNAME) {
682 _asr_strdname(rr.rr_dname, buf, sizeof buf);
683 buf[strlen(buf) - 1] = '\0';
684 c = res_hnok(buf) ? buf : as->as.ai.hostname;
685 } else if (as->as.ai.hints.ai_flags & AI_FQDN)
686 c = as->as.ai.fqdn;
687 else
688 c = NULL;
689
690 if (addrinfo_add(as, &u.sa, c))
691 return (-1); /* errno set */
692 }
693 return (0);
694 }
695
696 static int
addrconfig_setup(struct asr_query * as)697 addrconfig_setup(struct asr_query *as)
698 {
699 struct ifaddrs *ifa, *ifa0;
700 struct if_data *ifa_data;
701 struct sockaddr_in *sinp;
702 struct sockaddr_in6 *sin6p;
703 int rtable, ifa_rtable = -1;
704
705 if (getifaddrs(&ifa0) == -1)
706 return (-1);
707
708 rtable = getrtable();
709
710 as->as_flags |= ASYNC_NO_INET | ASYNC_NO_INET6;
711
712 for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
713 if (ifa->ifa_addr == NULL)
714 continue;
715
716 switch (ifa->ifa_addr->sa_family) {
717 case PF_LINK:
718 /* AF_LINK comes before inet / inet6 on an interface */
719 ifa_data = (struct if_data *)ifa->ifa_data;
720 ifa_rtable = ifa_data->ifi_rdomain;
721 break;
722 case PF_INET:
723 if (ifa_rtable != rtable)
724 continue;
725
726 sinp = (struct sockaddr_in *)ifa->ifa_addr;
727
728 if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
729 continue;
730
731 as->as_flags &= ~ASYNC_NO_INET;
732 break;
733 case PF_INET6:
734 if (ifa_rtable != rtable)
735 continue;
736
737 sin6p = (struct sockaddr_in6 *)ifa->ifa_addr;
738
739 if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr))
740 continue;
741
742 if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr))
743 continue;
744
745 as->as_flags &= ~ASYNC_NO_INET6;
746 break;
747 }
748 }
749
750 freeifaddrs(ifa0);
751
752 return (0);
753 }
754