]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libc/net/hesiod.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / libc / net / hesiod.c
1 /*      $NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $        */
2
3 /* Copyright (c) 1996 by Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
10  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
11  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
12  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
15  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
16  * SOFTWARE.
17  */
18
19 /* Copyright 1996 by the Massachusetts Institute of Technology.
20  *
21  * Permission to use, copy, modify, and distribute this
22  * software and its documentation for any purpose and without
23  * fee is hereby granted, provided that the above copyright
24  * notice appear in all copies and that both that copyright
25  * notice and this permission notice appear in supporting
26  * documentation, and that the name of M.I.T. not be used in
27  * advertising or publicity pertaining to distribution of the
28  * software without specific, written prior permission.
29  * M.I.T. makes no representations about the suitability of
30  * this software for any purpose.  It is provided "as is"
31  * without express or implied warranty.
32  */
33
34 /* This file is part of the hesiod library.  It implements the core
35  * portion of the hesiod resolver.
36  *
37  * This file is loosely based on an interim version of hesiod.c from
38  * the BIND IRS library, which was in turn based on an earlier version
39  * of this file.  Extensive changes have been made on each step of the
40  * path.
41  *
42  * This implementation is not truly thread-safe at the moment because
43  * it uses res_send() and accesses _res.
44  */
45
46 #include <sys/cdefs.h>
47
48 #if 0
49 static char *orig_rcsid = "$NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $";
50 #endif
51 #include <sys/cdefs.h>
52 __FBSDID("$FreeBSD$");
53
54 #include <sys/types.h>
55 #include <sys/param.h>
56 #include <netinet/in.h>
57 #include <arpa/nameser.h>
58
59 #include <ctype.h>
60 #include <errno.h>
61 #include <hesiod.h>
62 #include <resolv.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67
68 struct hesiod_p {
69         char    *lhs;                   /* normally ".ns" */
70         char    *rhs;                   /* AKA the default hesiod domain */
71         int      classes[2];            /* The class search order. */
72 };
73
74 #define MAX_HESRESP     1024
75
76 static int        read_config_file(struct hesiod_p *, const char *);
77 static char     **get_txt_records(int, const char *);
78 static int        init_context(void);
79 static void       translate_errors(void);
80
81
82 /*
83  * hesiod_init --
84  *      initialize a hesiod_p.
85  */
86 int 
87 hesiod_init(context)
88         void    **context;
89 {
90         struct hesiod_p *ctx;
91         const char      *p, *configname;
92
93         ctx = malloc(sizeof(struct hesiod_p));
94         if (ctx) {
95                 *context = ctx;
96                 if (!issetugid())
97                         configname = getenv("HESIOD_CONFIG");
98                 else
99                         configname = NULL;
100                 if (!configname)
101                         configname = _PATH_HESIOD_CONF;
102                 if (read_config_file(ctx, configname) >= 0) {
103                         /*
104                          * The default rhs can be overridden by an
105                          * environment variable.
106                          */
107                         if (!issetugid())
108                                 p = getenv("HES_DOMAIN");
109                         else
110                                 p = NULL;
111                         if (p) {
112                                 if (ctx->rhs)
113                                         free(ctx->rhs);
114                                 ctx->rhs = malloc(strlen(p) + 2);
115                                 if (ctx->rhs) {
116                                         *ctx->rhs = '.';
117                                         strcpy(ctx->rhs + 1,
118                                             (*p == '.') ? p + 1 : p);
119                                         return 0;
120                                 } else
121                                         errno = ENOMEM;
122                         } else
123                                 return 0;
124                 }
125         } else
126                 errno = ENOMEM;
127
128         if (ctx->lhs)
129                 free(ctx->lhs);
130         if (ctx->rhs)
131                 free(ctx->rhs);
132         if (ctx)
133                 free(ctx);
134         return -1;
135 }
136
137 /*
138  * hesiod_end --
139  *      Deallocates the hesiod_p.
140  */
141 void 
142 hesiod_end(context)
143         void    *context;
144 {
145         struct hesiod_p *ctx = (struct hesiod_p *) context;
146
147         free(ctx->rhs);
148         if (ctx->lhs)
149                 free(ctx->lhs);
150         free(ctx);
151 }
152
153 /*
154  * hesiod_to_bind --
155  *      takes a hesiod (name, type) and returns a DNS
156  *      name which is to be resolved.
157  */
158 char *
159 hesiod_to_bind(void *context, const char *name, const char *type)
160 {
161         struct hesiod_p *ctx = (struct hesiod_p *) context;
162         char             bindname[MAXDNAME], *p, *ret, **rhs_list = NULL;
163         const char      *rhs;
164         int              len;
165
166         if (strlcpy(bindname, name, sizeof(bindname)) >= sizeof(bindname)) {
167                 errno = EMSGSIZE;
168                 return NULL;
169         }
170
171                 /*
172                  * Find the right right hand side to use, possibly
173                  * truncating bindname.
174                  */
175         p = strchr(bindname, '@');
176         if (p) {
177                 *p++ = 0;
178                 if (strchr(p, '.'))
179                         rhs = name + (p - bindname);
180                 else {
181                         rhs_list = hesiod_resolve(context, p, "rhs-extension");
182                         if (rhs_list)
183                                 rhs = *rhs_list;
184                         else {
185                                 errno = ENOENT;
186                                 return NULL;
187                         }
188                 }
189         } else
190                 rhs = ctx->rhs;
191
192                 /* See if we have enough room. */
193         len = strlen(bindname) + 1 + strlen(type);
194         if (ctx->lhs)
195                 len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0);
196         len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0);
197         if (len > sizeof(bindname) - 1) {
198                 if (rhs_list)
199                         hesiod_free_list(context, rhs_list);
200                 errno = EMSGSIZE;
201                 return NULL;
202         }
203                 /* Put together the rest of the domain. */
204         strcat(bindname, ".");
205         strcat(bindname, type);
206                 /* Only append lhs if it isn't empty. */
207         if (ctx->lhs && ctx->lhs[0] != '\0' ) {
208                 if (ctx->lhs[0] != '.')
209                         strcat(bindname, ".");
210                 strcat(bindname, ctx->lhs);
211         }
212         if (rhs[0] != '.')
213                 strcat(bindname, ".");
214         strcat(bindname, rhs);
215
216                 /* rhs_list is no longer needed, since we're done with rhs. */
217         if (rhs_list)
218                 hesiod_free_list(context, rhs_list);
219
220                 /* Make a copy of the result and return it to the caller. */
221         ret = strdup(bindname);
222         if (!ret)
223                 errno = ENOMEM;
224         return ret;
225 }
226
227 /*
228  * hesiod_resolve --
229  *      Given a hesiod name and type, return an array of strings returned
230  *      by the resolver.
231  */
232 char **
233 hesiod_resolve(context, name, type)
234         void            *context;
235         const char      *name;
236         const char      *type;
237 {
238         struct hesiod_p *ctx = (struct hesiod_p *) context;
239         char            *bindname, **retvec;
240
241         bindname = hesiod_to_bind(context, name, type);
242         if (!bindname)
243                 return NULL;
244
245         retvec = get_txt_records(ctx->classes[0], bindname);
246         if (retvec == NULL && errno == ENOENT && ctx->classes[1])
247                 retvec = get_txt_records(ctx->classes[1], bindname);
248
249         free(bindname);
250         return retvec;
251 }
252
253 /*ARGSUSED*/
254 void 
255 hesiod_free_list(context, list)
256         void     *context;
257         char    **list;
258 {
259         char  **p;
260
261         if (list == NULL)
262                 return;
263         for (p = list; *p; p++)
264                 free(*p);
265         free(list);
266 }
267
268
269 /* read_config_file --
270  *      Parse the /etc/hesiod.conf file.  Returns 0 on success,
271  *      -1 on failure.  On failure, it might leave values in ctx->lhs
272  *      or ctx->rhs which need to be freed by the caller.
273  */
274 static int 
275 read_config_file(ctx, filename)
276         struct hesiod_p *ctx;
277         const char      *filename;
278 {
279         char    *key, *data, *p, **which;
280         char     buf[MAXDNAME + 7];
281         int      n;
282         FILE    *fp;
283
284                 /* Set default query classes. */
285         ctx->classes[0] = C_IN;
286         ctx->classes[1] = C_HS;
287
288                 /* Try to open the configuration file. */
289         fp = fopen(filename, "re");
290         if (!fp) {
291                 /* Use compiled in default domain names. */
292                 ctx->lhs = strdup(DEF_LHS);
293                 ctx->rhs = strdup(DEF_RHS);
294                 if (ctx->lhs && ctx->rhs)
295                         return 0;
296                 else {
297                         errno = ENOMEM;
298                         return -1;
299                 }
300         }
301         ctx->lhs = NULL;
302         ctx->rhs = NULL;
303         while (fgets(buf, sizeof(buf), fp) != NULL) {
304                 p = buf;
305                 if (*p == '#' || *p == '\n' || *p == '\r')
306                         continue;
307                 while (*p == ' ' || *p == '\t')
308                         p++;
309                 key = p;
310                 while (*p != ' ' && *p != '\t' && *p != '=')
311                         p++;
312                 *p++ = 0;
313
314                 while (isspace(*p) || *p == '=')
315                         p++;
316                 data = p;
317                 while (!isspace(*p))
318                         p++;
319                 *p = 0;
320
321                 if (strcasecmp(key, "lhs") == 0 ||
322                     strcasecmp(key, "rhs") == 0) {
323                         which = (strcasecmp(key, "lhs") == 0)
324                             ? &ctx->lhs : &ctx->rhs;
325                         *which = strdup(data);
326                         if (!*which) {
327                                 fclose(fp);
328                                 errno = ENOMEM;
329                                 return -1;
330                         }
331                 } else {
332                         if (strcasecmp(key, "classes") == 0) {
333                                 n = 0;
334                                 while (*data && n < 2) {
335                                         p = data;
336                                         while (*p && *p != ',')
337                                                 p++;
338                                         if (*p)
339                                                 *p++ = 0;
340                                         if (strcasecmp(data, "IN") == 0)
341                                                 ctx->classes[n++] = C_IN;
342                                         else
343                                                 if (strcasecmp(data, "HS") == 0)
344                                                         ctx->classes[n++] =
345                                                             C_HS;
346                                         data = p;
347                                 }
348                                 while (n < 2)
349                                         ctx->classes[n++] = 0;
350                         }
351                 }
352         }
353         fclose(fp);
354
355         if (!ctx->rhs || ctx->classes[0] == 0 ||
356             ctx->classes[0] == ctx->classes[1]) {
357                 errno = ENOEXEC;
358                 return -1;
359         }
360         return 0;
361 }
362
363 /*
364  * get_txt_records --
365  *      Given a DNS class and a DNS name, do a lookup for TXT records, and
366  *      return a list of them.
367  */
368 static char **
369 get_txt_records(qclass, name)
370         int              qclass;
371         const char      *name;
372 {
373         HEADER          *hp;
374         unsigned char    qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor;
375         char            *dst, **list;
376         int              ancount, qdcount, i, j, n, skip, type, class, len;
377
378                 /* Make sure the resolver is initialized. */
379         if ((_res.options & RES_INIT) == 0 && res_init() == -1)
380                 return NULL;
381
382                 /* Construct the query. */
383         n = res_mkquery(QUERY, name, qclass, T_TXT, NULL, 0,
384             NULL, qbuf, PACKETSZ);
385         if (n < 0)
386                 return NULL;
387
388                 /* Send the query. */
389         n = res_send(qbuf, n, abuf, MAX_HESRESP);
390         if (n < 0 || n > MAX_HESRESP) {
391                 errno = ECONNREFUSED; /* XXX */
392                 return NULL;
393         }
394                 /* Parse the header of the result. */
395         hp = (HEADER *) (void *) abuf;
396         ancount = ntohs(hp->ancount);
397         qdcount = ntohs(hp->qdcount);
398         p = abuf + sizeof(HEADER);
399         eom = abuf + n;
400
401                 /*
402                  * Skip questions, trying to get to the answer section
403                  * which follows.
404                  */
405         for (i = 0; i < qdcount; i++) {
406                 skip = dn_skipname(p, eom);
407                 if (skip < 0 || p + skip + QFIXEDSZ > eom) {
408                         errno = EMSGSIZE;
409                         return NULL;
410                 }
411                 p += skip + QFIXEDSZ;
412         }
413
414                 /* Allocate space for the text record answers. */
415         list = malloc((ancount + 1) * sizeof(char *));
416         if (!list) {
417                 errno = ENOMEM;
418                 return NULL;
419         }
420                 /* Parse the answers. */
421         j = 0;
422         for (i = 0; i < ancount; i++) {
423                 /* Parse the header of this answer. */
424                 skip = dn_skipname(p, eom);
425                 if (skip < 0 || p + skip + 10 > eom)
426                         break;
427                 type = p[skip + 0] << 8 | p[skip + 1];
428                 class = p[skip + 2] << 8 | p[skip + 3];
429                 len = p[skip + 8] << 8 | p[skip + 9];
430                 p += skip + 10;
431                 if (p + len > eom) {
432                         errno = EMSGSIZE;
433                         break;
434                 }
435                 /* Skip entries of the wrong class and type. */
436                 if (class != qclass || type != T_TXT) {
437                         p += len;
438                         continue;
439                 }
440                 /* Allocate space for this answer. */
441                 list[j] = malloc((size_t)len);
442                 if (!list[j]) {
443                         errno = ENOMEM;
444                         break;
445                 }
446                 dst = list[j++];
447
448                 /* Copy answer data into the allocated area. */
449                 eor = p + len;
450                 while (p < eor) {
451                         n = (unsigned char) *p++;
452                         if (p + n > eor) {
453                                 errno = EMSGSIZE;
454                                 break;
455                         }
456                         memcpy(dst, p, (size_t)n);
457                         p += n;
458                         dst += n;
459                 }
460                 if (p < eor) {
461                         errno = EMSGSIZE;
462                         break;
463                 }
464                 *dst = 0;
465         }
466
467                 /*
468                  * If we didn't terminate the loop normally, something
469                  * went wrong.
470                  */
471         if (i < ancount) {
472                 for (i = 0; i < j; i++)
473                         free(list[i]);
474                 free(list);
475                 return NULL;
476         }
477         if (j == 0) {
478                 errno = ENOENT;
479                 free(list);
480                 return NULL;
481         }
482         list[j] = NULL;
483         return list;
484 }
485
486                 /*
487                  *      COMPATIBILITY FUNCTIONS
488                  */
489
490 static int        inited = 0;
491 static void      *context;
492 static int        errval = HES_ER_UNINIT;
493
494 int
495 hes_init()
496 {
497         init_context();
498         return errval;
499 }
500
501 char *
502 hes_to_bind(name, type)
503         const char      *name;
504         const char      *type;
505 {
506         static  char    *bindname;
507         if (init_context() < 0)
508                 return NULL;
509         if (bindname)
510                 free(bindname);
511         bindname = hesiod_to_bind(context, name, type);
512         if (!bindname)
513                 translate_errors();
514         return bindname;
515 }
516
517 char **
518 hes_resolve(name, type)
519         const char      *name;
520         const char      *type;
521 {
522         static char     **list;
523
524         if (init_context() < 0)
525                 return NULL;
526
527         /*
528          * In the old Hesiod interface, the caller was responsible for
529          * freeing the returned strings but not the vector of strings itself.
530          */
531         if (list)
532                 free(list);
533
534         list = hesiod_resolve(context, name, type);
535         if (!list)
536                 translate_errors();
537         return list;
538 }
539
540 int
541 hes_error()
542 {
543         return errval;
544 }
545
546 void
547 hes_free(hp)
548         char **hp;
549 {
550         hesiod_free_list(context, hp);
551 }
552
553 static int
554 init_context()
555 {
556         if (!inited) {
557                 inited = 1;
558                 if (hesiod_init(&context) < 0) {
559                         errval = HES_ER_CONFIG;
560                         return -1;
561                 }
562                 errval = HES_ER_OK;
563         }
564         return 0;
565 }
566
567 static void
568 translate_errors()
569 {
570         switch (errno) {
571         case ENOENT:
572                 errval = HES_ER_NOTFOUND;
573                 break;
574         case ECONNREFUSED:
575         case EMSGSIZE:
576                 errval = HES_ER_NET;
577                 break;
578         case ENOMEM:
579         default:
580                 /* Not a good match, but the best we can do. */
581                 errval = HES_ER_CONFIG;
582                 break;
583         }
584 }