Pretty print.
[glibc.git] / hesiod / hesiod.c
blobf89a29ac954a4b4d4a98101778576826018a427e
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
14 * SOFTWARE.
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
38 * path.
40 * This implementation is not truly thread-safe at the moment because
41 * it uses res_send() and accesses _res.
44 #if defined(LIBC_SCCS) && !defined(lint)
45 static const char rcsid[] = "$Id$";
46 #endif
48 #include <sys/types.h>
49 #include <netinet/in.h>
50 #include <arpa/nameser.h>
51 #include <errno.h>
52 #include <netdb.h>
53 #include <resolv.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <ctype.h>
58 #include "hesiod.h"
59 #include "hesiod_p.h"
61 /* A few operating systems don't define these. */
62 #ifndef C_HS
63 # define C_HS 4
64 #endif
65 #ifndef T_TXT
66 # define T_TXT 16
67 #endif
69 static int read_config_file (struct hesiod_p *ctx, const char *filename);
70 static char **get_txt_records (struct hesiod_p *ctx, int class,
71 const char *name);
72 #ifdef _LIBC
73 # define cistrcmp(s1, s2) strcasecmp (s1, s2)
74 #else
75 static int cistrcmp (const char *s1, const char *s2);
76 #endif
78 /* This function is called to initialize a hesiod_p. */
79 int
80 hesiod_init (void **context)
82 struct hesiod_p *ctx;
83 const char *p, *configname;
85 ctx = malloc (sizeof (struct hesiod_p));
86 if (ctx)
88 *context = ctx;
89 configname = __secure_getenv ("HESIOD_CONFIG");
90 if (!configname)
91 configname = SYSCONFDIR "/hesiod.conf";
92 if (read_config_file (ctx, configname) >= 0)
94 /* The default rhs can be overridden by an environment variable. */
95 p = __secure_getenv ("HES_DOMAIN");
96 if (p)
98 if (ctx->rhs)
99 free (ctx->rhs);
100 ctx->rhs = malloc (strlen (p) + 2);
101 if (ctx->rhs)
103 *ctx->rhs = '.';
104 strcpy (ctx->rhs + 1, (*p == '.') ? p + 1 : p);
105 return 0;
107 else
108 __set_errno (ENOMEM);
110 else
111 return 0;
114 else
115 __set_errno (ENOMEM);
117 if (ctx->lhs)
118 free (ctx->lhs);
119 if (ctx->rhs)
120 free (ctx->rhs);
121 if (ctx)
122 free (ctx);
123 return -1;
126 /* This function deallocates the hesiod_p. */
127 void
128 hesiod_end (void *context)
130 struct hesiod_p *ctx = (struct hesiod_p *) context;
132 free (ctx->rhs);
133 if (ctx->lhs)
134 free (ctx->lhs);
135 free (ctx);
138 /* This function takes a hesiod (name, type) and returns a DNS
139 * name which is to be resolved.
141 char *
142 hesiod_to_bind (void *context, const char *name, const char *type)
144 struct hesiod_p *ctx = (struct hesiod_p *) context;
145 char bindname[MAXDNAME], *p, *endp, *ret, **rhs_list = NULL;
146 const char *rhs;
147 size_t len;
149 endp = stpcpy (bindname, name);
151 /* Find the right right hand side to use, possibly truncating bindname. */
152 p = strchr (bindname, '@');
153 if (p)
155 *p++ = 0;
156 if (strchr (p, '.'))
157 rhs = name + (p - bindname);
158 else
160 rhs_list = hesiod_resolve (context, p, "rhs-extension");
161 if (rhs_list)
162 rhs = *rhs_list;
163 else
165 __set_errno (ENOENT);
166 return NULL;
170 else
171 rhs = ctx->rhs;
173 /* See if we have enough room. */
174 len = (endp - bindname) + 1 + strlen (type);
175 if (ctx->lhs)
176 len += strlen (ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0);
177 len += strlen (rhs) + ((rhs[0] != '.') ? 1 : 0);
178 if (len > sizeof (bindname) - 1)
180 if (rhs_list)
181 hesiod_free_list (context, rhs_list);
182 __set_errno (EMSGSIZE);
183 return NULL;
186 /* Put together the rest of the domain. */
187 endp = stpcpy (stpcpy (endp, "."), type);
188 if (ctx->lhs)
190 if (ctx->lhs[0] != '.')
191 endp = stpcpy (endp, ".");
192 endp = stpcpy (endp, ctx->lhs);
194 if (rhs[0] != '.')
195 endp = stpcpy (endp, ".");
196 endp = stpcpy (endp, rhs);
198 /* rhs_list is no longer needed, since we're done with rhs. */
199 if (rhs_list)
200 hesiod_free_list (context, rhs_list);
202 /* Make a copy of the result and return it to the caller. */
203 ret = malloc ((endp - bindname) + 1);
204 if (!ret)
206 __set_errno (ENOMEM);
207 return NULL;
209 return strcpy (ret, bindname);
212 /* This is the core function. Given a hesiod name and type, it
213 * returns an array of strings returned by the resolver.
215 char **
216 hesiod_resolve (void *context, const char *name, const char *type)
218 struct hesiod_p *ctx = (struct hesiod_p *) context;
219 char *bindname, **retvec;
221 bindname = hesiod_to_bind (context, name, type);
222 if (bindname == NULL)
223 return NULL;
225 retvec = get_txt_records(ctx, ctx->classes[0], bindname);
226 if (retvec == NULL && errno == ENOENT && ctx->classes[1])
227 retvec = get_txt_records (ctx, ctx->classes[1], bindname);
229 free (bindname);
230 return retvec;
233 void
234 hesiod_free_list (void *context, char **list)
236 char **p;
238 for (p = list; *p; p++)
239 free (*p);
240 free (list);
243 /* This function parses the /etc/hesiod.conf file. Returns 0 on success,
244 * -1 on failure. On failure, it might leave values in ctx->lhs or
245 * ctx->rhs which need to be freed by the caller. */
246 static int
247 read_config_file (struct hesiod_p *ctx, const char *filename)
249 char *key, *data, *p, **which;
250 char buf[MAXDNAME + 7];
251 int n;
252 FILE *fp;
254 /* Set default query classes. */
255 ctx->classes[0] = C_IN;
256 ctx->classes[1] = C_HS;
258 /* Try to open the configuration file. */
259 fp = fopen (filename, "r");
260 if (fp == NULL)
262 /* Use compiled in default domain names. */
263 ctx->lhs = malloc (strlen (DEF_LHS) + 1);
264 ctx->rhs = malloc (strlen (DEF_RHS) + 1);
265 if (ctx->lhs && ctx->rhs)
267 strcpy (ctx->lhs, DEF_LHS);
268 strcpy (ctx->rhs, DEF_RHS);
269 return 0;
271 else
273 __set_errno (ENOMEM);
274 return -1;
278 ctx->lhs = NULL;
279 ctx->rhs = NULL;
280 while (fgets (buf, sizeof (buf), fp) != NULL)
282 p = buf;
283 if (*p == '#' || *p == '\n' || *p == '\r')
284 continue;
285 while (*p == ' ' || *p == '\t')
286 ++p;
287 key = p;
288 while(*p != ' ' && *p != '\t' && *p != '=')
289 ++p;
290 *p++ = 0;
292 while (isspace (*p) || *p == '=')
293 ++p;
294 data = p;
295 while (!isspace (*p))
296 ++p;
297 *p = 0;
299 if (cistrcmp (key, "lhs") == 0 || cistrcmp (key, "rhs") == 0)
301 which = (strcmp (key, "lhs") == 0) ? &ctx->lhs : &ctx->rhs;
302 *which = strdup (data);
303 if (!*which)
305 __set_errno (ENOMEM);
306 return -1;
309 else if (cistrcmp (key, "classes") == 0)
311 n = 0;
312 while (*data && n < 2)
314 p = data;
315 while (*p && *p != ',')
316 ++p;
317 if (*p)
318 *p++ = 0;
319 if (cistrcmp (data, "IN") == 0)
320 ctx->classes[n++] = C_IN;
321 else if (cistrcmp (data, "HS") == 0)
322 ctx->classes[n++] = C_HS;
323 data = p;
325 while (n < 2)
326 ctx->classes[n++] = 0;
329 fclose (fp);
331 if (!ctx->rhs || ctx->classes[0] == 0 || ctx->classes[0] == ctx->classes[1])
333 __set_errno (ENOEXEC);
334 return -1;
337 return 0;
340 /* Given a DNS class and a DNS name, do a lookup for TXT records, and
341 * return a list of them.
343 static char **
344 get_txt_records (struct hesiod_p *ctx, int qclass, const char *name)
346 HEADER *hp;
347 unsigned char qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor;
348 char *dst, **list;
349 int ancount, qdcount, i, j, n, skip, type, class, len;
351 /* Make sure the resolver is initialized. */
352 if ((_res.options & RES_INIT) == 0 && res_init () == -1)
353 return NULL;
355 /* Construct the query. */
356 n = res_mkquery (QUERY, name, qclass, T_TXT, NULL, 0,
357 NULL, qbuf, PACKETSZ);
358 if (n < 0)
359 return NULL;
361 /* Send the query. */
362 n = res_send (qbuf, n, abuf, MAX_HESRESP);
363 if (n < 0)
365 __set_errno (ECONNREFUSED);
366 return NULL;
369 /* Parse the header of the result. */
370 hp = (HEADER *) abuf;
371 ancount = ntohs (hp->ancount);
372 qdcount = ntohs (hp->qdcount);
373 p = abuf + sizeof (HEADER);
374 eom = abuf + n;
376 /* Skip questions, trying to get to the answer section which follows. */
377 for (i = 0; i < qdcount; ++i)
379 skip = dn_skipname (p, eom);
380 if (skip < 0 || p + skip + QFIXEDSZ > eom)
382 __set_errno (EMSGSIZE);
383 return NULL;
385 p += skip + QFIXEDSZ;
388 /* Allocate space for the text record answers. */
389 list = malloc ((ancount + 1) * sizeof(char *));
390 if (list == NULL)
392 __set_errno (ENOMEM);
393 return NULL;
396 /* Parse the answers. */
397 j = 0;
398 for (i = 0; i < ancount; i++)
400 /* Parse the header of this answer. */
401 skip = dn_skipname (p, eom);
402 if (skip < 0 || p + skip + 10 > eom)
403 break;
404 type = p[skip + 0] << 8 | p[skip + 1];
405 class = p[skip + 2] << 8 | p[skip + 3];
406 len = p[skip + 8] << 8 | p[skip + 9];
407 p += skip + 10;
408 if (p + len > eom)
410 __set_errno (EMSGSIZE);
411 break;
414 /* Skip entries of the wrong class and type. */
415 if (class != qclass || type != T_TXT)
417 p += len;
418 continue;
421 /* Allocate space for this answer. */
422 list[j] = malloc (len);
423 if (!list[j])
425 __set_errno (ENOMEM);
426 break;
428 dst = list[j++];
430 /* Copy answer data into the allocated area. */
431 eor = p + len;
432 while (p < eor)
434 n = (unsigned char) *p++;
435 if (p + n > eor)
437 __set_errno (EMSGSIZE);
438 break;
440 dst = mempcpy (dst, p, n);
441 p += n;
443 if (p < eor)
445 __set_errno (EMSGSIZE);
446 break;
448 *dst = 0;
451 /* If we didn't terminate the loop normally, something went wrong. */
452 if (i < ancount)
454 for (i = 0; i < j; i++)
455 free (list[i]);
456 free (list);
457 return NULL;
460 if (j == 0)
462 __set_errno (ENOENT);
463 free (list);
464 return NULL;
467 list[j] = NULL;
468 return list;
471 #ifndef _LIBC
472 static int
473 cistrcmp (const char *s1, const char *s2)
475 while (*s1 && tolower(*s1) == tolower(*s2))
477 s1++;
478 s2++;
480 return tolower(*s1) - tolower(*s2);
482 #endif