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