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