Detect FPU by checking CPUID features.
[dragonfly.git] / contrib / bind-9.5.2 / lib / bind / irs / hesiod.c
1 #if defined(LIBC_SCCS) && !defined(lint)
2 static const char rcsid[] = "$Id: hesiod.c,v 1.7 2005/07/28 06:51:48 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 /*! \file
24  * \brief
25  * hesiod.c --- the core portion of the hesiod resolver.
26  *
27  * This file is derived from the hesiod library from Project Athena;
28  * It has been extensively rewritten by Theodore Ts'o to have a more
29  * thread-safe interface.
30  * \author
31  * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>.
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         memset(ctx, 0, sizeof (*ctx));
87
88         if (parse_config_file(ctx, _PATH_HESIOD_CONF) < 0) {
89 #ifdef DEF_RHS
90                 /*
91                  * Use compiled in defaults.
92                  */
93                 ctx->LHS = malloc(strlen(DEF_LHS) + 1);
94                 ctx->RHS = malloc(strlen(DEF_RHS) + 1);
95                 if (ctx->LHS == NULL || ctx->RHS == NULL) {
96                         errno = ENOMEM;
97                         goto cleanup;
98                 }
99                 strcpy(ctx->LHS, DEF_LHS);      /* (checked) */
100                 strcpy(ctx->RHS, DEF_RHS);      /* (checked) */
101 #else
102                 goto cleanup;
103 #endif
104         }
105         /*
106          * The default RHS can be overridden by an environment
107          * variable.
108          */
109         if ((cp = getenv("HES_DOMAIN")) != NULL) {
110                 size_t RHSlen = strlen(cp) + 2;
111                 if (ctx->RHS)
112                         free(ctx->RHS);
113                 ctx->RHS = malloc(RHSlen);
114                 if (!ctx->RHS) {
115                         errno = ENOMEM;
116                         goto cleanup;
117                 }
118                 if (cp[0] == '.') {
119                         strcpy(ctx->RHS, cp);   /* (checked) */
120                 } else {
121                         strcpy(ctx->RHS, ".");  /* (checked) */
122                         strcat(ctx->RHS, cp);   /* (checked) */
123                 }
124         }
125
126         /*
127          * If there is no default hesiod realm set, we return an
128          * error.
129          */
130         if (!ctx->RHS) {
131                 errno = ENOEXEC;
132                 goto cleanup;
133         }
134         
135 #if 0
136         if (res_ninit(ctx->res) < 0)
137                 goto cleanup;
138 #endif
139
140         *context = ctx;
141         return (0);
142
143  cleanup:
144         hesiod_end(ctx);
145         return (-1);
146 }
147
148 /*%
149  * This function deallocates the hesiod_p
150  */
151 void
152 hesiod_end(void *context) {
153         struct hesiod_p *ctx = (struct hesiod_p *) context;
154         int save_errno = errno;
155
156         if (ctx->res)
157                 res_nclose(ctx->res);
158         if (ctx->RHS)
159                 free(ctx->RHS);
160         if (ctx->LHS)
161                 free(ctx->LHS);
162         if (ctx->res && ctx->free_res)
163                 (*ctx->free_res)(ctx->res);
164         free(ctx);
165         errno = save_errno;
166 }
167
168 /*%
169  * This function takes a hesiod (name, type) and returns a DNS
170  * name which is to be resolved.
171  */
172 char *
173 hesiod_to_bind(void *context, const char *name, const char *type) {
174         struct hesiod_p *ctx = (struct hesiod_p *) context;
175         char *bindname;
176         char **rhs_list = NULL;
177         const char *RHS, *cp;
178
179         /* Decide what our RHS is, and set cp to the end of the actual name. */
180         if ((cp = strchr(name, '@')) != NULL) {
181                 if (strchr(cp + 1, '.'))
182                         RHS = cp + 1;
183                 else if ((rhs_list = hesiod_resolve(context, cp + 1,
184                     "rhs-extension")) != NULL)
185                         RHS = *rhs_list;
186                 else {
187                         errno = ENOENT;
188                         return (NULL);
189                 }
190         } else {
191                 RHS = ctx->RHS;
192                 cp = name + strlen(name);
193         }
194
195         /*
196          * Allocate the space we need, including up to three periods and
197          * the terminating NUL.
198          */
199         if ((bindname = malloc((cp - name) + strlen(type) + strlen(RHS) +
200             (ctx->LHS ? strlen(ctx->LHS) : 0) + 4)) == NULL) {
201                 errno = ENOMEM;
202                 if (rhs_list)
203                         hesiod_free_list(context, rhs_list);
204                 return NULL;
205         }
206
207         /* Now put together the DNS name. */
208         memcpy(bindname, name, cp - name);
209         bindname[cp - name] = '\0';
210         strcat(bindname, ".");
211         strcat(bindname, type);
212         if (ctx->LHS) {
213                 if (ctx->LHS[0] != '.')
214                         strcat(bindname, ".");
215                 strcat(bindname, ctx->LHS);
216         }
217         if (RHS[0] != '.')
218                 strcat(bindname, ".");
219         strcat(bindname, RHS);
220
221         if (rhs_list)
222                 hesiod_free_list(context, rhs_list);
223
224         return (bindname);
225 }
226
227 /*%
228  * This is the core function.  Given a hesiod (name, type), it
229  * returns an array of strings returned by the resolver.
230  */
231 char **
232 hesiod_resolve(void *context, const char *name, const char *type) {
233         struct hesiod_p *ctx = (struct hesiod_p *) context;
234         char *bindname = hesiod_to_bind(context, name, type);
235         char **retvec;
236
237         if (bindname == NULL)
238                 return (NULL);
239         if (init(ctx) == -1) {
240                 free(bindname);
241                 return (NULL);
242         }
243
244         if ((retvec = get_txt_records(ctx, C_IN, bindname))) {
245                 free(bindname);
246                 return (retvec);
247         }
248         
249         if (errno != ENOENT)
250                 return (NULL);
251
252         retvec = get_txt_records(ctx, C_HS, bindname);
253         free(bindname);
254         return (retvec);
255 }
256
257 void
258 hesiod_free_list(void *context, char **list) {
259         char **p;
260
261         UNUSED(context);
262
263         for (p = list; *p; p++)
264                 free(*p);
265         free(list);
266 }
267
268 /*%
269  * This function parses the /etc/hesiod.conf file
270  */
271 static int
272 parse_config_file(struct hesiod_p *ctx, const char *filename) {
273         char *key, *data, *cp, **cpp;
274         char buf[MAXDNAME+7];
275         FILE *fp;
276
277         /*
278          * Clear the existing configuration variable, just in case
279          * they're set.
280          */
281         if (ctx->RHS)
282                 free(ctx->RHS);
283         if (ctx->LHS)
284                 free(ctx->LHS);
285         ctx->RHS = ctx->LHS = 0;
286
287         /*
288          * Now open and parse the file...
289          */
290         if (!(fp = fopen(filename, "r")))
291                 return (-1);
292         
293         while (fgets(buf, sizeof(buf), fp) != NULL) {
294                 cp = buf;
295                 if (*cp == '#' || *cp == '\n' || *cp == '\r')
296                         continue;
297                 while(*cp == ' ' || *cp == '\t')
298                         cp++;
299                 key = cp;
300                 while(*cp != ' ' && *cp != '\t' && *cp != '=')
301                         cp++;
302                 *cp++ = '\0';
303                 
304                 while(*cp == ' ' || *cp == '\t' || *cp == '=')
305                         cp++;
306                 data = cp;
307                 while(*cp != ' ' && *cp != '\n' && *cp != '\r')
308                         cp++;
309                 *cp++ = '\0';
310
311                 if (strcmp(key, "lhs") == 0)
312                         cpp = &ctx->LHS;
313                 else if (strcmp(key, "rhs") == 0)
314                         cpp = &ctx->RHS;
315                 else
316                         continue;
317
318                 *cpp = malloc(strlen(data) + 1);
319                 if (!*cpp) {
320                         errno = ENOMEM;
321                         goto cleanup;
322                 }
323                 strcpy(*cpp, data);
324         }
325         fclose(fp);
326         return (0);
327         
328  cleanup:
329         fclose(fp);
330         if (ctx->RHS)
331                 free(ctx->RHS);
332         if (ctx->LHS)
333                 free(ctx->LHS);
334         ctx->RHS = ctx->LHS = 0;
335         return (-1);
336 }
337
338 /*%
339  * Given a DNS class and a DNS name, do a lookup for TXT records, and
340  * return a list of them.
341  */
342 static char **
343 get_txt_records(struct hesiod_p *ctx, int class, const char *name) {
344         struct {
345                 int type;               /*%< RR type */
346                 int class;              /*%< RR class */
347                 int dlen;               /*%< len of data section */
348                 u_char *data;           /*%< pointer to data */
349         } rr;
350         HEADER *hp;
351         u_char qbuf[MAX_HESRESP], abuf[MAX_HESRESP];
352         u_char *cp, *erdata, *eom;
353         char *dst, *edst, **list;
354         int ancount, qdcount;
355         int i, j, n, skip;
356
357         /*
358          * Construct the query and send it.
359          */
360         n = res_nmkquery(ctx->res, QUERY, name, class, T_TXT, NULL, 0,
361                          NULL, qbuf, MAX_HESRESP);
362         if (n < 0) {
363                 errno = EMSGSIZE;
364                 return (NULL);
365         }
366         n = res_nsend(ctx->res, qbuf, n, abuf, MAX_HESRESP);
367         if (n < 0) {
368                 errno = ECONNREFUSED;
369                 return (NULL);
370         }
371         if (n < HFIXEDSZ) {
372                 errno = EMSGSIZE;
373                 return (NULL);
374         }
375
376         /*
377          * OK, parse the result.
378          */
379         hp = (HEADER *) abuf;
380         ancount = ntohs(hp->ancount);
381         qdcount = ntohs(hp->qdcount);
382         cp = abuf + sizeof(HEADER);
383         eom = abuf + n;
384
385         /* Skip query, trying to get to the answer section which follows. */
386         for (i = 0; i < qdcount; i++) {
387                 skip = dn_skipname(cp, eom);
388                 if (skip < 0 || cp + skip + QFIXEDSZ > eom) {
389                         errno = EMSGSIZE;
390                         return (NULL);
391                 }
392                 cp += skip + QFIXEDSZ;
393         }
394
395         list = malloc((ancount + 1) * sizeof(char *));
396         if (!list) {
397                 errno = ENOMEM;
398                 return (NULL);
399         }
400         j = 0;
401         for (i = 0; i < ancount; i++) {
402                 skip = dn_skipname(cp, eom);
403                 if (skip < 0) {
404                         errno = EMSGSIZE;
405                         goto cleanup;
406                 }
407                 cp += skip;
408                 if (cp + 3 * INT16SZ + INT32SZ > eom) {
409                         errno = EMSGSIZE;
410                         goto cleanup;
411                 }
412                 rr.type = ns_get16(cp);
413                 cp += INT16SZ;
414                 rr.class = ns_get16(cp);
415                 cp += INT16SZ + INT32SZ;        /*%< skip the ttl, too */
416                 rr.dlen = ns_get16(cp);
417                 cp += INT16SZ;
418                 if (cp + rr.dlen > eom) {
419                         errno = EMSGSIZE;
420                         goto cleanup;
421                 }
422                 rr.data = cp;
423                 cp += rr.dlen;
424                 if (rr.class != class || rr.type != T_TXT)
425                         continue;
426                 if (!(list[j] = malloc(rr.dlen)))
427                         goto cleanup;
428                 dst = list[j++];
429                 edst = dst + rr.dlen;
430                 erdata = rr.data + rr.dlen;
431                 cp = rr.data;
432                 while (cp < erdata) {
433                         n = (unsigned char) *cp++;
434                         if (cp + n > eom || dst + n > edst) {
435                                 errno = EMSGSIZE;
436                                 goto cleanup;
437                         }
438                         memcpy(dst, cp, n);
439                         cp += n;
440                         dst += n;
441                 }
442                 if (cp != erdata) {
443                         errno = EMSGSIZE;
444                         goto cleanup;
445                 }
446                 *dst = '\0';
447         }
448         list[j] = NULL;
449         if (j == 0) {
450                 errno = ENOENT;
451                 goto cleanup;
452         }
453         return (list);
454
455  cleanup:
456         for (i = 0; i < j; i++)
457                 free(list[i]);
458         free(list);
459         return (NULL);
460 }
461
462 struct __res_state *
463 __hesiod_res_get(void *context) {
464         struct hesiod_p *ctx = context;
465
466         if (!ctx->res) {
467                 struct __res_state *res;
468                 res = (struct __res_state *)malloc(sizeof *res);
469                 if (res == NULL) {
470                         errno = ENOMEM;
471                         return (NULL);
472                 }
473                 memset(res, 0, sizeof *res);
474                 __hesiod_res_set(ctx, res, free);
475         }
476
477         return (ctx->res);
478 }
479
480 void
481 __hesiod_res_set(void *context, struct __res_state *res,
482                  void (*free_res)(void *)) {
483         struct hesiod_p *ctx = context;
484
485         if (ctx->res && ctx->free_res) {
486                 res_nclose(ctx->res);
487                 (*ctx->free_res)(ctx->res);
488         }
489
490         ctx->res = res;
491         ctx->free_res = free_res;
492 }
493
494 static int
495 init(struct hesiod_p *ctx) {
496         
497         if (!ctx->res && !__hesiod_res_get(ctx))
498                 return (-1);
499
500         if (((ctx->res->options & RES_INIT) == 0U) &&
501             (res_ninit(ctx->res) == -1))
502                 return (-1);
503
504         return (0);
505 }