1 /* $OpenBSD: gethostnamadr_async.c,v 1.4 2012/07/10 15:58:00 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 #include <sys/types.h> 18 #include <sys/socket.h> 19 20 #include <netinet/in.h> 21 #include <arpa/inet.h> 22 #include <arpa/nameser.h> 23 24 #include <err.h> 25 #include <errno.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include "asr.h" 31 #include "asr_private.h" 32 33 34 #define MAXALIASES 16 35 #define MAXADDRS 16 36 37 ssize_t addr_as_fqdn(const char *, int, char *, size_t); 38 39 static int gethostnamadr_async_run(struct async *, struct async_res *); 40 static struct hostent *hostent_alloc(int); 41 static int hostent_set_cname(struct hostent *, const char *, int); 42 static int hostent_add_alias(struct hostent *, const char *, int); 43 static int hostent_add_addr(struct hostent *, const void *, int); 44 static struct hostent *hostent_file_match(FILE *, int, int, const char *, int); 45 static struct hostent *hostent_from_packet(int, int, char *, size_t); 46 47 struct async * 48 gethostbyname_async(const char *name, struct asr *asr) 49 { 50 return gethostbyname2_async(name, AF_INET, asr); 51 } 52 53 struct async * 54 gethostbyname2_async(const char *name, int af, struct asr *asr) 55 { 56 struct asr_ctx *ac; 57 struct async *as; 58 59 /* the original segfaults */ 60 if (name == NULL) { 61 errno = EINVAL; 62 return (NULL); 63 } 64 65 ac = asr_use_resolver(asr); 66 if ((as = async_new(ac, ASR_GETHOSTBYNAME)) == NULL) 67 goto abort; /* errno set */ 68 as->as_run = gethostnamadr_async_run; 69 70 as->as.hostnamadr.family = af; 71 if (af == AF_INET) 72 as->as.hostnamadr.addrlen = INADDRSZ; 73 else if (af == AF_INET6) 74 as->as.hostnamadr.addrlen = IN6ADDRSZ; 75 as->as.hostnamadr.name = strdup(name); 76 if (as->as.hostnamadr.name == NULL) 77 goto abort; /* errno set */ 78 79 asr_ctx_unref(ac); 80 return (as); 81 82 abort: 83 if (as) 84 async_free(as); 85 asr_ctx_unref(ac); 86 return (NULL); 87 } 88 89 struct async * 90 gethostbyaddr_async(const void *addr, socklen_t len, int af, struct asr *asr) 91 { 92 struct asr_ctx *ac; 93 struct async *as; 94 95 ac = asr_use_resolver(asr); 96 as = gethostbyaddr_async_ctx(addr, len, af, ac); 97 asr_ctx_unref(ac); 98 99 return (as); 100 } 101 102 struct async * 103 gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af, 104 struct asr_ctx *ac) 105 { 106 struct async *as; 107 108 if ((as = async_new(ac, ASR_GETHOSTBYADDR)) == NULL) 109 goto abort; /* errno set */ 110 as->as_run = gethostnamadr_async_run; 111 112 as->as.hostnamadr.family = af; 113 as->as.hostnamadr.addrlen = len; 114 if (len > 0) 115 memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len); 116 117 return (as); 118 119 abort: 120 if (as) 121 async_free(as); 122 return (NULL); 123 } 124 125 static int 126 gethostnamadr_async_run(struct async *as, struct async_res *ar) 127 { 128 int i, n, r, type; 129 FILE *f; 130 char dname[MAXDNAME], *data; 131 132 next: 133 switch(as->as_state) { 134 135 case ASR_STATE_INIT: 136 137 if (as->as.hostnamadr.family != AF_INET && 138 as->as.hostnamadr.family != AF_INET6) { 139 ar->ar_h_errno = NETDB_INTERNAL; 140 ar->ar_errno = EAFNOSUPPORT; 141 async_set_state(as, ASR_STATE_HALT); 142 break; 143 } 144 145 if ((as->as.hostnamadr.family == AF_INET && 146 as->as.hostnamadr.addrlen != INADDRSZ) || 147 (as->as.hostnamadr.family == AF_INET6 && 148 as->as.hostnamadr.addrlen != IN6ADDRSZ)) { 149 ar->ar_h_errno = NETDB_INTERNAL; 150 ar->ar_errno = EINVAL; 151 async_set_state(as, ASR_STATE_HALT); 152 break; 153 } 154 155 if (as->as_type == ASR_GETHOSTBYNAME) 156 async_set_state(as, ASR_STATE_NEXT_DOMAIN); 157 else 158 async_set_state(as, ASR_STATE_NEXT_DB); 159 break; 160 161 case ASR_STATE_NEXT_DOMAIN: 162 163 r = asr_iter_domain(as, as->as.hostnamadr.name, dname, sizeof(dname)); 164 if (r == -1) { 165 async_set_state(as, ASR_STATE_NOT_FOUND); 166 break; 167 } 168 169 if (as->as.hostnamadr.dname) 170 free(as->as.hostnamadr.dname); 171 if ((as->as.hostnamadr.dname = strdup(dname)) == NULL) { 172 ar->ar_h_errno = NETDB_INTERNAL; 173 ar->ar_errno = errno; 174 async_set_state(as, ASR_STATE_HALT); 175 } 176 177 as->as_db_idx = 0; 178 async_set_state(as, ASR_STATE_NEXT_DB); 179 break; 180 181 case ASR_STATE_NEXT_DB: 182 183 if (asr_iter_db(as) == -1) { 184 if (as->as_type == ASR_GETHOSTBYNAME) 185 async_set_state(as, ASR_STATE_NEXT_DOMAIN); 186 else 187 async_set_state(as, ASR_STATE_NOT_FOUND); 188 break; 189 } 190 191 switch(AS_DB(as)) { 192 193 case ASR_DB_DNS: 194 195 /* Create a subquery to do the DNS lookup */ 196 197 if (as->as_type == ASR_GETHOSTBYNAME) { 198 type = (as->as.hostnamadr.family == AF_INET) ? 199 T_A : T_AAAA; 200 as->as.hostnamadr.subq = res_query_async_ctx( 201 as->as.hostnamadr.dname, 202 C_IN, type, NULL, 0, as->as_ctx); 203 } else { 204 addr_as_fqdn(as->as.hostnamadr.addr, 205 as->as.hostnamadr.family, 206 dname, sizeof(dname)); 207 as->as.hostnamadr.subq = res_query_async_ctx( 208 dname, C_IN, T_PTR, NULL, 0, as->as_ctx); 209 } 210 211 if (as->as.hostnamadr.subq == NULL) { 212 ar->ar_errno = errno; 213 ar->ar_h_errno = NETDB_INTERNAL; 214 async_set_state(as, ASR_STATE_HALT); 215 break; 216 } 217 218 async_set_state(as, ASR_STATE_SUBQUERY); 219 break; 220 221 case ASR_DB_FILE: 222 223 /* Try to find a match in the host file */ 224 225 if ((f = fopen(as->as_ctx->ac_hostfile, "r")) == NULL) 226 break; 227 228 if (as->as_type == ASR_GETHOSTBYNAME) 229 data = as->as.hostnamadr.dname; 230 else 231 data = as->as.hostnamadr.addr; 232 233 ar->ar_hostent = hostent_file_match(f, as->as_type, 234 as->as.hostnamadr.family, data, 235 as->as.hostnamadr.addrlen); 236 237 fclose(f); 238 239 if (ar->ar_hostent == NULL) { 240 if (errno) { 241 ar->ar_errno = errno; 242 ar->ar_h_errno = NETDB_INTERNAL; 243 async_set_state(as, ASR_STATE_HALT); 244 } 245 /* otherwise not found */ 246 break; 247 } 248 249 ar->ar_h_errno = NETDB_SUCCESS; 250 async_set_state(as, ASR_STATE_HALT); 251 break; 252 } 253 break; 254 255 case ASR_STATE_SUBQUERY: 256 257 /* Run the DNS subquery. */ 258 259 if ((r = async_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND) 260 return (ASYNC_COND); 261 262 /* Done. */ 263 as->as.hostnamadr.subq = NULL; 264 265 if (ar->ar_datalen == -1) { 266 async_set_state(as, ASR_STATE_NEXT_DB); 267 break; 268 } 269 270 /* If we got a packet but no anwser, use the next DB. */ 271 if (ar->ar_count == 0) { 272 free(ar->ar_data); 273 async_set_state(as, ASR_STATE_NEXT_DB); 274 break; 275 } 276 277 /* Read the hostent from the packet. */ 278 279 ar->ar_hostent = hostent_from_packet(as->as_type, 280 as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen); 281 free(ar->ar_data); 282 283 if (ar->ar_hostent == NULL) { 284 ar->ar_errno = errno; 285 ar->ar_h_errno = NETDB_INTERNAL; 286 async_set_state(as, ASR_STATE_HALT); 287 break; 288 } 289 290 if (as->as_type == ASR_GETHOSTBYADDR) { 291 if (hostent_add_addr(ar->ar_hostent, 292 as->as.hostnamadr.addr, 293 as->as.hostnamadr.addrlen) == -1) { 294 freehostent(ar->ar_hostent); 295 ar->ar_errno = errno; 296 ar->ar_h_errno = NETDB_INTERNAL; 297 async_set_state(as, ASR_STATE_HALT); 298 break; 299 } 300 } 301 302 /* 303 * No address found in the dns packet. The blocking version 304 * reports this as an error. 305 */ 306 if (as->as_type == ASR_GETHOSTBYNAME && 307 ar->ar_hostent->h_addr_list[0] == NULL) { 308 freehostent(ar->ar_hostent); 309 async_set_state(as, ASR_STATE_NEXT_DB); 310 break; 311 } 312 313 ar->ar_h_errno = NETDB_SUCCESS; 314 async_set_state(as, ASR_STATE_HALT); 315 break; 316 317 case ASR_STATE_NOT_FOUND: 318 ar->ar_errno = 0; 319 ar->ar_h_errno = HOST_NOT_FOUND; 320 async_set_state(as, ASR_STATE_HALT); 321 break; 322 323 case ASR_STATE_HALT: 324 if (ar->ar_h_errno) 325 ar->ar_hostent = NULL; 326 else 327 ar->ar_errno = 0; 328 return (ASYNC_DONE); 329 330 default: 331 ar->ar_errno = EOPNOTSUPP; 332 ar->ar_h_errno = NETDB_INTERNAL; 333 ar->ar_gai_errno = EAI_SYSTEM; 334 async_set_state(as, ASR_STATE_HALT); 335 break; 336 } 337 goto next; 338 } 339 340 /* 341 * Lookup the first matching entry in the hostfile, either by address or by 342 * name depending on reqtype, and build a hostent from the line. 343 */ 344 static struct hostent * 345 hostent_file_match(FILE *f, int reqtype, int family, const char *data, int datalen) 346 { 347 char *tokens[MAXTOKEN], addr[16]; 348 struct hostent *h; 349 int n, i; 350 351 for(;;) { 352 n = asr_parse_namedb_line(f, tokens, MAXTOKEN); 353 if (n == -1) { 354 errno = 0; /* ignore errors reading the file */ 355 return (NULL); 356 } 357 358 if (reqtype == ASR_GETHOSTBYNAME) { 359 for (i = 1; i < n; i++) { 360 if (strcasecmp(data, tokens[i])) 361 continue; 362 if (inet_pton(family, tokens[0], addr) == 1) 363 goto found; 364 } 365 } else { 366 if (inet_pton(family, tokens[0], addr) == 1) 367 if (memcmp(addr, data, datalen) == 0) 368 goto found; 369 } 370 } 371 372 found: 373 if ((h = hostent_alloc(family)) == NULL) 374 return (NULL); 375 if (hostent_set_cname(h, tokens[1], 0) == -1) 376 goto fail; 377 for (i = 2; i < n; i ++) 378 if (hostent_add_alias(h, tokens[i], 0) == -1) 379 goto fail; 380 if (hostent_add_addr(h, addr, h->h_length) == -1) 381 goto fail; 382 return (h); 383 fail: 384 freehostent(h); 385 return (NULL); 386 } 387 388 /* 389 * Fill the hostent from the given DNS packet. 390 */ 391 static struct hostent * 392 hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen) 393 { 394 struct hostent *h; 395 struct packed p; 396 struct header hdr; 397 struct query q; 398 struct rr rr; 399 400 if ((h = hostent_alloc(family)) == NULL) 401 return (NULL); 402 403 packed_init(&p, pkt, pktlen); 404 unpack_header(&p, &hdr); 405 for(; hdr.qdcount; hdr.qdcount--) 406 unpack_query(&p, &q); 407 for(; hdr.ancount; hdr.ancount--) { 408 unpack_rr(&p, &rr); 409 if (rr.rr_class != C_IN) 410 continue; 411 switch (rr.rr_type) { 412 413 case T_CNAME: 414 if (reqtype == ASR_GETHOSTBYNAME) { 415 if (hostent_add_alias(h, rr.rr_dname, 1) == -1) 416 goto fail; 417 } else { 418 if (hostent_set_cname(h, rr.rr_dname, 1) == -1) 419 goto fail; 420 } 421 break; 422 423 case T_PTR: 424 if (reqtype != ASR_GETHOSTBYADDR) 425 break; 426 if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1) 427 goto fail; 428 /* XXX See if we need MULTI_PTRS_ARE_ALIASES */ 429 break; 430 431 case T_A: 432 if (reqtype != ASR_GETHOSTBYNAME) 433 break; 434 if (family != AF_INET) 435 break; 436 if (hostent_set_cname(h, rr.rr_dname, 1) == -1) 437 goto fail; 438 if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1) 439 goto fail; 440 break; 441 442 case T_AAAA: 443 if (reqtype != ASR_GETHOSTBYNAME) 444 break; 445 if (family != AF_INET6) 446 break; 447 if (hostent_set_cname(h, rr.rr_dname, 1) == -1) 448 goto fail; 449 if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1) 450 goto fail; 451 break; 452 } 453 } 454 455 return (h); 456 fail: 457 freehostent(h); 458 return (NULL); 459 } 460 461 static struct hostent * 462 hostent_alloc(int family) 463 { 464 struct hostent *h; 465 466 h = calloc(1, sizeof *h); 467 if (h == NULL) 468 return (NULL); 469 470 h->h_aliases = calloc(MAXALIASES, sizeof *h->h_aliases); 471 h->h_addr_list = calloc(MAXADDRS, sizeof *h->h_addr_list); 472 if (h->h_aliases == NULL || h->h_addr_list == NULL) { 473 freehostent(h); 474 return (NULL); 475 } 476 h->h_addrtype = family; 477 h->h_length = (family == AF_INET) ? 4 : 16; 478 479 return (h); 480 } 481 482 static int 483 hostent_set_cname(struct hostent *h, const char *name, int isdname) 484 { 485 char buf[MAXDNAME]; 486 487 if (h->h_name) 488 return (0); 489 490 if (isdname) { 491 asr_strdname(name, buf, sizeof buf); 492 buf[strlen(buf) - 1] = '\0'; 493 h->h_name = strdup(buf); 494 } else { 495 h->h_name = strdup(name); 496 } 497 if (h->h_name == NULL) 498 return (-1); 499 500 return (0); 501 } 502 503 static int 504 hostent_add_alias(struct hostent *h, const char *name, int isdname) 505 { 506 char buf[MAXDNAME]; 507 size_t i; 508 509 for (i = 0; i < MAXALIASES; i++) 510 if (h->h_aliases[i] == NULL) 511 break; 512 if (i == MAXALIASES) 513 return (0); 514 515 if (isdname) { 516 asr_strdname(name, buf, sizeof buf); 517 buf[strlen(buf)-1] = '\0'; 518 h->h_aliases[i] = strdup(buf); 519 } else { 520 h->h_aliases[i] = strdup(name); 521 } 522 if (h->h_aliases[i] == NULL) 523 return (-1); 524 525 return (0); 526 } 527 528 static int 529 hostent_add_addr(struct hostent *h, const void *addr, int size) 530 { 531 int i; 532 533 for (i = 0; i < MAXADDRS; i++) 534 if (h->h_addr_list[i] == NULL) 535 break; 536 if (i == MAXADDRS) 537 return (0); 538 539 h->h_addr_list[i] = malloc(size); 540 if (h->h_addr_list[i] == NULL) 541 return (-1); 542 memmove(h->h_addr_list[i], addr, size); 543 544 return (0); 545 } 546 547 void 548 freehostent(struct hostent *h) 549 { 550 char **c; 551 552 free(h->h_name); 553 for (c = h->h_aliases; *c; c++) 554 free(*c); 555 free(h->h_aliases); 556 for (c = h->h_addr_list; *c; c++) 557 free(*c); 558 free(h->h_addr_list); 559 free(h); 560 } 561 562 563 ssize_t 564 addr_as_fqdn(const char *addr, int family, char *dst, size_t max) 565 { 566 const struct in6_addr *in6_addr; 567 in_addr_t in_addr; 568 569 switch (family) { 570 case AF_INET: 571 in_addr = ntohl(*((const in_addr_t *)addr)); 572 snprintf(dst, max, 573 "%d.%d.%d.%d.in-addr.arpa.", 574 in_addr & 0xff, 575 (in_addr >> 8) & 0xff, 576 (in_addr >> 16) & 0xff, 577 (in_addr >> 24) & 0xff); 578 break; 579 case AF_INET6: 580 in6_addr = (const struct in6_addr *)addr; 581 snprintf(dst, max, 582 "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." 583 "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." 584 "ip6.arpa.", 585 in6_addr->s6_addr[15] & 0xf, 586 (in6_addr->s6_addr[15] >> 4) & 0xf, 587 in6_addr->s6_addr[14] & 0xf, 588 (in6_addr->s6_addr[14] >> 4) & 0xf, 589 in6_addr->s6_addr[13] & 0xf, 590 (in6_addr->s6_addr[13] >> 4) & 0xf, 591 in6_addr->s6_addr[12] & 0xf, 592 (in6_addr->s6_addr[12] >> 4) & 0xf, 593 in6_addr->s6_addr[11] & 0xf, 594 (in6_addr->s6_addr[11] >> 4) & 0xf, 595 in6_addr->s6_addr[10] & 0xf, 596 (in6_addr->s6_addr[10] >> 4) & 0xf, 597 in6_addr->s6_addr[9] & 0xf, 598 (in6_addr->s6_addr[9] >> 4) & 0xf, 599 in6_addr->s6_addr[8] & 0xf, 600 (in6_addr->s6_addr[8] >> 4) & 0xf, 601 in6_addr->s6_addr[7] & 0xf, 602 (in6_addr->s6_addr[7] >> 4) & 0xf, 603 in6_addr->s6_addr[6] & 0xf, 604 (in6_addr->s6_addr[6] >> 4) & 0xf, 605 in6_addr->s6_addr[5] & 0xf, 606 (in6_addr->s6_addr[5] >> 4) & 0xf, 607 in6_addr->s6_addr[4] & 0xf, 608 (in6_addr->s6_addr[4] >> 4) & 0xf, 609 in6_addr->s6_addr[3] & 0xf, 610 (in6_addr->s6_addr[3] >> 4) & 0xf, 611 in6_addr->s6_addr[2] & 0xf, 612 (in6_addr->s6_addr[2] >> 4) & 0xf, 613 in6_addr->s6_addr[1] & 0xf, 614 (in6_addr->s6_addr[1] >> 4) & 0xf, 615 in6_addr->s6_addr[0] & 0xf, 616 (in6_addr->s6_addr[0] >> 4) & 0xf); 617 break; 618 default: 619 return (-1); 620 } 621 return (0); 622 } 623