1 /* 2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #if defined(LIBC_SCCS) && !defined(lint) 7 static const char rcsid[] = "$Id: hesiod.c,v 1.23 2002/07/18 02:07:45 marka Exp $"; 8 #endif 9 10 /* 11 * Copyright (c) 1996,1999 by Internet Software Consortium. 12 * 13 * Permission to use, copy, modify, and distribute this software for any 14 * purpose with or without fee is hereby granted, provided that the above 15 * copyright notice and this permission notice appear in all copies. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 18 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 20 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 21 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 22 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 23 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 24 * SOFTWARE. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>. 31 */ 32 33 /* 34 * hesiod.c --- the core portion of the hesiod resolver. 35 * 36 * This file is derived from the hesiod library from Project Athena; 37 * It has been extensively rewritten by Theodore Ts'o to have a more 38 * thread-safe interface. 39 */ 40 41 /* Imports */ 42 43 #include "port_before.h" 44 45 #include <sys/types.h> 46 #include <netinet/in.h> 47 #include <arpa/nameser.h> 48 49 #include <errno.h> 50 #include <netdb.h> 51 #include <resolv.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 56 #include "port_after.h" 57 58 #include "pathnames.h" 59 #include "hesiod.h" 60 #include "hesiod_p.h" 61 62 /* Forward */ 63 64 int hesiod_init(void **context); 65 void hesiod_end(void *context); 66 char * hesiod_to_bind(void *context, const char *name, 67 const char *type); 68 char ** hesiod_resolve(void *context, const char *name, 69 const char *type); 70 void hesiod_free_list(void *context, char **list); 71 72 static int parse_config_file(struct hesiod_p *ctx, const char *filename); 73 static char ** get_txt_records(struct hesiod_p *ctx, int class, 74 const char *name); 75 static int init(struct hesiod_p *ctx); 76 77 /* Public */ 78 79 /* 80 * This function is called to initialize a hesiod_p. 81 */ 82 int 83 hesiod_init(void **context) { 84 struct hesiod_p *ctx; 85 char *cp; 86 87 ctx = malloc(sizeof(struct hesiod_p)); 88 if (ctx == 0) { 89 errno = ENOMEM; 90 return (-1); 91 } 92 93 #ifdef ORIGINAL_ISC_CODE 94 ctx->LHS = NULL; 95 ctx->RHS = NULL; 96 ctx->res = NULL; 97 #else 98 memset(ctx, 0, sizeof (*ctx)); 99 #endif /* ORIGINAL_ISC_CODE */ 100 101 if (parse_config_file(ctx, _PATH_HESIOD_CONF) < 0) { 102 #ifdef DEF_RHS 103 /* 104 * Use compiled in defaults. 105 */ 106 ctx->LHS = malloc(strlen(DEF_LHS)+1); 107 ctx->RHS = malloc(strlen(DEF_RHS)+1); 108 if (ctx->LHS == 0 || ctx->RHS == 0) { 109 errno = ENOMEM; 110 goto cleanup; 111 } 112 #ifdef HAVE_STRLCPY 113 strlcpy(ctx->LHS, DEF_LHS, strlen(DEF_LHS) + 1); 114 strlcpy(ctx->RHS, DEF_RHS, strlen(DEF_RHS) + 1); 115 #else 116 strcpy(ctx->LHS, DEF_LHS); 117 strcpy(ctx->RHS, DEF_RHS); 118 #endif 119 #else 120 goto cleanup; 121 #endif 122 } 123 /* 124 * The default RHS can be overridden by an environment 125 * variable. 126 */ 127 if ((cp = getenv("HES_DOMAIN")) != NULL) { 128 size_t RHSlen = strlen(cp) + 2; 129 if (ctx->RHS) 130 free(ctx->RHS); 131 ctx->RHS = malloc(RHSlen); 132 if (!ctx->RHS) { 133 errno = ENOMEM; 134 goto cleanup; 135 } 136 if (cp[0] == '.') { 137 #ifdef HAVE_STRLCPY 138 strlcpy(ctx->RHS, cp, RHSlen); 139 #else 140 strcpy(ctx->RHS, cp); 141 #endif 142 } else { 143 #ifdef HAVE_STRLCPY 144 strlcpy(ctx->RHS, ".", RHSlen); 145 #else 146 strcpy(ctx->RHS, "."); 147 #endif 148 #ifdef HAVE_STRLCAT 149 strlcat(ctx->RHS, cp, RHSlen); 150 #else 151 strcat(ctx->RHS, cp); 152 #endif 153 } 154 } 155 156 /* 157 * If there is no default hesiod realm set, we return an 158 * error. 159 */ 160 if (!ctx->RHS) { 161 errno = ENOEXEC; 162 goto cleanup; 163 } 164 165 #if 0 166 if (res_ninit(ctx->res) < 0) 167 goto cleanup; 168 #endif 169 170 *context = ctx; 171 return (0); 172 173 cleanup: 174 hesiod_end(ctx); 175 return (-1); 176 } 177 178 /* 179 * This function deallocates the hesiod_p 180 */ 181 void 182 hesiod_end(void *context) { 183 struct hesiod_p *ctx = (struct hesiod_p *) context; 184 int save_errno = errno; 185 186 if (ctx->res) 187 res_nclose(ctx->res); 188 if (ctx->RHS) 189 free(ctx->RHS); 190 if (ctx->LHS) 191 free(ctx->LHS); 192 if (ctx->res && ctx->free_res) 193 (*ctx->free_res)(ctx->res); 194 free(ctx); 195 errno = save_errno; 196 } 197 198 /* 199 * This function takes a hesiod (name, type) and returns a DNS 200 * name which is to be resolved. 201 */ 202 char * 203 hesiod_to_bind(void *context, const char *name, const char *type) { 204 struct hesiod_p *ctx = (struct hesiod_p *) context; 205 char *bindname; 206 char **rhs_list = NULL; 207 const char *RHS, *cp; 208 209 /* Decide what our RHS is, and set cp to the end of the actual name. */ 210 if ((cp = strchr(name, '@')) != NULL) { 211 if (strchr(cp + 1, '.')) 212 RHS = cp + 1; 213 else if ((rhs_list = hesiod_resolve(context, cp + 1, 214 "rhs-extension")) != NULL) 215 RHS = *rhs_list; 216 else { 217 errno = ENOENT; 218 return (NULL); 219 } 220 } else { 221 RHS = ctx->RHS; 222 cp = name + strlen(name); 223 } 224 225 /* 226 * Allocate the space we need, including up to three periods and 227 * the terminating NUL. 228 */ 229 if ((bindname = malloc((cp - name) + strlen(type) + strlen(RHS) + 230 (ctx->LHS ? strlen(ctx->LHS) : 0) + 4)) == NULL) { 231 errno = ENOMEM; 232 if (rhs_list) 233 hesiod_free_list(context, rhs_list); 234 return NULL; 235 } 236 237 /* Now put together the DNS name. */ 238 memcpy(bindname, name, cp - name); 239 bindname[cp - name] = '\0'; 240 strcat(bindname, "."); 241 strcat(bindname, type); 242 if (ctx->LHS) { 243 if (ctx->LHS[0] != '.') 244 strcat(bindname, "."); 245 strcat(bindname, ctx->LHS); 246 } 247 if (RHS[0] != '.') 248 strcat(bindname, "."); 249 strcat(bindname, RHS); 250 251 if (rhs_list) 252 hesiod_free_list(context, rhs_list); 253 254 return (bindname); 255 } 256 257 /* 258 * This is the core function. Given a hesiod (name, type), it 259 * returns an array of strings returned by the resolver. 260 */ 261 char ** 262 hesiod_resolve(void *context, const char *name, const char *type) { 263 struct hesiod_p *ctx = (struct hesiod_p *) context; 264 char *bindname = hesiod_to_bind(context, name, type); 265 char **retvec; 266 267 if (bindname == NULL) 268 return (NULL); 269 if (init(ctx) == -1) { 270 free(bindname); 271 return (NULL); 272 } 273 274 if ((retvec = get_txt_records(ctx, C_IN, bindname))) { 275 free(bindname); 276 return (retvec); 277 } 278 279 if (errno != ENOENT) 280 return (NULL); 281 282 retvec = get_txt_records(ctx, C_HS, bindname); 283 free(bindname); 284 return (retvec); 285 } 286 287 void 288 hesiod_free_list(void *context, char **list) { 289 char **p; 290 291 UNUSED(context); 292 293 for (p = list; *p; p++) 294 free(*p); 295 free(list); 296 } 297 298 /* 299 * This function parses the /etc/hesiod.conf file 300 */ 301 static int 302 parse_config_file(struct hesiod_p *ctx, const char *filename) { 303 char *key, *data, *cp, **cpp; 304 char buf[MAXDNAME+7]; 305 FILE *fp; 306 307 /* 308 * Clear the existing configuration variable, just in case 309 * they're set. 310 */ 311 if (ctx->RHS) 312 free(ctx->RHS); 313 if (ctx->LHS) 314 free(ctx->LHS); 315 ctx->RHS = ctx->LHS = 0; 316 317 /* 318 * Now open and parse the file... 319 */ 320 if (!(fp = fopen(filename, "r"))) 321 return (-1); 322 323 while (fgets(buf, sizeof(buf), fp) != NULL) { 324 cp = buf; 325 if (*cp == '#' || *cp == '\n' || *cp == '\r') 326 continue; 327 while(*cp == ' ' || *cp == '\t') 328 cp++; 329 key = cp; 330 while(*cp != ' ' && *cp != '\t' && *cp != '=') 331 cp++; 332 *cp++ = '\0'; 333 334 while(*cp == ' ' || *cp == '\t' || *cp == '=') 335 cp++; 336 data = cp; 337 while(*cp != ' ' && *cp != '\n' && *cp != '\r') 338 cp++; 339 *cp++ = '\0'; 340 341 if (strcmp(key, "lhs") == 0) 342 cpp = &ctx->LHS; 343 else if (strcmp(key, "rhs") == 0) 344 cpp = &ctx->RHS; 345 else 346 continue; 347 348 *cpp = malloc(strlen(data) + 1); 349 if (!*cpp) { 350 errno = ENOMEM; 351 goto cleanup; 352 } 353 strcpy(*cpp, data); 354 } 355 fclose(fp); 356 return (0); 357 358 cleanup: 359 fclose(fp); 360 if (ctx->RHS) 361 free(ctx->RHS); 362 if (ctx->LHS) 363 free(ctx->LHS); 364 ctx->RHS = ctx->LHS = 0; 365 return (-1); 366 } 367 368 /* 369 * Given a DNS class and a DNS name, do a lookup for TXT records, and 370 * return a list of them. 371 */ 372 static char ** 373 get_txt_records(struct hesiod_p *ctx, int class, const char *name) { 374 struct { 375 int type; /* RR type */ 376 int class; /* RR class */ 377 int dlen; /* len of data section */ 378 u_char *data; /* pointer to data */ 379 } rr; 380 HEADER *hp; 381 u_char qbuf[MAX_HESRESP], abuf[MAX_HESRESP]; 382 u_char *cp, *erdata, *eom; 383 char *dst, *edst, **list; 384 int ancount, qdcount; 385 int i, j, n, skip; 386 387 /* 388 * Construct the query and send it. 389 */ 390 n = res_nmkquery(ctx->res, QUERY, name, class, T_TXT, NULL, 0, 391 NULL, qbuf, MAX_HESRESP); 392 if (n < 0) { 393 errno = EMSGSIZE; 394 return (NULL); 395 } 396 n = res_nsend(ctx->res, qbuf, n, abuf, MAX_HESRESP); 397 if (n < 0) { 398 errno = ECONNREFUSED; 399 return (NULL); 400 } 401 if (n < HFIXEDSZ) { 402 errno = EMSGSIZE; 403 return (NULL); 404 } 405 406 /* 407 * OK, parse the result. 408 */ 409 hp = (HEADER *) abuf; 410 ancount = ntohs(hp->ancount); 411 qdcount = ntohs(hp->qdcount); 412 cp = abuf + sizeof(HEADER); 413 eom = abuf + n; 414 415 /* Skip query, trying to get to the answer section which follows. */ 416 for (i = 0; i < qdcount; i++) { 417 skip = dn_skipname(cp, eom); 418 if (skip < 0 || cp + skip + QFIXEDSZ > eom) { 419 errno = EMSGSIZE; 420 return (NULL); 421 } 422 cp += skip + QFIXEDSZ; 423 } 424 425 list = malloc((ancount + 1) * sizeof(char *)); 426 if (!list) { 427 errno = ENOMEM; 428 return (NULL); 429 } 430 j = 0; 431 for (i = 0; i < ancount; i++) { 432 skip = dn_skipname(cp, eom); 433 if (skip < 0) { 434 errno = EMSGSIZE; 435 goto cleanup; 436 } 437 cp += skip; 438 if (cp + 3 * INT16SZ + INT32SZ > eom) { 439 errno = EMSGSIZE; 440 goto cleanup; 441 } 442 rr.type = ns_get16(cp); 443 cp += INT16SZ; 444 rr.class = ns_get16(cp); 445 cp += INT16SZ + INT32SZ; /* skip the ttl, too */ 446 rr.dlen = ns_get16(cp); 447 cp += INT16SZ; 448 if (cp + rr.dlen > eom) { 449 errno = EMSGSIZE; 450 goto cleanup; 451 } 452 rr.data = cp; 453 cp += rr.dlen; 454 if (rr.class != class || rr.type != T_TXT) 455 continue; 456 if (!(list[j] = malloc(rr.dlen))) 457 goto cleanup; 458 dst = list[j++]; 459 edst = dst + rr.dlen; 460 erdata = rr.data + rr.dlen; 461 cp = rr.data; 462 while (cp < erdata) { 463 n = (unsigned char) *cp++; 464 if (cp + n > eom || dst + n > edst) { 465 errno = EMSGSIZE; 466 goto cleanup; 467 } 468 memcpy(dst, cp, n); 469 cp += n; 470 dst += n; 471 } 472 if (cp != erdata) { 473 errno = EMSGSIZE; 474 goto cleanup; 475 } 476 *dst = '\0'; 477 } 478 list[j] = NULL; 479 if (j == 0) { 480 errno = ENOENT; 481 goto cleanup; 482 } 483 return (list); 484 485 cleanup: 486 for (i = 0; i < j; i++) 487 free(list[i]); 488 free(list); 489 return (NULL); 490 } 491 492 struct __res_state * 493 __hesiod_res_get(void *context) { 494 struct hesiod_p *ctx = context; 495 496 if (!ctx->res) { 497 struct __res_state *res; 498 res = (struct __res_state *)malloc(sizeof *res); 499 if (res == NULL) { 500 errno = ENOMEM; 501 return (NULL); 502 } 503 memset(res, 0, sizeof *res); 504 __hesiod_res_set(ctx, res, free); 505 } 506 507 return (ctx->res); 508 } 509 510 void 511 __hesiod_res_set(void *context, struct __res_state *res, 512 void (*free_res)(void *)) { 513 struct hesiod_p *ctx = context; 514 515 if (ctx->res && ctx->free_res) { 516 res_nclose(ctx->res); 517 (*ctx->free_res)(ctx->res); 518 } 519 520 ctx->res = res; 521 ctx->free_res = free_res; 522 } 523 524 static int 525 init(struct hesiod_p *ctx) { 526 527 if (!ctx->res && !__hesiod_res_get(ctx)) 528 return (-1); 529 530 if (((ctx->res->options & RES_INIT) == 0) && 531 (res_ninit(ctx->res) == -1)) 532 return (-1); 533 534 return (0); 535 } 536