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