|  | #if defined(LIBC_SCCS) && !defined(lint) | 
|  | static const char rcsid[] = "$BINDId: hesiod.c,v 1.21 2000/02/28 14:51:08 vixie Exp $"; | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Copyright (c) 1996,1999 by Internet Software Consortium. | 
|  | * | 
|  | * Permission to use, copy, modify, and distribute this software for any | 
|  | * purpose with or without fee is hereby granted, provided that the above | 
|  | * copyright notice and this permission notice appear in all copies. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS | 
|  | * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES | 
|  | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE | 
|  | * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | 
|  | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR | 
|  | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | 
|  | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | 
|  | * SOFTWARE. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * hesiod.c --- the core portion of the hesiod resolver. | 
|  | * | 
|  | * This file is derived from the hesiod library from Project Athena; | 
|  | * It has been extensively rewritten by Theodore Ts'o to have a more | 
|  | * thread-safe interface. | 
|  | */ | 
|  |  | 
|  | /* Imports */ | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <netinet/in.h> | 
|  | #include <arpa/nameser.h> | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <netdb.h> | 
|  | #include <resolv.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "hesiod.h" | 
|  | #include "hesiod_p.h" | 
|  | #undef DEF_RHS | 
|  |  | 
|  | #define _PATH_HESIOD_CONF "/etc/hesiod.conf" | 
|  |  | 
|  | /* Forward */ | 
|  |  | 
|  | int		hesiod_init(void **context); | 
|  | void		hesiod_end(void *context); | 
|  | char *		hesiod_to_bind(void *context, const char *name, | 
|  | const char *type); | 
|  | char **		hesiod_resolve(void *context, const char *name, | 
|  | const char *type); | 
|  | void		hesiod_free_list(void *context, char **list); | 
|  |  | 
|  | static int	parse_config_file(struct hesiod_p *ctx, const char *filename); | 
|  | static char **	get_txt_records(struct hesiod_p *ctx, int class, | 
|  | const char *name); | 
|  | static int	init(struct hesiod_p *ctx); | 
|  |  | 
|  | /* Public */ | 
|  |  | 
|  | /* | 
|  | * This function is called to initialize a hesiod_p. | 
|  | */ | 
|  | int | 
|  | hesiod_init(void **context) { | 
|  | struct hesiod_p *ctx; | 
|  | const char *configname; | 
|  | char *cp; | 
|  |  | 
|  | ctx = malloc(sizeof(struct hesiod_p)); | 
|  | if (ctx == 0) | 
|  | return (-1); | 
|  |  | 
|  | ctx->LHS = NULL; | 
|  | ctx->RHS = NULL; | 
|  | ctx->res = NULL; | 
|  | /* Set default query classes. */ | 
|  | ctx->classes[0] = C_IN; | 
|  | ctx->classes[1] = C_HS; | 
|  |  | 
|  | configname = __libc_secure_getenv("HESIOD_CONFIG"); | 
|  | if (!configname) | 
|  | configname = _PATH_HESIOD_CONF; | 
|  | if (parse_config_file(ctx, configname) < 0) { | 
|  | #ifdef DEF_RHS | 
|  | /* | 
|  | * Use compiled in defaults. | 
|  | */ | 
|  | ctx->LHS = malloc(strlen(DEF_LHS)+1); | 
|  | ctx->RHS = malloc(strlen(DEF_RHS)+1); | 
|  | if (ctx->LHS == 0 || ctx->RHS == 0) | 
|  | goto cleanup; | 
|  | strcpy(ctx->LHS, DEF_LHS); | 
|  | strcpy(ctx->RHS, DEF_RHS); | 
|  | #else | 
|  | goto cleanup; | 
|  | #endif | 
|  | } | 
|  | /* | 
|  | * The default RHS can be overridden by an environment | 
|  | * variable. | 
|  | */ | 
|  | if ((cp = __libc_secure_getenv("HES_DOMAIN")) != NULL) { | 
|  | free(ctx->RHS); | 
|  | ctx->RHS = malloc(strlen(cp)+2); | 
|  | if (!ctx->RHS) | 
|  | goto cleanup; | 
|  | if (cp[0] == '.') | 
|  | strcpy(ctx->RHS, cp); | 
|  | else { | 
|  | ctx->RHS[0] = '.'; | 
|  | strcpy(ctx->RHS + 1, cp); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If there is no default hesiod realm set, we return an | 
|  | * error. | 
|  | */ | 
|  | if (!ctx->RHS) { | 
|  | __set_errno(ENOEXEC); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | if (res_ninit(ctx->res) < 0) | 
|  | goto cleanup; | 
|  | #endif | 
|  |  | 
|  | *context = ctx; | 
|  | return (0); | 
|  |  | 
|  | cleanup: | 
|  | hesiod_end(ctx); | 
|  | return (-1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This function deallocates the hesiod_p | 
|  | */ | 
|  | void | 
|  | hesiod_end(void *context) { | 
|  | struct hesiod_p *ctx = (struct hesiod_p *) context; | 
|  | int save_errno = errno; | 
|  |  | 
|  | if (ctx->res) | 
|  | res_nclose(ctx->res); | 
|  | free(ctx->RHS); | 
|  | free(ctx->LHS); | 
|  | if (ctx->res && ctx->free_res) | 
|  | (*ctx->free_res)(ctx->res); | 
|  | free(ctx); | 
|  | __set_errno(save_errno); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This function takes a hesiod (name, type) and returns a DNS | 
|  | * name which is to be resolved. | 
|  | */ | 
|  | char * | 
|  | hesiod_to_bind(void *context, const char *name, const char *type) { | 
|  | struct hesiod_p *ctx = (struct hesiod_p *) context; | 
|  | char *bindname; | 
|  | char **rhs_list = NULL; | 
|  | const char *RHS, *cp; | 
|  | char *endp; | 
|  |  | 
|  | /* Decide what our RHS is, and set cp to the end of the actual name. */ | 
|  | if ((cp = strchr(name, '@')) != NULL) { | 
|  | if (strchr(cp + 1, '.')) | 
|  | RHS = cp + 1; | 
|  | else if ((rhs_list = hesiod_resolve(context, cp + 1, | 
|  | "rhs-extension")) != NULL) | 
|  | RHS = *rhs_list; | 
|  | else { | 
|  | __set_errno(ENOENT); | 
|  | return (NULL); | 
|  | } | 
|  | } else { | 
|  | RHS = ctx->RHS; | 
|  | cp = name + strlen(name); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Allocate the space we need, including up to three periods and | 
|  | * the terminating NUL. | 
|  | */ | 
|  | if ((bindname = malloc((cp - name) + strlen(type) + strlen(RHS) + | 
|  | (ctx->LHS ? strlen(ctx->LHS) : 0) + 4)) == NULL) { | 
|  | if (rhs_list) | 
|  | hesiod_free_list(context, rhs_list); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Now put together the DNS name. */ | 
|  | endp = (char *) __mempcpy (bindname, name, cp - name); | 
|  | *endp++ = '.'; | 
|  | endp = (char *) __stpcpy (endp, type); | 
|  | if (ctx->LHS) { | 
|  | if (ctx->LHS[0] != '.') | 
|  | *endp++ = '.'; | 
|  | endp = __stpcpy (endp, ctx->LHS); | 
|  | } | 
|  | if (RHS[0] != '.') | 
|  | *endp++ = '.'; | 
|  | strcpy (endp, RHS); | 
|  |  | 
|  | if (rhs_list) | 
|  | hesiod_free_list(context, rhs_list); | 
|  |  | 
|  | return (bindname); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This is the core function.  Given a hesiod (name, type), it | 
|  | * returns an array of strings returned by the resolver. | 
|  | */ | 
|  | char ** | 
|  | hesiod_resolve(void *context, const char *name, const char *type) { | 
|  | struct hesiod_p *ctx = (struct hesiod_p *) context; | 
|  | char *bindname = hesiod_to_bind(context, name, type); | 
|  | char **retvec; | 
|  |  | 
|  | if (bindname == NULL) | 
|  | return (NULL); | 
|  | if (init(ctx) == -1) { | 
|  | free(bindname); | 
|  | return (NULL); | 
|  | } | 
|  |  | 
|  | retvec = get_txt_records(ctx, ctx->classes[0], bindname); | 
|  |  | 
|  | if (retvec == NULL && (errno == ENOENT || errno == ECONNREFUSED) && ctx->classes[1]) | 
|  | retvec = get_txt_records(ctx, ctx->classes[1], bindname); | 
|  |  | 
|  |  | 
|  | free(bindname); | 
|  | return (retvec); | 
|  | } | 
|  |  | 
|  | void | 
|  | hesiod_free_list(void *context, char **list) { | 
|  | char **p; | 
|  |  | 
|  | for (p = list; *p; p++) | 
|  | free(*p); | 
|  | free(list); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This function parses the /etc/hesiod.conf file | 
|  | */ | 
|  | static int | 
|  | parse_config_file(struct hesiod_p *ctx, const char *filename) { | 
|  | char buf[MAXDNAME+7]; | 
|  | FILE *fp; | 
|  |  | 
|  | /* | 
|  | * Clear the existing configuration variable, just in case | 
|  | * they're set. | 
|  | */ | 
|  | free(ctx->RHS); | 
|  | free(ctx->LHS); | 
|  | ctx->RHS = ctx->LHS = 0; | 
|  | /* Set default query classes. */ | 
|  | ctx->classes[0] = C_IN; | 
|  | ctx->classes[1] = C_HS; | 
|  |  | 
|  | /* | 
|  | * Now open and parse the file... | 
|  | */ | 
|  | if (!(fp = fopen(filename, "rce"))) | 
|  | return (-1); | 
|  |  | 
|  | while (fgets(buf, sizeof(buf), fp) != NULL) { | 
|  | char *key, *data, *cp, **cpp; | 
|  |  | 
|  | cp = buf; | 
|  | if (*cp == '#' || *cp == '\n' || *cp == '\r') | 
|  | continue; | 
|  | while(*cp == ' ' || *cp == '\t') | 
|  | cp++; | 
|  | key = cp; | 
|  | while(*cp != ' ' && *cp != '\t' && *cp != '=') | 
|  | cp++; | 
|  | *cp++ = '\0'; | 
|  |  | 
|  | while(*cp == ' ' || *cp == '\t' || *cp == '=') | 
|  | cp++; | 
|  | data = cp; | 
|  | while(*cp != ' ' && *cp != '\n' && *cp != '\r') | 
|  | cp++; | 
|  | *cp++ = '\0'; | 
|  |  | 
|  | cpp = NULL; | 
|  | if (strcasecmp(key, "lhs") == 0) | 
|  | cpp = &ctx->LHS; | 
|  | else if (strcasecmp(key, "rhs") == 0) | 
|  | cpp = &ctx->RHS; | 
|  | if (cpp) { | 
|  | *cpp = strdup(data); | 
|  | if (!*cpp) | 
|  | goto cleanup; | 
|  | } else if (strcasecmp(key, "classes") == 0) { | 
|  | int n = 0; | 
|  | while (*data && n < 2) { | 
|  | cp = strchrnul(data, ','); | 
|  | if (*cp != '\0') | 
|  | *cp++ = '\0'; | 
|  | if (strcasecmp(data, "IN") == 0) | 
|  | ctx->classes[n++] = C_IN; | 
|  | else if (strcasecmp(data, "HS") == 0) | 
|  | ctx->classes[n++] = C_HS; | 
|  | data = cp; | 
|  | } | 
|  | if (n == 0) { | 
|  | /* Restore the default.  Better than | 
|  | nother at all.  */ | 
|  | ctx->classes[0] = C_IN; | 
|  | ctx->classes[1] = C_HS; | 
|  | } else if (n == 1 | 
|  | || ctx->classes[0] == ctx->classes[1]) | 
|  | ctx->classes[1] = 0; | 
|  | } | 
|  | } | 
|  | fclose(fp); | 
|  | return (0); | 
|  |  | 
|  | cleanup: | 
|  | fclose(fp); | 
|  | free(ctx->RHS); | 
|  | free(ctx->LHS); | 
|  | ctx->RHS = ctx->LHS = 0; | 
|  | return (-1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Given a DNS class and a DNS name, do a lookup for TXT records, and | 
|  | * return a list of them. | 
|  | */ | 
|  | static char ** | 
|  | get_txt_records(struct hesiod_p *ctx, int class, const char *name) { | 
|  | struct { | 
|  | int type;		/* RR type */ | 
|  | int class;		/* RR class */ | 
|  | int dlen;		/* len of data section */ | 
|  | u_char *data;		/* pointer to data */ | 
|  | } rr; | 
|  | HEADER *hp; | 
|  | u_char qbuf[MAX_HESRESP], abuf[MAX_HESRESP]; | 
|  | u_char *cp, *erdata, *eom; | 
|  | char *dst, *edst, **list; | 
|  | int ancount, qdcount; | 
|  | int i, j, n, skip; | 
|  |  | 
|  | /* | 
|  | * Construct the query and send it. | 
|  | */ | 
|  | n = res_nmkquery(ctx->res, QUERY, name, class, T_TXT, NULL, 0, | 
|  | NULL, qbuf, MAX_HESRESP); | 
|  | if (n < 0) { | 
|  | __set_errno(EMSGSIZE); | 
|  | return (NULL); | 
|  | } | 
|  | n = res_nsend(ctx->res, qbuf, n, abuf, MAX_HESRESP); | 
|  | if (n < 0) { | 
|  | __set_errno(ECONNREFUSED); | 
|  | return (NULL); | 
|  | } | 
|  | if (n < HFIXEDSZ) { | 
|  | __set_errno(EMSGSIZE); | 
|  | return (NULL); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * OK, parse the result. | 
|  | */ | 
|  | hp = (HEADER *) abuf; | 
|  | ancount = ntohs(hp->ancount); | 
|  | qdcount = ntohs(hp->qdcount); | 
|  | cp = abuf + sizeof(HEADER); | 
|  | eom = abuf + n; | 
|  |  | 
|  | /* Skip query, trying to get to the answer section which follows. */ | 
|  | for (i = 0; i < qdcount; i++) { | 
|  | skip = dn_skipname(cp, eom); | 
|  | if (skip < 0 || cp + skip + QFIXEDSZ > eom) { | 
|  | __set_errno(EMSGSIZE); | 
|  | return (NULL); | 
|  | } | 
|  | cp += skip + QFIXEDSZ; | 
|  | } | 
|  |  | 
|  | list = malloc((ancount + 1) * sizeof(char *)); | 
|  | if (!list) | 
|  | return (NULL); | 
|  | j = 0; | 
|  | for (i = 0; i < ancount; i++) { | 
|  | skip = dn_skipname(cp, eom); | 
|  | if (skip < 0) { | 
|  | __set_errno(EMSGSIZE); | 
|  | goto cleanup; | 
|  | } | 
|  | cp += skip; | 
|  | if (cp + 3 * INT16SZ + INT32SZ > eom) { | 
|  | __set_errno(EMSGSIZE); | 
|  | goto cleanup; | 
|  | } | 
|  | rr.type = ns_get16(cp); | 
|  | cp += INT16SZ; | 
|  | rr.class = ns_get16(cp); | 
|  | cp += INT16SZ + INT32SZ;	/* skip the ttl, too */ | 
|  | rr.dlen = ns_get16(cp); | 
|  | cp += INT16SZ; | 
|  | if (cp + rr.dlen > eom) { | 
|  | __set_errno(EMSGSIZE); | 
|  | goto cleanup; | 
|  | } | 
|  | rr.data = cp; | 
|  | cp += rr.dlen; | 
|  | if (rr.class != class || rr.type != T_TXT) | 
|  | continue; | 
|  | if (!(list[j] = malloc(rr.dlen))) | 
|  | goto cleanup; | 
|  | dst = list[j++]; | 
|  | edst = dst + rr.dlen; | 
|  | erdata = rr.data + rr.dlen; | 
|  | cp = rr.data; | 
|  | while (cp < erdata) { | 
|  | n = (unsigned char) *cp++; | 
|  | if (cp + n > eom || dst + n > edst) { | 
|  | __set_errno(EMSGSIZE); | 
|  | goto cleanup; | 
|  | } | 
|  | memcpy(dst, cp, n); | 
|  | cp += n; | 
|  | dst += n; | 
|  | } | 
|  | if (cp != erdata) { | 
|  | __set_errno(EMSGSIZE); | 
|  | goto cleanup; | 
|  | } | 
|  | *dst = '\0'; | 
|  | } | 
|  | list[j] = NULL; | 
|  | if (j == 0) { | 
|  | __set_errno(ENOENT); | 
|  | goto cleanup; | 
|  | } | 
|  | return (list); | 
|  |  | 
|  | cleanup: | 
|  | for (i = 0; i < j; i++) | 
|  | free(list[i]); | 
|  | free(list); | 
|  | return (NULL); | 
|  | } | 
|  |  | 
|  | struct __res_state * | 
|  | __hesiod_res_get(void *context) { | 
|  | struct hesiod_p *ctx = context; | 
|  |  | 
|  | if (!ctx->res) { | 
|  | struct __res_state *res; | 
|  | res = (struct __res_state *)calloc(1, sizeof *res); | 
|  | if (res == NULL) | 
|  | return (NULL); | 
|  | __hesiod_res_set(ctx, res, free); | 
|  | } | 
|  |  | 
|  | return (ctx->res); | 
|  | } | 
|  |  | 
|  | void | 
|  | __hesiod_res_set(void *context, struct __res_state *res, | 
|  | void (*free_res)(void *)) { | 
|  | struct hesiod_p *ctx = context; | 
|  |  | 
|  | if (ctx->res && ctx->free_res) { | 
|  | res_nclose(ctx->res); | 
|  | (*ctx->free_res)(ctx->res); | 
|  | } | 
|  |  | 
|  | ctx->res = res; | 
|  | ctx->free_res = free_res; | 
|  | } | 
|  |  | 
|  | static int | 
|  | init(struct hesiod_p *ctx) { | 
|  |  | 
|  | if (!ctx->res && !__hesiod_res_get(ctx)) | 
|  | return (-1); | 
|  |  | 
|  | if (__res_maybe_init (ctx->res, 0) == -1) | 
|  | return (-1); | 
|  |  | 
|  | return (0); | 
|  | } |