1 /* $NetBSD: hesiod.c,v 1.30 2017/03/10 18:02:32 maya Exp $ */ 2 3 /* Copyright (c) 1996 by Internet Software Consortium. 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS 10 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 11 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 12 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 15 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 16 * SOFTWARE. 17 */ 18 19 /* Copyright 1996 by the Massachusetts Institute of Technology. 20 * 21 * Permission to use, copy, modify, and distribute this 22 * software and its documentation for any purpose and without 23 * fee is hereby granted, provided that the above copyright 24 * notice appear in all copies and that both that copyright 25 * notice and this permission notice appear in supporting 26 * documentation, and that the name of M.I.T. not be used in 27 * advertising or publicity pertaining to distribution of the 28 * software without specific, written prior permission. 29 * M.I.T. makes no representations about the suitability of 30 * this software for any purpose. It is provided "as is" 31 * without express or implied warranty. 32 */ 33 34 /* This file is part of the hesiod library. It implements the core 35 * portion of the hesiod resolver. 36 * 37 * This file is loosely based on an interim version of hesiod.c from 38 * the BIND IRS library, which was in turn based on an earlier version 39 * of this file. Extensive changes have been made on each step of the 40 * path. 41 * 42 * This implementation is thread-safe because it uses res_nsend(). 43 */ 44 45 #include <sys/cdefs.h> 46 47 #if defined(LIBC_SCCS) && !defined(lint) 48 __IDSTRING(rcsid_hesiod_c, 49 "#Id: hesiod.c,v 1.18.2.1 1997/01/03 20:48:20 ghudson Exp #"); 50 __IDSTRING(rcsid_hesiod_p_h, 51 "#Id: hesiod_p.h,v 1.1 1996/12/08 21:39:37 ghudson Exp #"); 52 __IDSTRING(rcsid_hescompat_c, 53 "#Id: hescompat.c,v 1.1.2.1 1996/12/16 08:37:45 ghudson Exp #"); 54 __RCSID("$NetBSD: hesiod.c,v 1.30 2017/03/10 18:02:32 maya Exp $"); 55 #endif /* LIBC_SCCS and not lint */ 56 57 #include "namespace.h" 58 59 #include <sys/types.h> 60 #include <sys/param.h> 61 #include <netinet/in.h> 62 #include <arpa/nameser.h> 63 64 #include <assert.h> 65 #include <ctype.h> 66 #include <errno.h> 67 #include <hesiod.h> 68 #include <resolv.h> 69 #include <stdio.h> 70 #include <stdlib.h> 71 #include <string.h> 72 #include <unistd.h> 73 74 #ifdef __weak_alias 75 __weak_alias(hesiod_init,_hesiod_init) 76 __weak_alias(hesiod_end,_hesiod_end) 77 __weak_alias(hesiod_to_bind,_hesiod_to_bind) 78 __weak_alias(hesiod_resolve,_hesiod_resolve) 79 __weak_alias(hesiod_free_list,_hesiod_free_list) 80 __weak_alias(hes_init,_hes_init) 81 __weak_alias(hes_to_bind,_hes_to_bind) 82 __weak_alias(hes_resolve,_hes_resolve) 83 __weak_alias(hes_error,_hes_error) 84 __weak_alias(hes_free,_hes_free) 85 #endif 86 87 struct hesiod_p { 88 char *lhs; /* normally ".ns" */ 89 char *rhs; /* AKA the default hesiod domain */ 90 int classes[2]; /* The class search order. */ 91 }; 92 93 #define MAX_HESRESP 1024 94 95 static int read_config_file(struct hesiod_p *, const char *); 96 static char **get_txt_records(int, const char *); 97 static int init_context(void); 98 static void translate_errors(void); 99 100 101 /* 102 * hesiod_init -- 103 * initialize a hesiod_p. 104 */ 105 int 106 hesiod_init(void **context) 107 { 108 struct hesiod_p *ctx; 109 const char *p, *configname; 110 int serrno; 111 112 _DIAGASSERT(context != NULL); 113 114 ctx = calloc(1, sizeof(struct hesiod_p)); 115 if (ctx) { 116 *context = ctx; 117 /* 118 * don't permit overrides from environment 119 * for set.id programs 120 */ 121 if (issetugid()) 122 configname = NULL; 123 else 124 configname = getenv("HESIOD_CONFIG"); 125 if (!configname) 126 configname = _PATH_HESIOD_CONF; 127 if (read_config_file(ctx, configname) >= 0) { 128 /* 129 * The default rhs can be overridden by an 130 * environment variable, unless set.id. 131 */ 132 if (issetugid()) 133 p = NULL; 134 else 135 p = getenv("HES_DOMAIN"); 136 if (p) { 137 if (ctx->rhs) 138 free(ctx->rhs); 139 ctx->rhs = malloc(strlen(p) + 2); 140 if (ctx->rhs) { 141 *ctx->rhs = '.'; 142 strcpy(ctx->rhs + 1, 143 (*p == '.') ? p + 1 : p); 144 return 0; 145 } else 146 errno = ENOMEM; 147 } else 148 return 0; 149 } 150 } else 151 errno = ENOMEM; 152 153 serrno = errno; 154 if (ctx) { 155 free(ctx->lhs); 156 free(ctx->rhs); 157 free(ctx); 158 } 159 errno = serrno; 160 return -1; 161 } 162 163 /* 164 * hesiod_end -- 165 * Deallocates the hesiod_p. 166 */ 167 void 168 hesiod_end(void *context) 169 { 170 struct hesiod_p *ctx = (struct hesiod_p *) context; 171 172 _DIAGASSERT(context != NULL); 173 174 free(ctx->rhs); 175 if (ctx->lhs) 176 free(ctx->lhs); 177 free(ctx); 178 } 179 180 /* 181 * hesiod_to_bind -- 182 * takes a hesiod (name, type) and returns a DNS 183 * name which is to be resolved. 184 */ 185 char * 186 hesiod_to_bind(void *context, const char *name, const char *type) 187 { 188 struct hesiod_p *ctx = (struct hesiod_p *) context; 189 char bindname[MAXDNAME], *p, *ret, **rhs_list = NULL; 190 const char *rhs; 191 size_t len; 192 193 _DIAGASSERT(context != NULL); 194 _DIAGASSERT(name != NULL); 195 _DIAGASSERT(type != NULL); 196 197 if (strlcpy(bindname, name, sizeof(bindname)) >= sizeof(bindname)) { 198 errno = EMSGSIZE; 199 return NULL; 200 } 201 202 /* 203 * Find the right right hand side to use, possibly 204 * truncating bindname. 205 */ 206 p = strchr(bindname, '@'); 207 if (p) { 208 *p++ = 0; 209 if (strchr(p, '.')) 210 rhs = name + (p - bindname); 211 else { 212 rhs_list = hesiod_resolve(context, p, "rhs-extension"); 213 if (rhs_list) 214 rhs = *rhs_list; 215 else { 216 errno = ENOENT; 217 return NULL; 218 } 219 } 220 } else 221 rhs = ctx->rhs; 222 223 /* See if we have enough room. */ 224 len = strlen(bindname) + 1 + strlen(type); 225 if (ctx->lhs) 226 len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0); 227 len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0); 228 if (len > sizeof(bindname) - 1) { 229 if (rhs_list) 230 hesiod_free_list(context, rhs_list); 231 errno = EMSGSIZE; 232 return NULL; 233 } 234 /* Put together the rest of the domain. */ 235 strlcat(bindname, ".", sizeof(bindname)); 236 strlcat(bindname, type, sizeof(bindname)); 237 /* Only append lhs if it isn't empty. */ 238 if (ctx->lhs && ctx->lhs[0] != '\0' ) { 239 if (ctx->lhs[0] != '.') 240 strlcat(bindname, ".", sizeof(bindname)); 241 strlcat(bindname, ctx->lhs, sizeof(bindname)); 242 } 243 if (rhs[0] != '.') 244 strlcat(bindname, ".", sizeof(bindname)); 245 strlcat(bindname, rhs, sizeof(bindname)); 246 247 /* rhs_list is no longer needed, since we're done with rhs. */ 248 if (rhs_list) 249 hesiod_free_list(context, rhs_list); 250 251 /* Make a copy of the result and return it to the caller. */ 252 ret = strdup(bindname); 253 if (ret == NULL) 254 errno = ENOMEM; 255 return ret; 256 } 257 258 /* 259 * hesiod_resolve -- 260 * Given a hesiod name and type, return an array of strings returned 261 * by the resolver. 262 */ 263 char ** 264 hesiod_resolve(void *context, const char *name, const char *type) 265 { 266 struct hesiod_p *ctx = (struct hesiod_p *) context; 267 char *bindname, **retvec; 268 269 _DIAGASSERT(context != NULL); 270 _DIAGASSERT(name != NULL); 271 _DIAGASSERT(type != NULL); 272 273 bindname = hesiod_to_bind(context, name, type); 274 if (!bindname) 275 return NULL; 276 277 retvec = get_txt_records(ctx->classes[0], bindname); 278 if (retvec == NULL && errno == ENOENT && ctx->classes[1]) 279 retvec = get_txt_records(ctx->classes[1], bindname); 280 281 free(bindname); 282 return retvec; 283 } 284 285 /*ARGSUSED*/ 286 void 287 hesiod_free_list(void *context, char **list) 288 { 289 char **p; 290 291 _DIAGASSERT(context != NULL); 292 293 if (list == NULL) 294 return; 295 for (p = list; *p; p++) 296 free(*p); 297 free(list); 298 } 299 300 301 /* read_config_file -- 302 * Parse the /etc/hesiod.conf file. Returns 0 on success, 303 * -1 on failure. On failure, it might leave values in ctx->lhs 304 * or ctx->rhs which need to be freed by the caller. 305 */ 306 static int 307 read_config_file(struct hesiod_p *ctx, const char *filename) 308 { 309 char *buf, *key, *data, *p, **which; 310 int n; 311 FILE *fp; 312 313 _DIAGASSERT(ctx != NULL); 314 _DIAGASSERT(filename != NULL); 315 316 /* Set default query classes. */ 317 ctx->classes[0] = C_IN; 318 ctx->classes[1] = C_HS; 319 320 /* Try to open the configuration file. */ 321 fp = fopen(filename, "re"); 322 if (!fp) { 323 /* Use compiled in default domain names. */ 324 ctx->lhs = strdup(DEF_LHS); 325 ctx->rhs = strdup(DEF_RHS); 326 if (ctx->lhs && ctx->rhs) 327 return 0; 328 else { 329 errno = ENOMEM; 330 return -1; 331 } 332 } 333 ctx->lhs = NULL; 334 ctx->rhs = NULL; 335 for (; (buf = fparseln(fp, NULL, NULL, NULL, FPARSELN_UNESCALL)) 336 != NULL; free(buf)) { 337 p = buf; 338 while (*p == ' ' || *p == '\t') 339 p++; 340 key = p; 341 while (*p != ' ' && *p != '\t' && *p != '=' && *p) 342 p++; 343 344 if (*p == '\0') 345 continue; 346 347 *p++ = 0; 348 349 while (isspace((u_char) *p) || *p == '=') 350 p++; 351 352 if (*p == '\0') 353 continue; 354 355 data = p; 356 while (!isspace((u_char) *p) && *p) 357 p++; 358 359 *p = 0; 360 361 if (strcasecmp(key, "lhs") == 0 || 362 strcasecmp(key, "rhs") == 0) { 363 which = (strcasecmp(key, "lhs") == 0) 364 ? &ctx->lhs : &ctx->rhs; 365 *which = strdup(data); 366 if (!*which) { 367 errno = ENOMEM; 368 free(buf); 369 (void)fclose(fp); 370 return -1; 371 } 372 } else { 373 if (strcasecmp(key, "classes") == 0) { 374 n = 0; 375 while (*data && n < 2) { 376 p = data; 377 while (*p && *p != ',') 378 p++; 379 if (*p) 380 *p++ = 0; 381 if (strcasecmp(data, "IN") == 0) 382 ctx->classes[n++] = C_IN; 383 else 384 if (strcasecmp(data, "HS") == 0) 385 ctx->classes[n++] = 386 C_HS; 387 data = p; 388 } 389 while (n < 2) 390 ctx->classes[n++] = 0; 391 } 392 } 393 } 394 fclose(fp); 395 396 if (!ctx->rhs || ctx->classes[0] == 0 || 397 ctx->classes[0] == ctx->classes[1]) { 398 errno = ENOEXEC; 399 return -1; 400 } 401 return 0; 402 } 403 404 /* 405 * get_txt_records -- 406 * Given a DNS class and a DNS name, do a lookup for TXT records, and 407 * return a list of them. 408 */ 409 static char ** 410 get_txt_records(int qclass, const char *name) 411 { 412 HEADER *hp; 413 unsigned char qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor; 414 char *dst, **list; 415 int ancount, qdcount, i, j, n, skip, type, class, len; 416 res_state res = __res_get_state(); 417 418 if (res == NULL) 419 return NULL; 420 421 _DIAGASSERT(name != NULL); 422 423 /* Construct the query. */ 424 n = res_nmkquery(res, QUERY, name, qclass, T_TXT, NULL, 0, 425 NULL, qbuf, PACKETSZ); 426 if (n < 0) { 427 errno = EMSGSIZE; 428 __res_put_state(res); 429 return NULL; 430 } 431 432 /* Send the query. */ 433 n = res_nsend(res, qbuf, n, abuf, MAX_HESRESP); 434 __res_put_state(res); 435 if (n < 0) { 436 errno = ECONNREFUSED; 437 return NULL; 438 } 439 /* Parse the header of the result. */ 440 hp = (HEADER *) (void *) abuf; 441 ancount = ntohs(hp->ancount); 442 qdcount = ntohs(hp->qdcount); 443 p = abuf + sizeof(HEADER); 444 eom = abuf + n; 445 446 /* 447 * Skip questions, trying to get to the answer section 448 * which follows. 449 */ 450 for (i = 0; i < qdcount; i++) { 451 skip = dn_skipname(p, eom); 452 if (skip < 0 || p + skip + QFIXEDSZ > eom) { 453 errno = EMSGSIZE; 454 return NULL; 455 } 456 p += skip + QFIXEDSZ; 457 } 458 459 /* Allocate space for the text record answers. */ 460 list = malloc((ancount + 1) * sizeof(char *)); 461 if (!list) { 462 errno = ENOMEM; 463 return NULL; 464 } 465 /* Parse the answers. */ 466 j = 0; 467 for (i = 0; i < ancount; i++) { 468 /* Parse the header of this answer. */ 469 skip = dn_skipname(p, eom); 470 if (skip < 0 || p + skip + 10 > eom) 471 break; 472 type = p[skip + 0] << 8 | p[skip + 1]; 473 class = p[skip + 2] << 8 | p[skip + 3]; 474 len = p[skip + 8] << 8 | p[skip + 9]; 475 p += skip + 10; 476 if (p + len > eom) { 477 errno = EMSGSIZE; 478 break; 479 } 480 /* Skip entries of the wrong class and type. */ 481 if (class != qclass || type != T_TXT) { 482 p += len; 483 continue; 484 } 485 /* Allocate space for this answer. */ 486 list[j] = malloc((size_t)len); 487 if (!list[j]) { 488 errno = ENOMEM; 489 break; 490 } 491 dst = list[j++]; 492 493 /* Copy answer data into the allocated area. */ 494 eor = p + len; 495 while (p < eor) { 496 n = (unsigned char) *p++; 497 if (p + n > eor) { 498 errno = EMSGSIZE; 499 break; 500 } 501 memcpy(dst, p, (size_t)n); 502 p += n; 503 dst += n; 504 } 505 if (p < eor) { 506 errno = EMSGSIZE; 507 break; 508 } 509 *dst = 0; 510 } 511 512 /* 513 * If we didn't terminate the loop normally, something 514 * went wrong. 515 */ 516 if (i < ancount) { 517 for (i = 0; i < j; i++) 518 free(list[i]); 519 free(list); 520 return NULL; 521 } 522 if (j == 0) { 523 errno = ENOENT; 524 free(list); 525 return NULL; 526 } 527 list[j] = NULL; 528 return list; 529 } 530 531 /* 532 * COMPATIBILITY FUNCTIONS 533 */ 534 535 static int inited = 0; 536 static void *context; 537 static int errval = HES_ER_UNINIT; 538 539 int 540 hes_init(void) 541 { 542 init_context(); 543 return errval; 544 } 545 546 char * 547 hes_to_bind(const char *name, const char *type) 548 { 549 static char *bindname; 550 551 _DIAGASSERT(name != NULL); 552 _DIAGASSERT(type != NULL); 553 554 if (init_context() < 0) 555 return NULL; 556 557 bindname = hesiod_to_bind(context, name, type); 558 if (!bindname) 559 translate_errors(); 560 return bindname; 561 } 562 563 char ** 564 hes_resolve(const char *name, const char *type) 565 { 566 static char **list; 567 568 _DIAGASSERT(name != NULL); 569 _DIAGASSERT(type != NULL); 570 571 if (init_context() < 0) 572 return NULL; 573 574 /* 575 * In the old Hesiod interface, the caller was responsible for 576 * freeing the returned strings but not the vector of strings itself. 577 */ 578 if (list) 579 free(list); 580 581 list = hesiod_resolve(context, name, type); 582 if (!list) 583 translate_errors(); 584 return list; 585 } 586 587 int 588 hes_error(void) 589 { 590 return errval; 591 } 592 593 void 594 hes_free(char **hp) 595 { 596 hesiod_free_list(context, hp); 597 } 598 599 static int 600 init_context(void) 601 { 602 if (!inited) { 603 inited = 1; 604 if (hesiod_init(&context) < 0) { 605 errval = HES_ER_CONFIG; 606 return -1; 607 } 608 errval = HES_ER_OK; 609 } 610 return 0; 611 } 612 613 static void 614 translate_errors(void) 615 { 616 switch (errno) { 617 case ENOENT: 618 errval = HES_ER_NOTFOUND; 619 break; 620 case ECONNREFUSED: 621 case EMSGSIZE: 622 errval = HES_ER_NET; 623 break; 624 default: 625 /* Not a good match, but the best we can do. */ 626 errval = HES_ER_CONFIG; 627 break; 628 } 629 } 630