1 /* $OpenBSD: getaddrinfo_async.c,v 1.36 2015/05/05 17:08:44 jca 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> /* for res_hnok */ 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 iter_domain(struct asr_query *, const char *, char *, size_t); 55 static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *); 56 static int addrinfo_from_file(struct asr_query *, int, FILE *); 57 static int addrinfo_from_pkt(struct asr_query *, char *, size_t); 58 static int addrconfig_setup(struct asr_query *); 59 #ifdef YP 60 static int addrinfo_from_yp(struct asr_query *, int, char *); 61 #endif 62 63 static const struct match matches[] = { 64 { PF_INET, SOCK_DGRAM, IPPROTO_UDP }, 65 { PF_INET, SOCK_STREAM, IPPROTO_TCP }, 66 { PF_INET, SOCK_RAW, 0 }, 67 { PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, 68 { PF_INET6, SOCK_STREAM, IPPROTO_TCP }, 69 { PF_INET6, SOCK_RAW, 0 }, 70 { -1, 0, 0, }, 71 }; 72 73 #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC) 74 #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0) 75 /* Do not match SOCK_RAW unless explicitely specified */ 76 #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \ 77 matches[(b)].socktype != SOCK_RAW)) 78 79 enum { 80 DOM_INIT, 81 DOM_DOMAIN, 82 DOM_DONE 83 }; 84 85 struct asr_query * 86 getaddrinfo_async(const char *hostname, const char *servname, 87 const struct addrinfo *hints, void *asr) 88 { 89 struct asr_ctx *ac; 90 struct asr_query *as; 91 char alias[MAXDNAME]; 92 93 ac = asr_use_resolver(asr); 94 if ((as = asr_async_new(ac, ASR_GETADDRINFO)) == NULL) 95 goto abort; /* errno set */ 96 as->as_run = getaddrinfo_async_run; 97 98 if (hostname) { 99 if (asr_hostalias(ac, hostname, alias, sizeof(alias))) 100 hostname = alias; 101 if ((as->as.ai.hostname = strdup(hostname)) == NULL) 102 goto abort; /* errno set */ 103 } 104 if (servname && (as->as.ai.servname = strdup(servname)) == NULL) 105 goto abort; /* errno set */ 106 if (hints) 107 memmove(&as->as.ai.hints, hints, sizeof *hints); 108 else { 109 memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints); 110 as->as.ai.hints.ai_family = PF_UNSPEC; 111 as->as.ai.hints.ai_flags = AI_ADDRCONFIG; 112 } 113 114 asr_ctx_unref(ac); 115 return (as); 116 abort: 117 if (as) 118 asr_async_free(as); 119 asr_ctx_unref(ac); 120 return (NULL); 121 } 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 = 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(as->as_ctx->ac_hostfile, "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 * Implement the search domain strategy. 584 * 585 * XXX duplicate from res_search_async 586 * 587 * This function works as a generator that constructs complete domains in 588 * buffer "buf" of size "len" for the given host name "name", according to the 589 * search rules defined by the resolving context. It is supposed to be called 590 * multiple times (with the same name) to generate the next possible domain 591 * name, if any. 592 * 593 * It returns -1 if all possibilities have been exhausted, 0 if there was an 594 * error generating the next name, or the resulting name length. 595 */ 596 static int 597 iter_domain(struct asr_query *as, const char *name, char * buf, size_t len) 598 { 599 const char *c; 600 int dots; 601 602 switch (as->as_dom_step) { 603 604 case DOM_INIT: 605 /* First call */ 606 607 /* 608 * If "name" is an FQDN, that's the only result and we 609 * don't try anything else. 610 */ 611 if (strlen(name) && name[strlen(name) - 1] == '.') { 612 DPRINT("asr: iter_domain(\"%s\") fqdn\n", name); 613 as->as_dom_flags |= ASYNC_DOM_FQDN; 614 as->as_dom_step = DOM_DONE; 615 return (domcat(name, NULL, buf, len)); 616 } 617 618 /* 619 * Otherwise, we iterate through the specified search domains. 620 */ 621 as->as_dom_step = DOM_DOMAIN; 622 as->as_dom_idx = 0; 623 624 /* 625 * If "name" as enough dots, use it as-is first, as indicated 626 * in resolv.conf(5). 627 */ 628 dots = 0; 629 for (c = name; *c; c++) 630 dots += (*c == '.'); 631 if (dots >= as->as_ctx->ac_ndots) { 632 DPRINT("asr: iter_domain(\"%s\") ndots\n", name); 633 as->as_dom_flags |= ASYNC_DOM_NDOTS; 634 if (strlcpy(buf, name, len) >= len) 635 return (0); 636 return (strlen(buf)); 637 } 638 /* Otherwise, starts using the search domains */ 639 /* FALLTHROUGH */ 640 641 case DOM_DOMAIN: 642 if (as->as_dom_idx < as->as_ctx->ac_domcount) { 643 DPRINT("asr: iter_domain(\"%s\") domain \"%s\"\n", 644 name, as->as_ctx->ac_dom[as->as_dom_idx]); 645 as->as_dom_flags |= ASYNC_DOM_DOMAIN; 646 return (domcat(name, 647 as->as_ctx->ac_dom[as->as_dom_idx++], buf, len)); 648 } 649 650 /* No more domain to try. */ 651 652 as->as_dom_step = DOM_DONE; 653 654 /* 655 * If the name was not tried as an absolute name before, 656 * do it now. 657 */ 658 if (!(as->as_dom_flags & ASYNC_DOM_NDOTS)) { 659 DPRINT("asr: iter_domain(\"%s\") as is\n", name); 660 as->as_dom_flags |= ASYNC_DOM_ASIS; 661 if (strlcpy(buf, name, len) >= len) 662 return (0); 663 return (strlen(buf)); 664 } 665 /* Otherwise, we are done. */ 666 667 case DOM_DONE: 668 default: 669 DPRINT("asr: iter_domain(\"%s\") done\n", name); 670 return (-1); 671 } 672 } 673 674 /* 675 * Use the sockaddr at "sa" to extend the result list on the "as" context, 676 * with the specified canonical name "cname". This function adds one 677 * entry per protocol/socktype match. 678 */ 679 static int 680 addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname) 681 { 682 struct addrinfo *ai; 683 int i, port, proto; 684 685 for (i = 0; matches[i].family != -1; i++) { 686 if (matches[i].family != sa->sa_family || 687 !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || 688 !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) 689 continue; 690 691 proto = as->as.ai.hints.ai_protocol; 692 if (!proto) 693 proto = matches[i].protocol; 694 695 if (proto == IPPROTO_TCP) 696 port = as->as.ai.port_tcp; 697 else if (proto == IPPROTO_UDP) 698 port = as->as.ai.port_udp; 699 else 700 port = 0; 701 702 /* servname specified, but not defined for this protocol */ 703 if (port == -1) 704 continue; 705 706 ai = calloc(1, sizeof(*ai) + sa->sa_len); 707 if (ai == NULL) 708 return (EAI_MEMORY); 709 ai->ai_family = sa->sa_family; 710 ai->ai_socktype = matches[i].socktype; 711 ai->ai_protocol = proto; 712 ai->ai_flags = as->as.ai.hints.ai_flags; 713 ai->ai_addrlen = sa->sa_len; 714 ai->ai_addr = (void *)(ai + 1); 715 if (cname && 716 as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { 717 if ((ai->ai_canonname = strdup(cname)) == NULL) { 718 free(ai); 719 return (EAI_MEMORY); 720 } 721 } 722 memmove(ai->ai_addr, sa, sa->sa_len); 723 if (sa->sa_family == PF_INET) 724 ((struct sockaddr_in *)ai->ai_addr)->sin_port = 725 htons(port); 726 else if (sa->sa_family == PF_INET6) 727 ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = 728 htons(port); 729 730 if (as->as.ai.aifirst == NULL) 731 as->as.ai.aifirst = ai; 732 if (as->as.ai.ailast) 733 as->as.ai.ailast->ai_next = ai; 734 as->as.ai.ailast = ai; 735 as->as_count += 1; 736 } 737 738 return (0); 739 } 740 741 static int 742 addrinfo_from_file(struct asr_query *as, int family, FILE *f) 743 { 744 char *tokens[MAXTOKEN], *c; 745 int n, i; 746 union { 747 struct sockaddr sa; 748 struct sockaddr_in sain; 749 struct sockaddr_in6 sain6; 750 } u; 751 752 for (;;) { 753 n = asr_parse_namedb_line(f, tokens, MAXTOKEN); 754 if (n == -1) 755 break; /* ignore errors reading the file */ 756 757 for (i = 1; i < n; i++) { 758 if (strcasecmp(as->as.ai.hostname, tokens[i])) 759 continue; 760 if (asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1) 761 continue; 762 break; 763 } 764 if (i == n) 765 continue; 766 767 if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) 768 c = tokens[1]; 769 else 770 c = NULL; 771 772 if (addrinfo_add(as, &u.sa, c)) 773 return (-1); /* errno set */ 774 } 775 return (0); 776 } 777 778 static int 779 addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen) 780 { 781 struct asr_unpack p; 782 struct asr_dns_header h; 783 struct asr_dns_query q; 784 struct asr_dns_rr rr; 785 int i; 786 union { 787 struct sockaddr sa; 788 struct sockaddr_in sain; 789 struct sockaddr_in6 sain6; 790 } u; 791 char buf[MAXDNAME], *c; 792 793 asr_unpack_init(&p, pkt, pktlen); 794 asr_unpack_header(&p, &h); 795 for (; h.qdcount; h.qdcount--) 796 asr_unpack_query(&p, &q); 797 798 for (i = 0; i < h.ancount; i++) { 799 asr_unpack_rr(&p, &rr); 800 if (rr.rr_type != q.q_type || 801 rr.rr_class != q.q_class) 802 continue; 803 804 memset(&u, 0, sizeof u); 805 if (rr.rr_type == T_A) { 806 u.sain.sin_len = sizeof u.sain; 807 u.sain.sin_family = AF_INET; 808 u.sain.sin_addr = rr.rr.in_a.addr; 809 u.sain.sin_port = 0; 810 } else if (rr.rr_type == T_AAAA) { 811 u.sain6.sin6_len = sizeof u.sain6; 812 u.sain6.sin6_family = AF_INET6; 813 u.sain6.sin6_addr = rr.rr.in_aaaa.addr6; 814 u.sain6.sin6_port = 0; 815 } else 816 continue; 817 818 if (as->as.ai.hints.ai_flags & AI_CANONNAME) { 819 asr_strdname(rr.rr_dname, buf, sizeof buf); 820 buf[strlen(buf) - 1] = '\0'; 821 c = res_hnok(buf) ? buf : NULL; 822 } else if (as->as.ai.hints.ai_flags & AI_FQDN) 823 c = as->as.ai.fqdn; 824 else 825 c = NULL; 826 827 if (addrinfo_add(as, &u.sa, c)) 828 return (-1); /* errno set */ 829 } 830 return (0); 831 } 832 833 static int 834 addrconfig_setup(struct asr_query *as) 835 { 836 struct ifaddrs *ifa, *ifa0; 837 struct sockaddr_in *sinp; 838 struct sockaddr_in6 *sin6p; 839 840 if (getifaddrs(&ifa0) != 0) 841 return (-1); 842 843 as->as.ai.flags |= ASYNC_NO_INET | ASYNC_NO_INET6; 844 845 for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { 846 if (ifa->ifa_addr == NULL) 847 continue; 848 849 switch (ifa->ifa_addr->sa_family) { 850 case PF_INET: 851 sinp = (struct sockaddr_in *)ifa->ifa_addr; 852 853 if (sinp->sin_addr.s_addr == INADDR_LOOPBACK) 854 continue; 855 856 as->as.ai.flags &= ~ASYNC_NO_INET; 857 break; 858 case PF_INET6: 859 sin6p = (struct sockaddr_in6 *)ifa->ifa_addr; 860 861 if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr)) 862 continue; 863 864 if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr)) 865 continue; 866 867 as->as.ai.flags &= ~ASYNC_NO_INET6; 868 break; 869 } 870 } 871 872 freeifaddrs(ifa0); 873 874 return (0); 875 } 876 877 #ifdef YP 878 static int 879 strsplit(char *line, char **tokens, int ntokens) 880 { 881 int ntok; 882 char *cp, **tp; 883 884 for (cp = line, tp = tokens, ntok = 0; 885 ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; ) 886 if (**tp != '\0') { 887 tp++; 888 ntok++; 889 } 890 891 return (ntok); 892 } 893 894 static int 895 addrinfo_from_yp(struct asr_query *as, int family, char *line) 896 { 897 char *next, *tokens[MAXTOKEN], *c; 898 int ntok; 899 union { 900 struct sockaddr sa; 901 struct sockaddr_in sain; 902 struct sockaddr_in6 sain6; 903 } u; 904 905 for (next = line; line; line = next) { 906 if ((next = strchr(line, '\n'))) { 907 *next = '\0'; 908 next += 1; 909 } 910 ntok = strsplit(line, tokens, MAXTOKEN); 911 if (ntok < 2) 912 continue; 913 914 if (asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1) 915 continue; 916 917 if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) 918 c = tokens[1]; 919 else 920 c = NULL; 921 922 if (addrinfo_add(as, &u.sa, c)) 923 return (-1); /* errno set */ 924 } 925 return (0); 926 } 927 #endif 928