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