1 /* $OpenBSD: getaddrinfo_async.c,v 1.43 2015/09/14 11:52:49 guenther 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 <err.h> 28 #include <errno.h> 29 #include <ifaddrs.h> 30 #include <resolv.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <limits.h> 35 36 #ifdef YP 37 #include <rpc/rpc.h> 38 #include <rpcsvc/yp.h> 39 #include <rpcsvc/ypclnt.h> 40 #include "ypinternal.h" 41 #endif 42 43 #include "asr_private.h" 44 45 struct match { 46 int family; 47 int socktype; 48 int protocol; 49 }; 50 51 static int getaddrinfo_async_run(struct asr_query *, struct asr_result *); 52 static int get_port(const char *, const char *, int); 53 static int iter_family(struct asr_query *, int); 54 static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *); 55 static int addrinfo_from_file(struct asr_query *, int, FILE *); 56 static int addrinfo_from_pkt(struct asr_query *, char *, size_t); 57 static int addrconfig_setup(struct asr_query *); 58 #ifdef YP 59 static int addrinfo_from_yp(struct asr_query *, int, char *); 60 #endif 61 62 static const struct match matches[] = { 63 { PF_INET, SOCK_DGRAM, IPPROTO_UDP }, 64 { PF_INET, SOCK_STREAM, IPPROTO_TCP }, 65 { PF_INET, SOCK_RAW, 0 }, 66 { PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, 67 { PF_INET6, SOCK_STREAM, IPPROTO_TCP }, 68 { PF_INET6, SOCK_RAW, 0 }, 69 { -1, 0, 0, }, 70 }; 71 72 #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC) 73 #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0) 74 /* Do not match SOCK_RAW unless explicitely specified */ 75 #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \ 76 matches[(b)].socktype != SOCK_RAW)) 77 78 enum { 79 DOM_INIT, 80 DOM_DOMAIN, 81 DOM_DONE 82 }; 83 84 struct asr_query * 85 getaddrinfo_async(const char *hostname, const char *servname, 86 const struct addrinfo *hints, void *asr) 87 { 88 struct asr_ctx *ac; 89 struct asr_query *as; 90 char alias[MAXDNAME]; 91 92 ac = _asr_use_resolver(asr); 93 if ((as = _asr_async_new(ac, ASR_GETADDRINFO)) == NULL) 94 goto abort; /* errno set */ 95 as->as_run = getaddrinfo_async_run; 96 97 if (hostname) { 98 if (_asr_hostalias(ac, hostname, alias, sizeof(alias))) 99 hostname = alias; 100 if ((as->as.ai.hostname = strdup(hostname)) == NULL) 101 goto abort; /* errno set */ 102 } 103 if (servname && (as->as.ai.servname = strdup(servname)) == NULL) 104 goto abort; /* errno set */ 105 if (hints) 106 memmove(&as->as.ai.hints, hints, sizeof *hints); 107 else { 108 memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints); 109 as->as.ai.hints.ai_family = PF_UNSPEC; 110 as->as.ai.hints.ai_flags = AI_ADDRCONFIG; 111 } 112 113 _asr_ctx_unref(ac); 114 return (as); 115 abort: 116 if (as) 117 _asr_async_free(as); 118 _asr_ctx_unref(ac); 119 return (NULL); 120 } 121 DEF_WEAK(getaddrinfo_async); 122 123 static int 124 getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar) 125 { 126 #ifdef YP 127 static char *domain = NULL; 128 char *res; 129 int len; 130 char *name; 131 #endif 132 char fqdn[MAXDNAME]; 133 const char *str; 134 struct addrinfo *ai; 135 int i, family, r; 136 FILE *f; 137 union { 138 struct sockaddr sa; 139 struct sockaddr_in sain; 140 struct sockaddr_in6 sain6; 141 } sa; 142 143 next: 144 switch (as->as_state) { 145 146 case ASR_STATE_INIT: 147 148 /* 149 * First, make sure the parameters are valid. 150 */ 151 152 as->as_count = 0; 153 154 if (as->as.ai.hostname == NULL && 155 as->as.ai.servname == NULL) { 156 ar->ar_gai_errno = EAI_NONAME; 157 async_set_state(as, ASR_STATE_HALT); 158 break; 159 } 160 161 if (as->as.ai.hostname && as->as.ai.hostname[0] == '\0') { 162 ar->ar_gai_errno = EAI_NODATA; 163 async_set_state(as, ASR_STATE_HALT); 164 break; 165 } 166 167 ai = &as->as.ai.hints; 168 169 if (ai->ai_addrlen || 170 ai->ai_canonname || 171 ai->ai_addr || 172 ai->ai_next) { 173 ar->ar_gai_errno = EAI_BADHINTS; 174 async_set_state(as, ASR_STATE_HALT); 175 break; 176 } 177 178 if (ai->ai_flags & ~AI_MASK || 179 (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) { 180 ar->ar_gai_errno = EAI_BADFLAGS; 181 async_set_state(as, ASR_STATE_HALT); 182 break; 183 } 184 185 if (ai->ai_family != PF_UNSPEC && 186 ai->ai_family != PF_INET && 187 ai->ai_family != PF_INET6) { 188 ar->ar_gai_errno = EAI_FAMILY; 189 async_set_state(as, ASR_STATE_HALT); 190 break; 191 } 192 193 if (ai->ai_socktype && 194 ai->ai_socktype != SOCK_DGRAM && 195 ai->ai_socktype != SOCK_STREAM && 196 ai->ai_socktype != SOCK_RAW) { 197 ar->ar_gai_errno = EAI_SOCKTYPE; 198 async_set_state(as, ASR_STATE_HALT); 199 break; 200 } 201 202 if (ai->ai_socktype == SOCK_RAW && 203 get_port(as->as.ai.servname, NULL, 1) != 0) { 204 ar->ar_gai_errno = EAI_SERVICE; 205 async_set_state(as, ASR_STATE_HALT); 206 break; 207 } 208 209 /* Restrict result set to configured address families */ 210 if (ai->ai_flags & AI_ADDRCONFIG) { 211 if (addrconfig_setup(as) != 0) { 212 ar->ar_gai_errno = EAI_FAIL; 213 async_set_state(as, ASR_STATE_HALT); 214 break; 215 } 216 } 217 218 /* Make sure there is at least a valid combination */ 219 for (i = 0; matches[i].family != -1; i++) 220 if (MATCH_FAMILY(ai->ai_family, i) && 221 MATCH_SOCKTYPE(ai->ai_socktype, i) && 222 MATCH_PROTO(ai->ai_protocol, i)) 223 break; 224 if (matches[i].family == -1) { 225 ar->ar_gai_errno = EAI_BADHINTS; 226 async_set_state(as, ASR_STATE_HALT); 227 break; 228 } 229 230 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) 231 as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", 232 as->as.ai.hints.ai_flags & AI_NUMERICSERV); 233 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) 234 as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", 235 as->as.ai.hints.ai_flags & AI_NUMERICSERV); 236 if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || 237 (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || 238 (ai->ai_protocol && (as->as.ai.port_udp == -1 || 239 as->as.ai.port_tcp == -1))) { 240 ar->ar_gai_errno = EAI_SERVICE; 241 async_set_state(as, ASR_STATE_HALT); 242 break; 243 } 244 245 ar->ar_gai_errno = 0; 246 247 /* If hostname is NULL, use local address */ 248 if (as->as.ai.hostname == NULL) { 249 for (family = iter_family(as, 1); 250 family != -1; 251 family = iter_family(as, 0)) { 252 /* 253 * We could use statically built sockaddrs for 254 * those, rather than parsing over and over. 255 */ 256 if (family == PF_INET) 257 str = (ai->ai_flags & AI_PASSIVE) ? \ 258 "0.0.0.0" : "127.0.0.1"; 259 else /* PF_INET6 */ 260 str = (ai->ai_flags & AI_PASSIVE) ? \ 261 "::" : "::1"; 262 /* This can't fail */ 263 _asr_sockaddr_from_str(&sa.sa, family, str); 264 if ((r = addrinfo_add(as, &sa.sa, NULL))) { 265 ar->ar_gai_errno = r; 266 break; 267 } 268 } 269 if (ar->ar_gai_errno == 0 && as->as_count == 0) { 270 ar->ar_gai_errno = EAI_NODATA; 271 } 272 async_set_state(as, ASR_STATE_HALT); 273 break; 274 } 275 276 /* Try numeric addresses first */ 277 for (family = iter_family(as, 1); 278 family != -1; 279 family = iter_family(as, 0)) { 280 281 if (_asr_sockaddr_from_str(&sa.sa, family, 282 as->as.ai.hostname) == -1) 283 continue; 284 285 if ((r = addrinfo_add(as, &sa.sa, NULL))) 286 ar->ar_gai_errno = r; 287 break; 288 } 289 if (ar->ar_gai_errno || as->as_count) { 290 async_set_state(as, ASR_STATE_HALT); 291 break; 292 } 293 294 if (ai->ai_flags & AI_NUMERICHOST) { 295 ar->ar_gai_errno = EAI_NONAME; 296 async_set_state(as, ASR_STATE_HALT); 297 break; 298 } 299 300 async_set_state(as, ASR_STATE_NEXT_DB); 301 break; 302 303 case ASR_STATE_NEXT_DB: 304 if (_asr_iter_db(as) == -1) { 305 async_set_state(as, ASR_STATE_NOT_FOUND); 306 break; 307 } 308 as->as_family_idx = 0; 309 async_set_state(as, ASR_STATE_SAME_DB); 310 break; 311 312 case ASR_STATE_NEXT_FAMILY: 313 as->as_family_idx += 1; 314 if (as->as.ai.hints.ai_family != AF_UNSPEC || 315 AS_FAMILY(as) == -1) { 316 /* The family was specified, or we have tried all 317 * families with this DB. 318 */ 319 if (as->as_count) { 320 ar->ar_gai_errno = 0; 321 async_set_state(as, ASR_STATE_HALT); 322 } else 323 async_set_state(as, ASR_STATE_NEXT_DOMAIN); 324 break; 325 } 326 async_set_state(as, ASR_STATE_SAME_DB); 327 break; 328 329 case ASR_STATE_NEXT_DOMAIN: 330 /* domain search is only for dns */ 331 if (AS_DB(as) != ASR_DB_DNS) { 332 async_set_state(as, ASR_STATE_NEXT_DB); 333 break; 334 } 335 as->as_family_idx = 0; 336 337 free(as->as.ai.fqdn); 338 as->as.ai.fqdn = NULL; 339 r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn)); 340 if (r == -1) { 341 async_set_state(as, ASR_STATE_NEXT_DB); 342 break; 343 } 344 if (r == 0) { 345 ar->ar_gai_errno = EAI_FAIL; 346 async_set_state(as, ASR_STATE_HALT); 347 break; 348 } 349 as->as.ai.fqdn = strdup(fqdn); 350 if (as->as.ai.fqdn == NULL) { 351 ar->ar_gai_errno = EAI_MEMORY; 352 async_set_state(as, ASR_STATE_HALT); 353 } 354 355 async_set_state(as, ASR_STATE_SAME_DB); 356 break; 357 358 case ASR_STATE_SAME_DB: 359 /* query the current DB again */ 360 switch (AS_DB(as)) { 361 case ASR_DB_DNS: 362 if (as->as.ai.fqdn == NULL) { 363 /* First try, initialize domain iteration */ 364 as->as_dom_flags = 0; 365 as->as_dom_step = DOM_INIT; 366 async_set_state(as, ASR_STATE_NEXT_DOMAIN); 367 break; 368 } 369 370 family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? 371 AS_FAMILY(as) : as->as.ai.hints.ai_family; 372 373 if (family == AF_INET && 374 as->as.ai.flags & ASYNC_NO_INET) { 375 async_set_state(as, ASR_STATE_NEXT_FAMILY); 376 break; 377 } else if (family == AF_INET6 && 378 as->as.ai.flags & ASYNC_NO_INET6) { 379 async_set_state(as, ASR_STATE_NEXT_FAMILY); 380 break; 381 } 382 383 as->as.ai.subq = _res_query_async_ctx(as->as.ai.fqdn, 384 C_IN, (family == AF_INET6) ? T_AAAA : T_A, 385 as->as_ctx); 386 387 if (as->as.ai.subq == NULL) { 388 if (errno == ENOMEM) 389 ar->ar_gai_errno = EAI_MEMORY; 390 else 391 ar->ar_gai_errno = EAI_FAIL; 392 async_set_state(as, ASR_STATE_HALT); 393 break; 394 } 395 async_set_state(as, ASR_STATE_SUBQUERY); 396 break; 397 398 case ASR_DB_FILE: 399 f = fopen(_PATH_HOSTS, "re"); 400 if (f == NULL) { 401 async_set_state(as, ASR_STATE_NEXT_DB); 402 break; 403 } 404 family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? 405 AS_FAMILY(as) : as->as.ai.hints.ai_family; 406 407 r = addrinfo_from_file(as, family, f); 408 if (r == -1) { 409 if (errno == ENOMEM) 410 ar->ar_gai_errno = EAI_MEMORY; 411 else 412 ar->ar_gai_errno = EAI_FAIL; 413 async_set_state(as, ASR_STATE_HALT); 414 } else 415 async_set_state(as, ASR_STATE_NEXT_FAMILY); 416 fclose(f); 417 break; 418 419 #ifdef YP 420 case ASR_DB_YP: 421 if (!domain && _yp_check(&domain) == 0) { 422 async_set_state(as, ASR_STATE_NEXT_DB); 423 break; 424 } 425 family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? 426 AS_FAMILY(as) : as->as.ai.hints.ai_family; 427 428 name = as->as.ai.hostname; 429 430 /* XXX 431 * ipnodes.byname could also contain IPv4 address 432 */ 433 r = yp_match(domain, (family == AF_INET6) ? 434 "ipnodes.byname" : "hosts.byname", 435 name, strlen(name), &res, &len); 436 if (r == 0) { 437 r = addrinfo_from_yp(as, family, res); 438 free(res); 439 if (r == -1) { 440 if (errno == ENOMEM) 441 ar->ar_gai_errno = EAI_MEMORY; 442 else 443 ar->ar_gai_errno = EAI_FAIL; 444 async_set_state(as, ASR_STATE_HALT); 445 break; 446 } 447 } 448 async_set_state(as, ASR_STATE_NEXT_FAMILY); 449 break; 450 #endif 451 default: 452 async_set_state(as, ASR_STATE_NEXT_DB); 453 } 454 break; 455 456 case ASR_STATE_SUBQUERY: 457 if ((r = asr_run(as->as.ai.subq, ar)) == ASYNC_COND) 458 return (ASYNC_COND); 459 460 as->as.ai.subq = NULL; 461 462 if (ar->ar_datalen == -1) { 463 async_set_state(as, ASR_STATE_NEXT_FAMILY); 464 break; 465 } 466 467 r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen); 468 if (r == -1) { 469 if (errno == ENOMEM) 470 ar->ar_gai_errno = EAI_MEMORY; 471 else 472 ar->ar_gai_errno = EAI_FAIL; 473 async_set_state(as, ASR_STATE_HALT); 474 } else 475 async_set_state(as, ASR_STATE_NEXT_FAMILY); 476 free(ar->ar_data); 477 break; 478 479 case ASR_STATE_NOT_FOUND: 480 /* No result found. Maybe we can try again. */ 481 if (as->as.ai.flags & ASYNC_AGAIN) 482 ar->ar_gai_errno = EAI_AGAIN; 483 else 484 ar->ar_gai_errno = EAI_NODATA; 485 async_set_state(as, ASR_STATE_HALT); 486 break; 487 488 case ASR_STATE_HALT: 489 if (ar->ar_gai_errno == 0) { 490 ar->ar_count = as->as_count; 491 ar->ar_addrinfo = as->as.ai.aifirst; 492 as->as.ai.aifirst = NULL; 493 } else { 494 ar->ar_count = 0; 495 ar->ar_addrinfo = NULL; 496 } 497 return (ASYNC_DONE); 498 499 default: 500 ar->ar_errno = EOPNOTSUPP; 501 ar->ar_gai_errno = EAI_SYSTEM; 502 async_set_state(as, ASR_STATE_HALT); 503 break; 504 } 505 goto next; 506 } 507 508 /* 509 * Retreive the port number for the service name "servname" and 510 * the protocol "proto". 511 */ 512 static int 513 get_port(const char *servname, const char *proto, int numonly) 514 { 515 struct servent se; 516 struct servent_data sed; 517 int port, r; 518 const char *e; 519 520 if (servname == NULL) 521 return (0); 522 523 e = NULL; 524 port = strtonum(servname, 0, USHRT_MAX, &e); 525 if (e == NULL) 526 return (port); 527 if (errno == ERANGE) 528 return (-2); /* invalid */ 529 if (numonly) 530 return (-2); 531 532 memset(&sed, 0, sizeof(sed)); 533 r = getservbyname_r(servname, proto, &se, &sed); 534 port = ntohs(se.s_port); 535 endservent_r(&sed); 536 537 if (r == -1) 538 return (-1); /* not found */ 539 540 return (port); 541 } 542 543 /* 544 * Iterate over the address families that are to be queried. Use the 545 * list on the async context, unless a specific family was given in hints. 546 */ 547 static int 548 iter_family(struct asr_query *as, int first) 549 { 550 if (first) { 551 as->as_family_idx = 0; 552 if (as->as.ai.hints.ai_family != PF_UNSPEC) 553 return as->as.ai.hints.ai_family; 554 return AS_FAMILY(as); 555 } 556 557 if (as->as.ai.hints.ai_family != PF_UNSPEC) 558 return (-1); 559 560 as->as_family_idx++; 561 562 return AS_FAMILY(as); 563 } 564 565 /* 566 * Concatenate a name and a domain name. The result has no trailing dot. 567 * Return the resulting string length, or 0 in case of error. 568 */ 569 static size_t 570 domcat(const char *name, const char *domain, char *buf, size_t buflen) 571 { 572 size_t r; 573 574 r = _asr_make_fqdn(name, domain, buf, buflen); 575 if (r == 0) 576 return (0); 577 buf[r - 1] = '\0'; 578 579 return (r - 1); 580 } 581 582 /* 583 * Use the sockaddr at "sa" to extend the result list on the "as" context, 584 * with the specified canonical name "cname". This function adds one 585 * entry per protocol/socktype match. 586 */ 587 static int 588 addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname) 589 { 590 struct addrinfo *ai; 591 int i, port, proto; 592 593 for (i = 0; matches[i].family != -1; i++) { 594 if (matches[i].family != sa->sa_family || 595 !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || 596 !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) 597 continue; 598 599 proto = as->as.ai.hints.ai_protocol; 600 if (!proto) 601 proto = matches[i].protocol; 602 603 if (proto == IPPROTO_TCP) 604 port = as->as.ai.port_tcp; 605 else if (proto == IPPROTO_UDP) 606 port = as->as.ai.port_udp; 607 else 608 port = 0; 609 610 /* servname specified, but not defined for this protocol */ 611 if (port == -1) 612 continue; 613 614 ai = calloc(1, sizeof(*ai) + sa->sa_len); 615 if (ai == NULL) 616 return (EAI_MEMORY); 617 ai->ai_family = sa->sa_family; 618 ai->ai_socktype = matches[i].socktype; 619 ai->ai_protocol = proto; 620 ai->ai_flags = as->as.ai.hints.ai_flags; 621 ai->ai_addrlen = sa->sa_len; 622 ai->ai_addr = (void *)(ai + 1); 623 if (cname && 624 as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { 625 if ((ai->ai_canonname = strdup(cname)) == NULL) { 626 free(ai); 627 return (EAI_MEMORY); 628 } 629 } 630 memmove(ai->ai_addr, sa, sa->sa_len); 631 if (sa->sa_family == PF_INET) 632 ((struct sockaddr_in *)ai->ai_addr)->sin_port = 633 htons(port); 634 else if (sa->sa_family == PF_INET6) 635 ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = 636 htons(port); 637 638 if (as->as.ai.aifirst == NULL) 639 as->as.ai.aifirst = ai; 640 if (as->as.ai.ailast) 641 as->as.ai.ailast->ai_next = ai; 642 as->as.ai.ailast = ai; 643 as->as_count += 1; 644 } 645 646 return (0); 647 } 648 649 static int 650 addrinfo_from_file(struct asr_query *as, int family, FILE *f) 651 { 652 char *tokens[MAXTOKEN], *c, buf[BUFSIZ + 1]; 653 int n, i; 654 union { 655 struct sockaddr sa; 656 struct sockaddr_in sain; 657 struct sockaddr_in6 sain6; 658 } u; 659 660 for (;;) { 661 n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); 662 if (n == -1) 663 break; /* ignore errors reading the file */ 664 665 for (i = 1; i < n; i++) { 666 if (strcasecmp(as->as.ai.hostname, tokens[i])) 667 continue; 668 if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1) 669 continue; 670 break; 671 } 672 if (i == n) 673 continue; 674 675 if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) 676 c = tokens[1]; 677 else 678 c = NULL; 679 680 if (addrinfo_add(as, &u.sa, c)) 681 return (-1); /* errno set */ 682 } 683 return (0); 684 } 685 686 static int 687 addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen) 688 { 689 struct asr_unpack p; 690 struct asr_dns_header h; 691 struct asr_dns_query q; 692 struct asr_dns_rr rr; 693 int i; 694 union { 695 struct sockaddr sa; 696 struct sockaddr_in sain; 697 struct sockaddr_in6 sain6; 698 } u; 699 char buf[MAXDNAME], *c; 700 701 _asr_unpack_init(&p, pkt, pktlen); 702 _asr_unpack_header(&p, &h); 703 for (; h.qdcount; h.qdcount--) 704 _asr_unpack_query(&p, &q); 705 706 for (i = 0; i < h.ancount; i++) { 707 _asr_unpack_rr(&p, &rr); 708 if (rr.rr_type != q.q_type || 709 rr.rr_class != q.q_class) 710 continue; 711 712 memset(&u, 0, sizeof u); 713 if (rr.rr_type == T_A) { 714 u.sain.sin_len = sizeof u.sain; 715 u.sain.sin_family = AF_INET; 716 u.sain.sin_addr = rr.rr.in_a.addr; 717 u.sain.sin_port = 0; 718 } else if (rr.rr_type == T_AAAA) { 719 u.sain6.sin6_len = sizeof u.sain6; 720 u.sain6.sin6_family = AF_INET6; 721 u.sain6.sin6_addr = rr.rr.in_aaaa.addr6; 722 u.sain6.sin6_port = 0; 723 } else 724 continue; 725 726 if (as->as.ai.hints.ai_flags & AI_CANONNAME) { 727 _asr_strdname(rr.rr_dname, buf, sizeof buf); 728 buf[strlen(buf) - 1] = '\0'; 729 c = res_hnok(buf) ? buf : NULL; 730 } else if (as->as.ai.hints.ai_flags & AI_FQDN) 731 c = as->as.ai.fqdn; 732 else 733 c = NULL; 734 735 if (addrinfo_add(as, &u.sa, c)) 736 return (-1); /* errno set */ 737 } 738 return (0); 739 } 740 741 static int 742 addrconfig_setup(struct asr_query *as) 743 { 744 struct ifaddrs *ifa, *ifa0; 745 struct sockaddr_in *sinp; 746 struct sockaddr_in6 *sin6p; 747 748 if (getifaddrs(&ifa0) != 0) 749 return (-1); 750 751 as->as.ai.flags |= ASYNC_NO_INET | ASYNC_NO_INET6; 752 753 for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { 754 if (ifa->ifa_addr == NULL) 755 continue; 756 757 switch (ifa->ifa_addr->sa_family) { 758 case PF_INET: 759 sinp = (struct sockaddr_in *)ifa->ifa_addr; 760 761 if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) 762 continue; 763 764 as->as.ai.flags &= ~ASYNC_NO_INET; 765 break; 766 case PF_INET6: 767 sin6p = (struct sockaddr_in6 *)ifa->ifa_addr; 768 769 if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr)) 770 continue; 771 772 if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr)) 773 continue; 774 775 as->as.ai.flags &= ~ASYNC_NO_INET6; 776 break; 777 } 778 } 779 780 freeifaddrs(ifa0); 781 782 return (0); 783 } 784 785 #ifdef YP 786 static int 787 strsplit(char *line, char **tokens, int ntokens) 788 { 789 int ntok; 790 char *cp, **tp; 791 792 for (cp = line, tp = tokens, ntok = 0; 793 ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; ) 794 if (**tp != '\0') { 795 tp++; 796 ntok++; 797 } 798 799 return (ntok); 800 } 801 802 static int 803 addrinfo_from_yp(struct asr_query *as, int family, char *line) 804 { 805 char *next, *tokens[MAXTOKEN], *c; 806 int ntok; 807 union { 808 struct sockaddr sa; 809 struct sockaddr_in sain; 810 struct sockaddr_in6 sain6; 811 } u; 812 813 for (next = line; line; line = next) { 814 if ((next = strchr(line, '\n'))) { 815 *next = '\0'; 816 next += 1; 817 } 818 ntok = strsplit(line, tokens, MAXTOKEN); 819 if (ntok < 2) 820 continue; 821 822 if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1) 823 continue; 824 825 if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) 826 c = tokens[1]; 827 else 828 c = NULL; 829 830 if (addrinfo_add(as, &u.sa, c)) 831 return (-1); /* errno set */ 832 } 833 return (0); 834 } 835 #endif 836