165532Snectar/* $NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $ */ 265532Snectar 365532Snectar/* Copyright (c) 1996 by Internet Software Consortium. 465532Snectar * 565532Snectar * Permission to use, copy, modify, and distribute this software for any 665532Snectar * purpose with or without fee is hereby granted, provided that the above 765532Snectar * copyright notice and this permission notice appear in all copies. 865532Snectar * 965532Snectar * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 1065532Snectar * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 1165532Snectar * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 1265532Snectar * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 1365532Snectar * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 1465532Snectar * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 1565532Snectar * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 1665532Snectar * SOFTWARE. 1765532Snectar */ 1865532Snectar 1965532Snectar/* Copyright 1996 by the Massachusetts Institute of Technology. 2065532Snectar * 2165532Snectar * Permission to use, copy, modify, and distribute this 2265532Snectar * software and its documentation for any purpose and without 2365532Snectar * fee is hereby granted, provided that the above copyright 2465532Snectar * notice appear in all copies and that both that copyright 2565532Snectar * notice and this permission notice appear in supporting 2665532Snectar * documentation, and that the name of M.I.T. not be used in 2765532Snectar * advertising or publicity pertaining to distribution of the 2865532Snectar * software without specific, written prior permission. 2965532Snectar * M.I.T. makes no representations about the suitability of 3065532Snectar * this software for any purpose. It is provided "as is" 3165532Snectar * without express or implied warranty. 3265532Snectar */ 3365532Snectar 3465532Snectar/* This file is part of the hesiod library. It implements the core 3565532Snectar * portion of the hesiod resolver. 3665532Snectar * 3765532Snectar * This file is loosely based on an interim version of hesiod.c from 3865532Snectar * the BIND IRS library, which was in turn based on an earlier version 3965532Snectar * of this file. Extensive changes have been made on each step of the 4065532Snectar * path. 4165532Snectar * 4265532Snectar * This implementation is not truly thread-safe at the moment because 4365532Snectar * it uses res_send() and accesses _res. 4465532Snectar */ 4565532Snectar 4665532Snectar#include <sys/cdefs.h> 4765532Snectar 4892986Sobrien#if 0 4965532Snectarstatic char *orig_rcsid = "$NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $"; 5092986Sobrien#endif 5192986Sobrien#include <sys/cdefs.h> 5292986Sobrien__FBSDID("$FreeBSD$"); 5365532Snectar 5465532Snectar#include <sys/types.h> 5565532Snectar#include <sys/param.h> 5665532Snectar#include <netinet/in.h> 5765532Snectar#include <arpa/nameser.h> 5865532Snectar 5965532Snectar#include <ctype.h> 6065532Snectar#include <errno.h> 6165532Snectar#include <hesiod.h> 6265532Snectar#include <resolv.h> 6365532Snectar#include <stdio.h> 6465532Snectar#include <stdlib.h> 6565532Snectar#include <string.h> 6666451Snectar#include <unistd.h> 6765532Snectar 6865532Snectarstruct hesiod_p { 6965532Snectar char *lhs; /* normally ".ns" */ 7065532Snectar char *rhs; /* AKA the default hesiod domain */ 7165532Snectar int classes[2]; /* The class search order. */ 7265532Snectar}; 7365532Snectar 7465532Snectar#define MAX_HESRESP 1024 7565532Snectar 7692905Sobrienstatic int read_config_file(struct hesiod_p *, const char *); 7792905Sobrienstatic char **get_txt_records(int, const char *); 7892905Sobrienstatic int init_context(void); 7992905Sobrienstatic void translate_errors(void); 8065532Snectar 8165532Snectar 8265532Snectar/* 8365532Snectar * hesiod_init -- 8465532Snectar * initialize a hesiod_p. 8565532Snectar */ 8665532Snectarint 8765532Snectarhesiod_init(context) 8865532Snectar void **context; 8965532Snectar{ 9065532Snectar struct hesiod_p *ctx; 9165532Snectar const char *p, *configname; 9265532Snectar 9365532Snectar ctx = malloc(sizeof(struct hesiod_p)); 9465532Snectar if (ctx) { 9565532Snectar *context = ctx; 9666485Snectar if (!issetugid()) 9766451Snectar configname = getenv("HESIOD_CONFIG"); 9866451Snectar else 9966451Snectar configname = NULL; 10065532Snectar if (!configname) 10165532Snectar configname = _PATH_HESIOD_CONF; 10265532Snectar if (read_config_file(ctx, configname) >= 0) { 10365532Snectar /* 10465532Snectar * The default rhs can be overridden by an 10565532Snectar * environment variable. 10665532Snectar */ 10766485Snectar if (!issetugid()) 10866451Snectar p = getenv("HES_DOMAIN"); 10966451Snectar else 11066451Snectar p = NULL; 11165532Snectar if (p) { 11265532Snectar if (ctx->rhs) 11365532Snectar free(ctx->rhs); 11465532Snectar ctx->rhs = malloc(strlen(p) + 2); 11565532Snectar if (ctx->rhs) { 11665532Snectar *ctx->rhs = '.'; 11765532Snectar strcpy(ctx->rhs + 1, 11865532Snectar (*p == '.') ? p + 1 : p); 11965532Snectar return 0; 12065532Snectar } else 12165532Snectar errno = ENOMEM; 12265532Snectar } else 12365532Snectar return 0; 12465532Snectar } 12565532Snectar } else 12665532Snectar errno = ENOMEM; 12765532Snectar 12865532Snectar if (ctx->lhs) 12965532Snectar free(ctx->lhs); 13065532Snectar if (ctx->rhs) 13165532Snectar free(ctx->rhs); 13265532Snectar if (ctx) 13365532Snectar free(ctx); 13465532Snectar return -1; 13565532Snectar} 13665532Snectar 13765532Snectar/* 13865532Snectar * hesiod_end -- 13965532Snectar * Deallocates the hesiod_p. 14065532Snectar */ 14165532Snectarvoid 14265532Snectarhesiod_end(context) 14365532Snectar void *context; 14465532Snectar{ 14565532Snectar struct hesiod_p *ctx = (struct hesiod_p *) context; 14665532Snectar 14765532Snectar free(ctx->rhs); 14865532Snectar if (ctx->lhs) 14965532Snectar free(ctx->lhs); 15065532Snectar free(ctx); 15165532Snectar} 15265532Snectar 15365532Snectar/* 15465532Snectar * hesiod_to_bind -- 15565532Snectar * takes a hesiod (name, type) and returns a DNS 15665532Snectar * name which is to be resolved. 15765532Snectar */ 15865532Snectarchar * 15965532Snectarhesiod_to_bind(void *context, const char *name, const char *type) 16065532Snectar{ 16165532Snectar struct hesiod_p *ctx = (struct hesiod_p *) context; 16265532Snectar char bindname[MAXDNAME], *p, *ret, **rhs_list = NULL; 16365532Snectar const char *rhs; 16465532Snectar int len; 16565532Snectar 166114443Snectar if (strlcpy(bindname, name, sizeof(bindname)) >= sizeof(bindname)) { 16771929Snectar errno = EMSGSIZE; 16871929Snectar return NULL; 16971929Snectar } 17065532Snectar 17165532Snectar /* 17265532Snectar * Find the right right hand side to use, possibly 17365532Snectar * truncating bindname. 17465532Snectar */ 17565532Snectar p = strchr(bindname, '@'); 17665532Snectar if (p) { 17765532Snectar *p++ = 0; 17865532Snectar if (strchr(p, '.')) 17965532Snectar rhs = name + (p - bindname); 18065532Snectar else { 18165532Snectar rhs_list = hesiod_resolve(context, p, "rhs-extension"); 18265532Snectar if (rhs_list) 18365532Snectar rhs = *rhs_list; 18465532Snectar else { 18565532Snectar errno = ENOENT; 18665532Snectar return NULL; 18765532Snectar } 18865532Snectar } 18965532Snectar } else 19065532Snectar rhs = ctx->rhs; 19165532Snectar 19265532Snectar /* See if we have enough room. */ 19365532Snectar len = strlen(bindname) + 1 + strlen(type); 19465532Snectar if (ctx->lhs) 19565532Snectar len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0); 19665532Snectar len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0); 19765532Snectar if (len > sizeof(bindname) - 1) { 19865532Snectar if (rhs_list) 19965532Snectar hesiod_free_list(context, rhs_list); 20065532Snectar errno = EMSGSIZE; 20165532Snectar return NULL; 20265532Snectar } 20365532Snectar /* Put together the rest of the domain. */ 20465532Snectar strcat(bindname, "."); 20565532Snectar strcat(bindname, type); 20665532Snectar /* Only append lhs if it isn't empty. */ 20765532Snectar if (ctx->lhs && ctx->lhs[0] != '\0' ) { 20865532Snectar if (ctx->lhs[0] != '.') 20965532Snectar strcat(bindname, "."); 21065532Snectar strcat(bindname, ctx->lhs); 21165532Snectar } 21265532Snectar if (rhs[0] != '.') 21365532Snectar strcat(bindname, "."); 21465532Snectar strcat(bindname, rhs); 21565532Snectar 21665532Snectar /* rhs_list is no longer needed, since we're done with rhs. */ 21765532Snectar if (rhs_list) 21865532Snectar hesiod_free_list(context, rhs_list); 21965532Snectar 22065532Snectar /* Make a copy of the result and return it to the caller. */ 22165532Snectar ret = strdup(bindname); 22265532Snectar if (!ret) 22365532Snectar errno = ENOMEM; 22465532Snectar return ret; 22565532Snectar} 22665532Snectar 22765532Snectar/* 22865532Snectar * hesiod_resolve -- 22965532Snectar * Given a hesiod name and type, return an array of strings returned 23065532Snectar * by the resolver. 23165532Snectar */ 23265532Snectarchar ** 23365532Snectarhesiod_resolve(context, name, type) 23465532Snectar void *context; 23565532Snectar const char *name; 23665532Snectar const char *type; 23765532Snectar{ 23865532Snectar struct hesiod_p *ctx = (struct hesiod_p *) context; 23965532Snectar char *bindname, **retvec; 24065532Snectar 24165532Snectar bindname = hesiod_to_bind(context, name, type); 24265532Snectar if (!bindname) 24365532Snectar return NULL; 24465532Snectar 24565532Snectar retvec = get_txt_records(ctx->classes[0], bindname); 24665532Snectar if (retvec == NULL && errno == ENOENT && ctx->classes[1]) 24765532Snectar retvec = get_txt_records(ctx->classes[1], bindname); 24865532Snectar 24965532Snectar free(bindname); 25065532Snectar return retvec; 25165532Snectar} 25265532Snectar 25365532Snectar/*ARGSUSED*/ 25465532Snectarvoid 25565532Snectarhesiod_free_list(context, list) 25665532Snectar void *context; 25765532Snectar char **list; 25865532Snectar{ 25965532Snectar char **p; 26065532Snectar 26165532Snectar if (list == NULL) 26265532Snectar return; 26365532Snectar for (p = list; *p; p++) 26465532Snectar free(*p); 26565532Snectar free(list); 26665532Snectar} 26765532Snectar 26865532Snectar 26965532Snectar/* read_config_file -- 27065532Snectar * Parse the /etc/hesiod.conf file. Returns 0 on success, 27165532Snectar * -1 on failure. On failure, it might leave values in ctx->lhs 27265532Snectar * or ctx->rhs which need to be freed by the caller. 27365532Snectar */ 27465532Snectarstatic int 27565532Snectarread_config_file(ctx, filename) 27665532Snectar struct hesiod_p *ctx; 27765532Snectar const char *filename; 27865532Snectar{ 27965532Snectar char *key, *data, *p, **which; 28065532Snectar char buf[MAXDNAME + 7]; 28165532Snectar int n; 28265532Snectar FILE *fp; 28365532Snectar 28465532Snectar /* Set default query classes. */ 28565532Snectar ctx->classes[0] = C_IN; 28665532Snectar ctx->classes[1] = C_HS; 28765532Snectar 28865532Snectar /* Try to open the configuration file. */ 289254700Sjilles fp = fopen(filename, "re"); 29065532Snectar if (!fp) { 29165532Snectar /* Use compiled in default domain names. */ 29265532Snectar ctx->lhs = strdup(DEF_LHS); 29365532Snectar ctx->rhs = strdup(DEF_RHS); 29465532Snectar if (ctx->lhs && ctx->rhs) 29565532Snectar return 0; 29665532Snectar else { 29765532Snectar errno = ENOMEM; 29865532Snectar return -1; 29965532Snectar } 30065532Snectar } 30165532Snectar ctx->lhs = NULL; 30265532Snectar ctx->rhs = NULL; 30365532Snectar while (fgets(buf, sizeof(buf), fp) != NULL) { 30465532Snectar p = buf; 30565532Snectar if (*p == '#' || *p == '\n' || *p == '\r') 30665532Snectar continue; 30765532Snectar while (*p == ' ' || *p == '\t') 30865532Snectar p++; 30965532Snectar key = p; 31065532Snectar while (*p != ' ' && *p != '\t' && *p != '=') 31165532Snectar p++; 31265532Snectar *p++ = 0; 31365532Snectar 31465532Snectar while (isspace(*p) || *p == '=') 31565532Snectar p++; 31665532Snectar data = p; 31765532Snectar while (!isspace(*p)) 31865532Snectar p++; 31965532Snectar *p = 0; 32065532Snectar 32165532Snectar if (strcasecmp(key, "lhs") == 0 || 32265532Snectar strcasecmp(key, "rhs") == 0) { 32365532Snectar which = (strcasecmp(key, "lhs") == 0) 32465532Snectar ? &ctx->lhs : &ctx->rhs; 32565532Snectar *which = strdup(data); 32665532Snectar if (!*which) { 327217143Skib fclose(fp); 32865532Snectar errno = ENOMEM; 32965532Snectar return -1; 33065532Snectar } 33165532Snectar } else { 33265532Snectar if (strcasecmp(key, "classes") == 0) { 33365532Snectar n = 0; 33465532Snectar while (*data && n < 2) { 33565532Snectar p = data; 33665532Snectar while (*p && *p != ',') 33765532Snectar p++; 33865532Snectar if (*p) 33965532Snectar *p++ = 0; 34065532Snectar if (strcasecmp(data, "IN") == 0) 34165532Snectar ctx->classes[n++] = C_IN; 34265532Snectar else 34365532Snectar if (strcasecmp(data, "HS") == 0) 34465532Snectar ctx->classes[n++] = 34565532Snectar C_HS; 34665532Snectar data = p; 34765532Snectar } 34865532Snectar while (n < 2) 34965532Snectar ctx->classes[n++] = 0; 35065532Snectar } 35165532Snectar } 35265532Snectar } 35365532Snectar fclose(fp); 35465532Snectar 35565532Snectar if (!ctx->rhs || ctx->classes[0] == 0 || 35665532Snectar ctx->classes[0] == ctx->classes[1]) { 35765532Snectar errno = ENOEXEC; 35865532Snectar return -1; 35965532Snectar } 36065532Snectar return 0; 36165532Snectar} 36265532Snectar 36365532Snectar/* 36465532Snectar * get_txt_records -- 36565532Snectar * Given a DNS class and a DNS name, do a lookup for TXT records, and 36665532Snectar * return a list of them. 36765532Snectar */ 36865532Snectarstatic char ** 36965532Snectarget_txt_records(qclass, name) 37065532Snectar int qclass; 37165532Snectar const char *name; 37265532Snectar{ 37365532Snectar HEADER *hp; 37465532Snectar unsigned char qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor; 37565532Snectar char *dst, **list; 37665532Snectar int ancount, qdcount, i, j, n, skip, type, class, len; 37765532Snectar 37865532Snectar /* Make sure the resolver is initialized. */ 37965532Snectar if ((_res.options & RES_INIT) == 0 && res_init() == -1) 38065532Snectar return NULL; 38165532Snectar 38265532Snectar /* Construct the query. */ 38365532Snectar n = res_mkquery(QUERY, name, qclass, T_TXT, NULL, 0, 38465532Snectar NULL, qbuf, PACKETSZ); 38565532Snectar if (n < 0) 38665532Snectar return NULL; 38765532Snectar 38865532Snectar /* Send the query. */ 38965532Snectar n = res_send(qbuf, n, abuf, MAX_HESRESP); 390103350Snectar if (n < 0 || n > MAX_HESRESP) { 391103350Snectar errno = ECONNREFUSED; /* XXX */ 39265532Snectar return NULL; 39365532Snectar } 39465532Snectar /* Parse the header of the result. */ 39565532Snectar hp = (HEADER *) (void *) abuf; 39665532Snectar ancount = ntohs(hp->ancount); 39765532Snectar qdcount = ntohs(hp->qdcount); 39865532Snectar p = abuf + sizeof(HEADER); 39965532Snectar eom = abuf + n; 40065532Snectar 40165532Snectar /* 40265532Snectar * Skip questions, trying to get to the answer section 40365532Snectar * which follows. 40465532Snectar */ 40565532Snectar for (i = 0; i < qdcount; i++) { 40665532Snectar skip = dn_skipname(p, eom); 40765532Snectar if (skip < 0 || p + skip + QFIXEDSZ > eom) { 40865532Snectar errno = EMSGSIZE; 40965532Snectar return NULL; 41065532Snectar } 41165532Snectar p += skip + QFIXEDSZ; 41265532Snectar } 41365532Snectar 41465532Snectar /* Allocate space for the text record answers. */ 41565532Snectar list = malloc((ancount + 1) * sizeof(char *)); 41665532Snectar if (!list) { 41765532Snectar errno = ENOMEM; 41865532Snectar return NULL; 41965532Snectar } 42065532Snectar /* Parse the answers. */ 42165532Snectar j = 0; 42265532Snectar for (i = 0; i < ancount; i++) { 42365532Snectar /* Parse the header of this answer. */ 42465532Snectar skip = dn_skipname(p, eom); 42565532Snectar if (skip < 0 || p + skip + 10 > eom) 42665532Snectar break; 42765532Snectar type = p[skip + 0] << 8 | p[skip + 1]; 42865532Snectar class = p[skip + 2] << 8 | p[skip + 3]; 42965532Snectar len = p[skip + 8] << 8 | p[skip + 9]; 43065532Snectar p += skip + 10; 43165532Snectar if (p + len > eom) { 43265532Snectar errno = EMSGSIZE; 43365532Snectar break; 43465532Snectar } 43565532Snectar /* Skip entries of the wrong class and type. */ 43665532Snectar if (class != qclass || type != T_TXT) { 43765532Snectar p += len; 43865532Snectar continue; 43965532Snectar } 44065532Snectar /* Allocate space for this answer. */ 44165532Snectar list[j] = malloc((size_t)len); 44265532Snectar if (!list[j]) { 44365532Snectar errno = ENOMEM; 44465532Snectar break; 44565532Snectar } 44665532Snectar dst = list[j++]; 44765532Snectar 44865532Snectar /* Copy answer data into the allocated area. */ 44965532Snectar eor = p + len; 45065532Snectar while (p < eor) { 45165532Snectar n = (unsigned char) *p++; 45265532Snectar if (p + n > eor) { 45365532Snectar errno = EMSGSIZE; 45465532Snectar break; 45565532Snectar } 45665532Snectar memcpy(dst, p, (size_t)n); 45765532Snectar p += n; 45865532Snectar dst += n; 45965532Snectar } 46065532Snectar if (p < eor) { 46165532Snectar errno = EMSGSIZE; 46265532Snectar break; 46365532Snectar } 46465532Snectar *dst = 0; 46565532Snectar } 46665532Snectar 46765532Snectar /* 46865532Snectar * If we didn't terminate the loop normally, something 46965532Snectar * went wrong. 47065532Snectar */ 47165532Snectar if (i < ancount) { 47265532Snectar for (i = 0; i < j; i++) 47365532Snectar free(list[i]); 47465532Snectar free(list); 47565532Snectar return NULL; 47665532Snectar } 47765532Snectar if (j == 0) { 47865532Snectar errno = ENOENT; 47965532Snectar free(list); 48065532Snectar return NULL; 48165532Snectar } 48265532Snectar list[j] = NULL; 48365532Snectar return list; 48465532Snectar} 48565532Snectar 48665532Snectar /* 48765532Snectar * COMPATIBILITY FUNCTIONS 48865532Snectar */ 48965532Snectar 49065532Snectarstatic int inited = 0; 49165532Snectarstatic void *context; 49265532Snectarstatic int errval = HES_ER_UNINIT; 49365532Snectar 49465532Snectarint 49565532Snectarhes_init() 49665532Snectar{ 49765532Snectar init_context(); 49865532Snectar return errval; 49965532Snectar} 50065532Snectar 50165532Snectarchar * 50265532Snectarhes_to_bind(name, type) 50365532Snectar const char *name; 50465532Snectar const char *type; 50565532Snectar{ 50665532Snectar static char *bindname; 50765532Snectar if (init_context() < 0) 50865532Snectar return NULL; 50965532Snectar if (bindname) 51065532Snectar free(bindname); 51165532Snectar bindname = hesiod_to_bind(context, name, type); 51265532Snectar if (!bindname) 51365532Snectar translate_errors(); 51465532Snectar return bindname; 51565532Snectar} 51665532Snectar 51765532Snectarchar ** 51865532Snectarhes_resolve(name, type) 51965532Snectar const char *name; 52065532Snectar const char *type; 52165532Snectar{ 52265532Snectar static char **list; 52365532Snectar 52465532Snectar if (init_context() < 0) 52565532Snectar return NULL; 52665532Snectar 52765532Snectar /* 52865532Snectar * In the old Hesiod interface, the caller was responsible for 52965532Snectar * freeing the returned strings but not the vector of strings itself. 53065532Snectar */ 53165532Snectar if (list) 53265532Snectar free(list); 53365532Snectar 53465532Snectar list = hesiod_resolve(context, name, type); 53565532Snectar if (!list) 53665532Snectar translate_errors(); 53765532Snectar return list; 53865532Snectar} 53965532Snectar 54065532Snectarint 54165532Snectarhes_error() 54265532Snectar{ 54365532Snectar return errval; 54465532Snectar} 54565532Snectar 54665532Snectarvoid 54765532Snectarhes_free(hp) 54865532Snectar char **hp; 54965532Snectar{ 55065532Snectar hesiod_free_list(context, hp); 55165532Snectar} 55265532Snectar 55365532Snectarstatic int 55465532Snectarinit_context() 55565532Snectar{ 55665532Snectar if (!inited) { 55765532Snectar inited = 1; 55865532Snectar if (hesiod_init(&context) < 0) { 55965532Snectar errval = HES_ER_CONFIG; 56065532Snectar return -1; 56165532Snectar } 56265532Snectar errval = HES_ER_OK; 56365532Snectar } 56465532Snectar return 0; 56565532Snectar} 56665532Snectar 56765532Snectarstatic void 56865532Snectartranslate_errors() 56965532Snectar{ 57065532Snectar switch (errno) { 57165532Snectar case ENOENT: 57265532Snectar errval = HES_ER_NOTFOUND; 57365532Snectar break; 57465532Snectar case ECONNREFUSED: 57565532Snectar case EMSGSIZE: 57665532Snectar errval = HES_ER_NET; 57765532Snectar break; 57865532Snectar case ENOMEM: 57965532Snectar default: 58065532Snectar /* Not a good match, but the best we can do. */ 58165532Snectar errval = HES_ER_CONFIG; 58265532Snectar break; 58365532Snectar } 58465532Snectar} 585