Merge from vendor branch CVS:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / bind / irs / hesiod.c
1 #if defined(LIBC_SCCS) && !defined(lint)
2 static const char rcsid[] = "$Id: hesiod.c,v 1.1.2.4 2004/05/17 07:46:43 marka Exp $";
3 #endif
4
5 /*
6  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7  * Copyright (c) 1996,1999 by Internet Software Consortium.
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21
22 /*
23  * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>.
24  */
25
26 /*
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  */
33
34 /* Imports */
35
36 #include "port_before.h"
37
38 #include <sys/types.h>
39 #include <netinet/in.h>
40 #include <arpa/nameser.h>
41
42 #include <errno.h>
43 #include <netdb.h>
44 #include <resolv.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 #include "port_after.h"
50
51 #include "pathnames.h"
52 #include "hesiod.h"
53 #include "hesiod_p.h"
54
55 /* Forward */
56
57 int             hesiod_init(void **context);
58 void            hesiod_end(void *context);
59 char *          hesiod_to_bind(void *context, const char *name,
60                                const char *type);
61 char **         hesiod_resolve(void *context, const char *name,
62                                const char *type);
63 void            hesiod_free_list(void *context, char **list);
64
65 static int      parse_config_file(struct hesiod_p *ctx, const char *filename);
66 static char **  get_txt_records(struct hesiod_p *ctx, int class,
67                                 const char *name);
68 static int      init(struct hesiod_p *ctx);
69
70 /* Public */
71
72 /*
73  * This function is called to initialize a hesiod_p.
74  */
75 int
76 hesiod_init(void **context) {
77         struct hesiod_p *ctx;
78         char *cp;
79
80         ctx = malloc(sizeof(struct hesiod_p));
81         if (ctx == 0) {
82                 errno = ENOMEM;
83                 return (-1);
84         }
85
86         ctx->LHS = NULL;
87         ctx->RHS = NULL;
88         ctx->res = NULL;
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  */
153 void
154 hesiod_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  */
174 char *
175 hesiod_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  */
233 char **
234 hesiod_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
259 void
260 hesiod_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  */
273 static int
274 parse_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  */
344 static char **
345 get_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
464 struct __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
482 void
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
496 static int
497 init(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 }