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