1 /* Copyright (c) 1996 by Internet Software Consortium.
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.
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
17 /* Copyright 1996 by the Massachusetts Institute of Technology.
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.
32 /* This file is part of the hesiod library. It implements the core
33 * portion of the hesiod resolver.
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
40 * This implementation is not truly thread-safe at the moment because
41 * it uses res_send() and accesses _res.
44 static const char rcsid[] = "$Id: hesiod.c,v 1.18.2.1 1997/01/03 20:48:20 ghudson Exp $";
46 #include <sys/types.h>
47 #include <netinet/in.h>
48 #include <arpa/nameser.h>
59 /* A few operating systems don't define these. */
67 static int read_config_file(struct hesiod_p *ctx, const char *filename);
68 static char **get_txt_records(struct hesiod_p *ctx, int class,
70 static int cistrcmp(const char *s1, const char *s2);
72 /* This function is called to initialize a hesiod_p. */
73 int hesiod_init(void **context)
76 const char *p, *configname;
78 ctx = malloc(sizeof(struct hesiod_p));
82 configname = getenv("HESIOD_CONFIG");
84 configname = SYSCONFDIR "/hesiod.conf";
85 if (read_config_file(ctx, configname) >= 0)
87 /* The default rhs can be overridden by an environment variable. */
88 p = getenv("HES_DOMAIN");
93 ctx->rhs = malloc(strlen(p) + 2);
97 strcpy(ctx->rhs + 1, (*p == '.') ? p + 1 : p);
119 /* This function deallocates the hesiod_p. */
120 void hesiod_end(void *context)
122 struct hesiod_p *ctx = (struct hesiod_p *) context;
130 /* This function takes a hesiod (name, type) and returns a DNS
131 * name which is to be resolved.
133 char *hesiod_to_bind(void *context, const char *name, const char *type)
135 struct hesiod_p *ctx = (struct hesiod_p *) context;
136 char bindname[MAXDNAME], *p, *ret, **rhs_list = NULL;
140 strcpy(bindname, name);
142 /* Find the right right hand side to use, possibly truncating bindname. */
143 p = strchr(bindname, '@');
148 rhs = name + (p - bindname);
151 rhs_list = hesiod_resolve(context, p, "rhs-extension");
163 /* See if we have enough room. */
164 len = strlen(bindname) + 1 + strlen(type);
166 len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0);
167 len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0);
168 if (len > sizeof(bindname) - 1)
171 hesiod_free_list(context, rhs_list);
176 /* Put together the rest of the domain. */
177 strcat(bindname, ".");
178 strcat(bindname, type);
181 if (ctx->lhs[0] != '.')
182 strcat(bindname, ".");
183 strcat(bindname, ctx->lhs);
186 strcat(bindname, ".");
187 strcat(bindname, rhs);
189 /* rhs_list is no longer needed, since we're done with rhs. */
191 hesiod_free_list(context, rhs_list);
193 /* Make a copy of the result and return it to the caller. */
194 ret = malloc(strlen(bindname) + 1);
200 strcpy(ret, bindname);
204 /* This is the core function. Given a hesiod name and type, it
205 * returns an array of strings returned by the resolver.
207 char **hesiod_resolve(void *context, const char *name, const char *type)
209 struct hesiod_p *ctx = (struct hesiod_p *) context;
210 char *bindname, **retvec;
212 bindname = hesiod_to_bind(context, name, type);
216 retvec = get_txt_records(ctx, ctx->classes[0], bindname);
217 if (retvec == NULL && errno == ENOENT && ctx->classes[1])
218 retvec = get_txt_records(ctx, ctx->classes[1], bindname);
224 void hesiod_free_list(void *context, char **list)
228 for (p = list; *p; p++)
233 /* This function parses the /etc/hesiod.conf file. Returns 0 on success,
234 * -1 on failure. On failure, it might leave values in ctx->lhs or
235 * ctx->rhs which need to be freed by the caller. */
236 static int read_config_file(struct hesiod_p *ctx, const char *filename)
238 char *key, *data, *p, **which;
239 char buf[MAXDNAME + 7];
243 /* Set default query classes. */
244 ctx->classes[0] = C_IN;
245 ctx->classes[1] = C_HS;
247 /* Try to open the configuration file. */
248 fp = fopen(filename, "r");
251 /* Use compiled in default domain names. */
252 ctx->lhs = malloc(strlen(DEF_LHS) + 1);
253 ctx->rhs = malloc(strlen(DEF_RHS) + 1);
254 if (ctx->lhs && ctx->rhs)
256 strcpy(ctx->lhs, DEF_LHS);
257 strcpy(ctx->rhs, DEF_RHS);
269 while (fgets(buf, sizeof(buf), fp) != NULL)
272 if (*p == '#' || *p == '\n' || *p == '\r')
274 while(*p == ' ' || *p == '\t')
277 while(*p != ' ' && *p != '\t' && *p != '=')
281 while(isspace(*p) || *p == '=')
288 if (cistrcmp(key, "lhs") == 0 || cistrcmp(key, "rhs") == 0)
290 which = (strcmp(key, "lhs") == 0) ? &ctx->lhs : &ctx->rhs;
291 *which = malloc(strlen(data) + 1);
297 strcpy(*which, data);
299 else if (cistrcmp(key, "classes") == 0)
302 while (*data && n < 2)
305 while (*p && *p != ',')
309 if (cistrcmp(data, "IN") == 0)
310 ctx->classes[n++] = C_IN;
311 else if (cistrcmp(data, "HS") == 0)
312 ctx->classes[n++] = C_HS;
316 ctx->classes[n++] = 0;
321 if (!ctx->rhs || ctx->classes[0] == 0 || ctx->classes[0] == ctx->classes[1])
330 /* Given a DNS class and a DNS name, do a lookup for TXT records, and
331 * return a list of them.
333 static char **get_txt_records(struct hesiod_p *ctx, int qclass,
337 unsigned char qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor;
339 int ancount, qdcount, i, j, n, skip, type, class, len;
341 /* Make sure the resolver is initialized. */
342 if ((_res.options & RES_INIT) == 0 && res_init() == -1)
345 /* Construct the query. */
346 n = res_mkquery(QUERY, name, qclass, T_TXT, NULL, 0,
347 NULL, qbuf, PACKETSZ);
351 /* Send the query. */
352 n = res_send(qbuf, n, abuf, MAX_HESRESP);
355 errno = ECONNREFUSED;
359 /* Parse the header of the result. */
360 hp = (HEADER *) abuf;
361 ancount = ntohs(hp->ancount);
362 qdcount = ntohs(hp->qdcount);
363 p = abuf + sizeof(HEADER);
366 /* Skip questions, trying to get to the answer section which follows. */
367 for (i = 0; i < qdcount; i++)
369 skip = dn_skipname(p, eom);
370 if (skip < 0 || p + skip + QFIXEDSZ > eom)
375 p += skip + QFIXEDSZ;
378 /* Allocate space for the text record answers. */
379 list = malloc((ancount + 1) * sizeof(char *));
386 /* Parse the answers. */
388 for (i = 0; i < ancount; i++)
390 /* Parse the header of this answer. */
391 skip = dn_skipname(p, eom);
392 if (skip < 0 || p + skip + 10 > eom)
394 type = p[skip + 0] << 8 | p[skip + 1];
395 class = p[skip + 2] << 8 | p[skip + 3];
396 len = p[skip + 8] << 8 | p[skip + 9];
404 /* Skip entries of the wrong class and type. */
405 if (class != qclass || type != T_TXT)
411 /* Allocate space for this answer. */
412 list[j] = malloc(len);
420 /* Copy answer data into the allocated area. */
424 n = (unsigned char) *p++;
442 /* If we didn't terminate the loop normally, something went wrong. */
445 for (i = 0; i < j; i++)
462 static int cistrcmp(const char *s1, const char *s2)
464 while (*s1 && tolower(*s1) == tolower(*s2))
469 return tolower(*s1) - tolower(*s2);