1 /* $OpenBSD: getaddrinfo_async.c,v 1.62 2024/01/15 18:03:39 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 * 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 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, NULL))) 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 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 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 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 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 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 : NULL; 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 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