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