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