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