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