1 /* $OpenBSD: getaddrinfo_async.c,v 1.50 2015/12/16 16:32:30 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 <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 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; 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) != 0) { 195 ar->ar_gai_errno = EAI_FAIL; 196 async_set_state(as, ASR_STATE_HALT); 197 break; 198 } 199 } 200 201 /* Make sure there is at least a valid combination */ 202 for (i = 0; matches[i].family != -1; i++) 203 if (MATCH_FAMILY(ai->ai_family, i) && 204 MATCH_SOCKTYPE(ai->ai_socktype, i) && 205 MATCH_PROTO(ai->ai_protocol, i)) 206 break; 207 if (matches[i].family == -1) { 208 ar->ar_gai_errno = EAI_BADHINTS; 209 async_set_state(as, ASR_STATE_HALT); 210 break; 211 } 212 213 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) 214 as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", 215 as->as.ai.hints.ai_flags & AI_NUMERICSERV); 216 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) 217 as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", 218 as->as.ai.hints.ai_flags & AI_NUMERICSERV); 219 if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || 220 (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || 221 (ai->ai_protocol && (as->as.ai.port_udp == -1 || 222 as->as.ai.port_tcp == -1))) { 223 ar->ar_gai_errno = EAI_SERVICE; 224 async_set_state(as, ASR_STATE_HALT); 225 break; 226 } 227 228 ar->ar_gai_errno = 0; 229 230 /* If hostname is NULL, use local address */ 231 if (as->as.ai.hostname == NULL) { 232 for (family = iter_family(as, 1); 233 family != -1; 234 family = iter_family(as, 0)) { 235 /* 236 * We could use statically built sockaddrs for 237 * those, rather than parsing over and over. 238 */ 239 if (family == PF_INET) 240 str = (ai->ai_flags & AI_PASSIVE) ? \ 241 "0.0.0.0" : "127.0.0.1"; 242 else /* PF_INET6 */ 243 str = (ai->ai_flags & AI_PASSIVE) ? \ 244 "::" : "::1"; 245 /* This can't fail */ 246 _asr_sockaddr_from_str(&sa.sa, family, str); 247 if ((r = addrinfo_add(as, &sa.sa, NULL))) { 248 ar->ar_gai_errno = r; 249 break; 250 } 251 } 252 if (ar->ar_gai_errno == 0 && as->as_count == 0) { 253 ar->ar_gai_errno = EAI_NODATA; 254 } 255 async_set_state(as, ASR_STATE_HALT); 256 break; 257 } 258 259 /* Try numeric addresses first */ 260 for (family = iter_family(as, 1); 261 family != -1; 262 family = iter_family(as, 0)) { 263 264 if (_asr_sockaddr_from_str(&sa.sa, family, 265 as->as.ai.hostname) == -1) 266 continue; 267 268 if ((r = addrinfo_add(as, &sa.sa, NULL))) 269 ar->ar_gai_errno = r; 270 break; 271 } 272 if (ar->ar_gai_errno || as->as_count) { 273 async_set_state(as, ASR_STATE_HALT); 274 break; 275 } 276 277 if (ai->ai_flags & AI_NUMERICHOST) { 278 ar->ar_gai_errno = EAI_NONAME; 279 async_set_state(as, ASR_STATE_HALT); 280 break; 281 } 282 283 async_set_state(as, ASR_STATE_NEXT_DB); 284 break; 285 286 case ASR_STATE_NEXT_DB: 287 if (_asr_iter_db(as) == -1) { 288 async_set_state(as, ASR_STATE_NOT_FOUND); 289 break; 290 } 291 as->as_family_idx = 0; 292 async_set_state(as, ASR_STATE_SAME_DB); 293 break; 294 295 case ASR_STATE_NEXT_FAMILY: 296 as->as_family_idx += 1; 297 if (as->as.ai.hints.ai_family != AF_UNSPEC || 298 AS_FAMILY(as) == -1) { 299 /* The family was specified, or we have tried all 300 * families with this DB. 301 */ 302 if (as->as_count) { 303 ar->ar_gai_errno = 0; 304 async_set_state(as, ASR_STATE_HALT); 305 } else 306 async_set_state(as, ASR_STATE_NEXT_DOMAIN); 307 break; 308 } 309 async_set_state(as, ASR_STATE_SAME_DB); 310 break; 311 312 case ASR_STATE_NEXT_DOMAIN: 313 /* domain search is only for dns */ 314 if (AS_DB(as) != ASR_DB_DNS) { 315 async_set_state(as, ASR_STATE_NEXT_DB); 316 break; 317 } 318 as->as_family_idx = 0; 319 320 free(as->as.ai.fqdn); 321 as->as.ai.fqdn = NULL; 322 r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn)); 323 if (r == -1) { 324 async_set_state(as, ASR_STATE_NEXT_DB); 325 break; 326 } 327 if (r == 0) { 328 ar->ar_gai_errno = EAI_FAIL; 329 async_set_state(as, ASR_STATE_HALT); 330 break; 331 } 332 as->as.ai.fqdn = strdup(fqdn); 333 if (as->as.ai.fqdn == NULL) { 334 ar->ar_gai_errno = EAI_MEMORY; 335 async_set_state(as, ASR_STATE_HALT); 336 } 337 338 async_set_state(as, ASR_STATE_SAME_DB); 339 break; 340 341 case ASR_STATE_SAME_DB: 342 /* query the current DB again */ 343 switch (AS_DB(as)) { 344 case ASR_DB_DNS: 345 if (as->as.ai.fqdn == NULL) { 346 /* First try, initialize domain iteration */ 347 as->as_dom_flags = 0; 348 as->as_dom_step = DOM_INIT; 349 async_set_state(as, ASR_STATE_NEXT_DOMAIN); 350 break; 351 } 352 353 family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? 354 AS_FAMILY(as) : as->as.ai.hints.ai_family; 355 356 if (family == AF_INET && 357 as->as.ai.flags & ASYNC_NO_INET) { 358 async_set_state(as, ASR_STATE_NEXT_FAMILY); 359 break; 360 } else if (family == AF_INET6 && 361 as->as.ai.flags & ASYNC_NO_INET6) { 362 async_set_state(as, ASR_STATE_NEXT_FAMILY); 363 break; 364 } 365 366 as->as.ai.subq = _res_query_async_ctx(as->as.ai.fqdn, 367 C_IN, (family == AF_INET6) ? T_AAAA : T_A, 368 as->as_ctx); 369 370 if (as->as.ai.subq == NULL) { 371 if (errno == ENOMEM) 372 ar->ar_gai_errno = EAI_MEMORY; 373 else 374 ar->ar_gai_errno = EAI_FAIL; 375 async_set_state(as, ASR_STATE_HALT); 376 break; 377 } 378 async_set_state(as, ASR_STATE_SUBQUERY); 379 break; 380 381 case ASR_DB_FILE: 382 f = fopen(_PATH_HOSTS, "re"); 383 if (f == NULL) { 384 async_set_state(as, ASR_STATE_NEXT_DB); 385 break; 386 } 387 family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? 388 AS_FAMILY(as) : as->as.ai.hints.ai_family; 389 390 r = addrinfo_from_file(as, family, f); 391 if (r == -1) { 392 if (errno == ENOMEM) 393 ar->ar_gai_errno = EAI_MEMORY; 394 else 395 ar->ar_gai_errno = EAI_FAIL; 396 async_set_state(as, ASR_STATE_HALT); 397 } else 398 async_set_state(as, ASR_STATE_NEXT_FAMILY); 399 fclose(f); 400 break; 401 402 default: 403 async_set_state(as, ASR_STATE_NEXT_DB); 404 } 405 break; 406 407 case ASR_STATE_SUBQUERY: 408 if ((r = asr_run(as->as.ai.subq, ar)) == ASYNC_COND) 409 return (ASYNC_COND); 410 411 as->as.ai.subq = NULL; 412 413 if (ar->ar_datalen == -1) { 414 async_set_state(as, ASR_STATE_NEXT_FAMILY); 415 break; 416 } 417 418 r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen); 419 if (r == -1) { 420 if (errno == ENOMEM) 421 ar->ar_gai_errno = EAI_MEMORY; 422 else 423 ar->ar_gai_errno = EAI_FAIL; 424 async_set_state(as, ASR_STATE_HALT); 425 } else 426 async_set_state(as, ASR_STATE_NEXT_FAMILY); 427 free(ar->ar_data); 428 break; 429 430 case ASR_STATE_NOT_FOUND: 431 /* No result found. Maybe we can try again. */ 432 if (as->as.ai.flags & ASYNC_AGAIN) 433 ar->ar_gai_errno = EAI_AGAIN; 434 else 435 ar->ar_gai_errno = EAI_NODATA; 436 async_set_state(as, ASR_STATE_HALT); 437 break; 438 439 case ASR_STATE_HALT: 440 if (ar->ar_gai_errno == 0) { 441 ar->ar_count = as->as_count; 442 ar->ar_addrinfo = as->as.ai.aifirst; 443 as->as.ai.aifirst = NULL; 444 } else { 445 ar->ar_count = 0; 446 ar->ar_addrinfo = NULL; 447 } 448 return (ASYNC_DONE); 449 450 default: 451 ar->ar_errno = EOPNOTSUPP; 452 ar->ar_gai_errno = EAI_SYSTEM; 453 async_set_state(as, ASR_STATE_HALT); 454 break; 455 } 456 goto next; 457 } 458 459 /* 460 * Retreive the port number for the service name "servname" and 461 * the protocol "proto". 462 */ 463 static int 464 get_port(const char *servname, const char *proto, int numonly) 465 { 466 struct servent se; 467 struct servent_data sed; 468 int port, r; 469 const char *e; 470 471 if (servname == NULL) 472 return (0); 473 474 e = NULL; 475 port = strtonum(servname, 0, USHRT_MAX, &e); 476 if (e == NULL) 477 return (port); 478 if (errno == ERANGE) 479 return (-2); /* invalid */ 480 if (numonly) 481 return (-2); 482 483 memset(&sed, 0, sizeof(sed)); 484 r = getservbyname_r(servname, proto, &se, &sed); 485 port = ntohs(se.s_port); 486 endservent_r(&sed); 487 488 if (r == -1) 489 return (-1); /* not found */ 490 491 return (port); 492 } 493 494 /* 495 * Iterate over the address families that are to be queried. Use the 496 * list on the async context, unless a specific family was given in hints. 497 */ 498 static int 499 iter_family(struct asr_query *as, int first) 500 { 501 if (first) { 502 as->as_family_idx = 0; 503 if (as->as.ai.hints.ai_family != PF_UNSPEC) 504 return as->as.ai.hints.ai_family; 505 return AS_FAMILY(as); 506 } 507 508 if (as->as.ai.hints.ai_family != PF_UNSPEC) 509 return (-1); 510 511 as->as_family_idx++; 512 513 return AS_FAMILY(as); 514 } 515 516 /* 517 * Use the sockaddr at "sa" to extend the result list on the "as" context, 518 * with the specified canonical name "cname". This function adds one 519 * entry per protocol/socktype match. 520 */ 521 static int 522 addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname) 523 { 524 struct addrinfo *ai; 525 int i, port, proto; 526 527 for (i = 0; matches[i].family != -1; i++) { 528 if (matches[i].family != sa->sa_family || 529 !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || 530 !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) 531 continue; 532 533 proto = as->as.ai.hints.ai_protocol; 534 if (!proto) 535 proto = matches[i].protocol; 536 537 if (proto == IPPROTO_TCP) 538 port = as->as.ai.port_tcp; 539 else if (proto == IPPROTO_UDP) 540 port = as->as.ai.port_udp; 541 else 542 port = 0; 543 544 /* servname specified, but not defined for this protocol */ 545 if (port == -1) 546 continue; 547 548 ai = calloc(1, sizeof(*ai) + sa->sa_len); 549 if (ai == NULL) 550 return (EAI_MEMORY); 551 ai->ai_family = sa->sa_family; 552 ai->ai_socktype = matches[i].socktype; 553 ai->ai_protocol = proto; 554 ai->ai_flags = as->as.ai.hints.ai_flags; 555 ai->ai_addrlen = sa->sa_len; 556 ai->ai_addr = (void *)(ai + 1); 557 if (cname && 558 as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { 559 if ((ai->ai_canonname = strdup(cname)) == NULL) { 560 free(ai); 561 return (EAI_MEMORY); 562 } 563 } 564 memmove(ai->ai_addr, sa, sa->sa_len); 565 if (sa->sa_family == PF_INET) 566 ((struct sockaddr_in *)ai->ai_addr)->sin_port = 567 htons(port); 568 else if (sa->sa_family == PF_INET6) 569 ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = 570 htons(port); 571 572 if (as->as.ai.aifirst == NULL) 573 as->as.ai.aifirst = ai; 574 if (as->as.ai.ailast) 575 as->as.ai.ailast->ai_next = ai; 576 as->as.ai.ailast = ai; 577 as->as_count += 1; 578 } 579 580 return (0); 581 } 582 583 static int 584 addrinfo_from_file(struct asr_query *as, int family, FILE *f) 585 { 586 char *tokens[MAXTOKEN], *c, buf[BUFSIZ + 1]; 587 int n, i; 588 union { 589 struct sockaddr sa; 590 struct sockaddr_in sain; 591 struct sockaddr_in6 sain6; 592 } u; 593 594 for (;;) { 595 n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); 596 if (n == -1) 597 break; /* ignore errors reading the file */ 598 599 for (i = 1; i < n; i++) { 600 if (strcasecmp(as->as.ai.hostname, tokens[i])) 601 continue; 602 if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1) 603 continue; 604 break; 605 } 606 if (i == n) 607 continue; 608 609 if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) 610 c = tokens[1]; 611 else 612 c = NULL; 613 614 if (addrinfo_add(as, &u.sa, c)) 615 return (-1); /* errno set */ 616 } 617 return (0); 618 } 619 620 static int 621 addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen) 622 { 623 struct asr_unpack p; 624 struct asr_dns_header h; 625 struct asr_dns_query q; 626 struct asr_dns_rr rr; 627 int i; 628 union { 629 struct sockaddr sa; 630 struct sockaddr_in sain; 631 struct sockaddr_in6 sain6; 632 } u; 633 char buf[MAXDNAME], *c; 634 635 _asr_unpack_init(&p, pkt, pktlen); 636 _asr_unpack_header(&p, &h); 637 for (; h.qdcount; h.qdcount--) 638 _asr_unpack_query(&p, &q); 639 640 for (i = 0; i < h.ancount; i++) { 641 _asr_unpack_rr(&p, &rr); 642 if (rr.rr_type != q.q_type || 643 rr.rr_class != q.q_class) 644 continue; 645 646 memset(&u, 0, sizeof u); 647 if (rr.rr_type == T_A) { 648 u.sain.sin_len = sizeof u.sain; 649 u.sain.sin_family = AF_INET; 650 u.sain.sin_addr = rr.rr.in_a.addr; 651 u.sain.sin_port = 0; 652 } else if (rr.rr_type == T_AAAA) { 653 u.sain6.sin6_len = sizeof u.sain6; 654 u.sain6.sin6_family = AF_INET6; 655 u.sain6.sin6_addr = rr.rr.in_aaaa.addr6; 656 u.sain6.sin6_port = 0; 657 } else 658 continue; 659 660 if (as->as.ai.hints.ai_flags & AI_CANONNAME) { 661 _asr_strdname(rr.rr_dname, buf, sizeof buf); 662 buf[strlen(buf) - 1] = '\0'; 663 c = res_hnok(buf) ? buf : NULL; 664 } else if (as->as.ai.hints.ai_flags & AI_FQDN) 665 c = as->as.ai.fqdn; 666 else 667 c = NULL; 668 669 if (addrinfo_add(as, &u.sa, c)) 670 return (-1); /* errno set */ 671 } 672 return (0); 673 } 674 675 static int 676 addrconfig_setup(struct asr_query *as) 677 { 678 struct ifaddrs *ifa, *ifa0; 679 struct sockaddr_in *sinp; 680 struct sockaddr_in6 *sin6p; 681 682 if (getifaddrs(&ifa0) != 0) 683 return (-1); 684 685 as->as.ai.flags |= ASYNC_NO_INET | ASYNC_NO_INET6; 686 687 for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { 688 if (ifa->ifa_addr == NULL) 689 continue; 690 691 switch (ifa->ifa_addr->sa_family) { 692 case PF_INET: 693 sinp = (struct sockaddr_in *)ifa->ifa_addr; 694 695 if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) 696 continue; 697 698 as->as.ai.flags &= ~ASYNC_NO_INET; 699 break; 700 case PF_INET6: 701 sin6p = (struct sockaddr_in6 *)ifa->ifa_addr; 702 703 if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr)) 704 continue; 705 706 if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr)) 707 continue; 708 709 as->as.ai.flags &= ~ASYNC_NO_INET6; 710 break; 711 } 712 } 713 714 freeifaddrs(ifa0); 715 716 return (0); 717 } 718