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