1 /* Copyright (c) 1996 by Internet Software Consortium. 2 * 3 * Permission to use, copy, modify, and distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 8 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 9 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 10 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 11 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 12 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 13 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 14 * SOFTWARE. 15 */ 16 17 /* Copyright 1996 by the Massachusetts Institute of Technology. 18 * 19 * Permission to use, copy, modify, and distribute this 20 * software and its documentation for any purpose and without 21 * fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright 23 * notice and this permission notice appear in supporting 24 * documentation, and that the name of M.I.T. not be used in 25 * advertising or publicity pertaining to distribution of the 26 * software without specific, written prior permission. 27 * M.I.T. makes no representations about the suitability of 28 * this software for any purpose. It is provided "as is" 29 * without express or implied warranty. 30 */ 31 32 /* This file is part of the hesiod library. It implements the core 33 * portion of the hesiod resolver. 34 * 35 * This file is loosely based on an interim version of hesiod.c from 36 * the BIND IRS library, which was in turn based on an earlier version 37 * of this file. Extensive changes have been made on each step of the 38 * path. 39 * 40 * This implementation is not truly thread-safe at the moment because 41 * it uses res_send() and accesses _res. 42 * 43 * $NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $ 44 * $FreeBSD: src/lib/libc/net/hesiod.c,v 1.9 2003/05/01 19:03:14 nectar Exp $ 45 */ 46 47 #include <sys/types.h> 48 #include <sys/param.h> 49 #include <netinet/in.h> 50 #include <arpa/nameser.h> 51 52 #include <ctype.h> 53 #include <errno.h> 54 #include <hesiod.h> 55 #include <resolv.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 61 struct hesiod_p { 62 char *lhs; /* normally ".ns" */ 63 char *rhs; /* AKA the default hesiod domain */ 64 int classes[2]; /* The class search order. */ 65 }; 66 67 #define MAX_HESRESP 1024 68 69 static int read_config_file(struct hesiod_p *, const char *); 70 static char **get_txt_records(int, const char *); 71 static int init_context(void); 72 static void translate_errors(void); 73 74 75 /* 76 * hesiod_init -- 77 * initialize a hesiod_p. 78 */ 79 int 80 hesiod_init(void **context) 81 { 82 struct hesiod_p *ctx; 83 const char *p, *configname; 84 85 ctx = malloc(sizeof(struct hesiod_p)); 86 if (ctx) { 87 *context = ctx; 88 if (!issetugid()) 89 configname = getenv("HESIOD_CONFIG"); 90 else 91 configname = NULL; 92 if (!configname) 93 configname = _PATH_HESIOD_CONF; 94 if (read_config_file(ctx, configname) >= 0) { 95 /* 96 * The default rhs can be overridden by an 97 * environment variable. 98 */ 99 if (!issetugid()) 100 p = getenv("HES_DOMAIN"); 101 else 102 p = NULL; 103 if (p) { 104 if (ctx->rhs) 105 free(ctx->rhs); 106 ctx->rhs = malloc(strlen(p) + 2); 107 if (ctx->rhs) { 108 *ctx->rhs = '.'; 109 strcpy(ctx->rhs + 1, 110 (*p == '.') ? p + 1 : p); 111 return 0; 112 } else 113 errno = ENOMEM; 114 } else 115 return 0; 116 } 117 } else 118 errno = ENOMEM; 119 120 if (ctx->lhs) 121 free(ctx->lhs); 122 if (ctx->rhs) 123 free(ctx->rhs); 124 if (ctx) 125 free(ctx); 126 return -1; 127 } 128 129 /* 130 * hesiod_end -- 131 * Deallocates the hesiod_p. 132 */ 133 void 134 hesiod_end(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(void *context, const char *name, const char *type) 225 { 226 struct hesiod_p *ctx = (struct hesiod_p *) context; 227 char *bindname, **retvec; 228 229 bindname = hesiod_to_bind(context, name, type); 230 if (!bindname) 231 return NULL; 232 233 retvec = get_txt_records(ctx->classes[0], bindname); 234 if (retvec == NULL && errno == ENOENT && ctx->classes[1]) 235 retvec = get_txt_records(ctx->classes[1], bindname); 236 237 free(bindname); 238 return retvec; 239 } 240 241 /*ARGSUSED*/ 242 void 243 hesiod_free_list(void *context, char **list) 244 { 245 char **p; 246 247 if (list == NULL) 248 return; 249 for (p = list; *p; p++) 250 free(*p); 251 free(list); 252 } 253 254 255 /* read_config_file -- 256 * Parse the /etc/hesiod.conf file. Returns 0 on success, 257 * -1 on failure. On failure, it might leave values in ctx->lhs 258 * or ctx->rhs which need to be freed by the caller. 259 */ 260 static int 261 read_config_file(struct hesiod_p *ctx, const char *filename) 262 { 263 char *key, *data, *p, **which; 264 char buf[MAXDNAME + 7]; 265 int n; 266 FILE *fp; 267 268 /* Set default query classes. */ 269 ctx->classes[0] = C_IN; 270 ctx->classes[1] = C_HS; 271 272 /* Try to open the configuration file. */ 273 fp = fopen(filename, "r"); 274 if (!fp) { 275 /* Use compiled in default domain names. */ 276 ctx->lhs = strdup(DEF_LHS); 277 ctx->rhs = strdup(DEF_RHS); 278 if (ctx->lhs && ctx->rhs) 279 return 0; 280 else { 281 errno = ENOMEM; 282 return -1; 283 } 284 } 285 ctx->lhs = NULL; 286 ctx->rhs = NULL; 287 while (fgets(buf, sizeof(buf), fp) != NULL) { 288 p = buf; 289 if (*p == '#' || *p == '\n' || *p == '\r') 290 continue; 291 while (*p == ' ' || *p == '\t') 292 p++; 293 key = p; 294 while (*p != ' ' && *p != '\t' && *p != '=') 295 p++; 296 *p++ = 0; 297 298 while (isspace(*p) || *p == '=') 299 p++; 300 data = p; 301 while (!isspace(*p)) 302 p++; 303 *p = 0; 304 305 if (strcasecmp(key, "lhs") == 0 || 306 strcasecmp(key, "rhs") == 0) { 307 which = (strcasecmp(key, "lhs") == 0) 308 ? &ctx->lhs : &ctx->rhs; 309 *which = strdup(data); 310 if (!*which) { 311 errno = ENOMEM; 312 return -1; 313 } 314 } else { 315 if (strcasecmp(key, "classes") == 0) { 316 n = 0; 317 while (*data && n < 2) { 318 p = data; 319 while (*p && *p != ',') 320 p++; 321 if (*p) 322 *p++ = 0; 323 if (strcasecmp(data, "IN") == 0) 324 ctx->classes[n++] = C_IN; 325 else 326 if (strcasecmp(data, "HS") == 0) 327 ctx->classes[n++] = 328 C_HS; 329 data = p; 330 } 331 while (n < 2) 332 ctx->classes[n++] = 0; 333 } 334 } 335 } 336 fclose(fp); 337 338 if (!ctx->rhs || ctx->classes[0] == 0 || 339 ctx->classes[0] == ctx->classes[1]) { 340 errno = ENOEXEC; 341 return -1; 342 } 343 return 0; 344 } 345 346 /* 347 * get_txt_records -- 348 * Given a DNS class and a DNS name, do a lookup for TXT records, and 349 * return a list of them. 350 */ 351 static char ** 352 get_txt_records(int qclass, const char *name) 353 { 354 HEADER *hp; 355 unsigned char qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor; 356 char *dst, **list; 357 int ancount, qdcount, i, j, n, skip, type, class, len; 358 359 /* Make sure the resolver is initialized. */ 360 if ((_res.options & RES_INIT) == 0 && res_init() == -1) 361 return NULL; 362 363 /* Construct the query. */ 364 n = res_mkquery(QUERY, name, qclass, T_TXT, NULL, 0, 365 NULL, qbuf, PACKETSZ); 366 if (n < 0) 367 return NULL; 368 369 /* Send the query. */ 370 n = res_send(qbuf, n, abuf, MAX_HESRESP); 371 if (n < 0 || n > MAX_HESRESP) { 372 errno = ECONNREFUSED; /* XXX */ 373 return NULL; 374 } 375 /* Parse the header of the result. */ 376 hp = (HEADER *) (void *) abuf; 377 ancount = ntohs(hp->ancount); 378 qdcount = ntohs(hp->qdcount); 379 p = abuf + sizeof(HEADER); 380 eom = abuf + n; 381 382 /* 383 * Skip questions, trying to get to the answer section 384 * which follows. 385 */ 386 for (i = 0; i < qdcount; i++) { 387 skip = dn_skipname(p, eom); 388 if (skip < 0 || p + skip + QFIXEDSZ > eom) { 389 errno = EMSGSIZE; 390 return NULL; 391 } 392 p += skip + QFIXEDSZ; 393 } 394 395 /* Allocate space for the text record answers. */ 396 list = malloc((ancount + 1) * sizeof(char *)); 397 if (!list) { 398 errno = ENOMEM; 399 return NULL; 400 } 401 /* Parse the answers. */ 402 j = 0; 403 for (i = 0; i < ancount; i++) { 404 /* Parse the header of this answer. */ 405 skip = dn_skipname(p, eom); 406 if (skip < 0 || p + skip + 10 > eom) 407 break; 408 type = p[skip + 0] << 8 | p[skip + 1]; 409 class = p[skip + 2] << 8 | p[skip + 3]; 410 len = p[skip + 8] << 8 | p[skip + 9]; 411 p += skip + 10; 412 if (p + len > eom) { 413 errno = EMSGSIZE; 414 break; 415 } 416 /* Skip entries of the wrong class and type. */ 417 if (class != qclass || type != T_TXT) { 418 p += len; 419 continue; 420 } 421 /* Allocate space for this answer. */ 422 list[j] = malloc((size_t)len); 423 if (!list[j]) { 424 errno = ENOMEM; 425 break; 426 } 427 dst = list[j++]; 428 429 /* Copy answer data into the allocated area. */ 430 eor = p + len; 431 while (p < eor) { 432 n = (unsigned char) *p++; 433 if (p + n > eor) { 434 errno = EMSGSIZE; 435 break; 436 } 437 memcpy(dst, p, (size_t)n); 438 p += n; 439 dst += n; 440 } 441 if (p < eor) { 442 errno = EMSGSIZE; 443 break; 444 } 445 *dst = 0; 446 } 447 448 /* 449 * If we didn't terminate the loop normally, something 450 * went wrong. 451 */ 452 if (i < ancount) { 453 for (i = 0; i < j; i++) 454 free(list[i]); 455 free(list); 456 return NULL; 457 } 458 if (j == 0) { 459 errno = ENOENT; 460 free(list); 461 return NULL; 462 } 463 list[j] = NULL; 464 return list; 465 } 466 467 /* 468 * COMPATIBILITY FUNCTIONS 469 */ 470 471 static int inited = 0; 472 static void *context; 473 static int errval = HES_ER_UNINIT; 474 475 int 476 hes_init(void) 477 { 478 init_context(); 479 return errval; 480 } 481 482 char * 483 hes_to_bind(const char *name, const char *type) 484 { 485 static char *bindname; 486 if (init_context() < 0) 487 return NULL; 488 if (bindname) 489 free(bindname); 490 bindname = hesiod_to_bind(context, name, type); 491 if (!bindname) 492 translate_errors(); 493 return bindname; 494 } 495 496 char ** 497 hes_resolve(const char *name, const char *type) 498 { 499 static char **list; 500 501 if (init_context() < 0) 502 return NULL; 503 504 /* 505 * In the old Hesiod interface, the caller was responsible for 506 * freeing the returned strings but not the vector of strings itself. 507 */ 508 if (list) 509 free(list); 510 511 list = hesiod_resolve(context, name, type); 512 if (!list) 513 translate_errors(); 514 return list; 515 } 516 517 int 518 hes_error(void) 519 { 520 return errval; 521 } 522 523 void 524 hes_free(char **hp) 525 { 526 hesiod_free_list(context, hp); 527 } 528 529 static int 530 init_context(void) 531 { 532 if (!inited) { 533 inited = 1; 534 if (hesiod_init(&context) < 0) { 535 errval = HES_ER_CONFIG; 536 return -1; 537 } 538 errval = HES_ER_OK; 539 } 540 return 0; 541 } 542 543 static void 544 translate_errors(void) 545 { 546 switch (errno) { 547 case ENOENT: 548 errval = HES_ER_NOTFOUND; 549 break; 550 case ECONNREFUSED: 551 case EMSGSIZE: 552 errval = HES_ER_NET; 553 break; 554 case ENOMEM: 555 default: 556 /* Not a good match, but the best we can do. */ 557 errval = HES_ER_CONFIG; 558 break; 559 } 560 } 561