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