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