]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/net/hesiod.c
sqlite3: Vendor import of sqlite3 3.41.2
[FreeBSD/FreeBSD.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/param.h>
55 #include <netinet/in.h>
56 #include <arpa/nameser.h>
57
58 #include <ctype.h>
59 #include <errno.h>
60 #include <hesiod.h>
61 #include <resolv.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66
67 struct hesiod_p {
68         char    *lhs;                   /* normally ".ns" */
69         char    *rhs;                   /* AKA the default hesiod domain */
70         int      classes[2];            /* The class search order. */
71 };
72
73 #define MAX_HESRESP     1024
74
75 static int        read_config_file(struct hesiod_p *, const char *);
76 static char     **get_txt_records(int, const char *);
77 static int        init_context(void);
78 static void       translate_errors(void);
79
80
81 /*
82  * hesiod_init --
83  *      initialize a hesiod_p.
84  */
85 int 
86 hesiod_init(context)
87         void    **context;
88 {
89         struct hesiod_p *ctx;
90         const char      *p, *configname;
91
92         ctx = malloc(sizeof(struct hesiod_p));
93         if (ctx) {
94                 *context = ctx;
95                 configname = secure_getenv("HESIOD_CONFIG");
96                 if (!configname)
97                         configname = _PATH_HESIOD_CONF;
98                 if (read_config_file(ctx, configname) >= 0) {
99                         /*
100                          * The default rhs can be overridden by an
101                          * environment variable.
102                          */
103                         p = secure_getenv("HES_DOMAIN");
104                         if (p) {
105                                 if (ctx->rhs)
106                                         free(ctx->rhs);
107                                 ctx->rhs = malloc(strlen(p) + 2);
108                                 if (ctx->rhs) {
109                                         *ctx->rhs = '.';
110                                         strcpy(ctx->rhs + 1,
111                                             (*p == '.') ? p + 1 : p);
112                                         return 0;
113                                 } else
114                                         errno = ENOMEM;
115                         } else
116                                 return 0;
117                 }
118         } else
119                 errno = ENOMEM;
120
121         if (ctx->lhs)
122                 free(ctx->lhs);
123         if (ctx->rhs)
124                 free(ctx->rhs);
125         if (ctx)
126                 free(ctx);
127         return -1;
128 }
129
130 /*
131  * hesiod_end --
132  *      Deallocates the hesiod_p.
133  */
134 void 
135 hesiod_end(context)
136         void    *context;
137 {
138         struct hesiod_p *ctx = (struct hesiod_p *) context;
139
140         free(ctx->rhs);
141         if (ctx->lhs)
142                 free(ctx->lhs);
143         free(ctx);
144 }
145
146 /*
147  * hesiod_to_bind --
148  *      takes a hesiod (name, type) and returns a DNS
149  *      name which is to be resolved.
150  */
151 char *
152 hesiod_to_bind(void *context, const char *name, const char *type)
153 {
154         struct hesiod_p *ctx = (struct hesiod_p *) context;
155         char             bindname[MAXDNAME], *p, *ret, **rhs_list = NULL;
156         const char      *rhs;
157         int              len;
158
159         if (strlcpy(bindname, name, sizeof(bindname)) >= sizeof(bindname)) {
160                 errno = EMSGSIZE;
161                 return NULL;
162         }
163
164                 /*
165                  * Find the right right hand side to use, possibly
166                  * truncating bindname.
167                  */
168         p = strchr(bindname, '@');
169         if (p) {
170                 *p++ = 0;
171                 if (strchr(p, '.'))
172                         rhs = name + (p - bindname);
173                 else {
174                         rhs_list = hesiod_resolve(context, p, "rhs-extension");
175                         if (rhs_list)
176                                 rhs = *rhs_list;
177                         else {
178                                 errno = ENOENT;
179                                 return NULL;
180                         }
181                 }
182         } else
183                 rhs = ctx->rhs;
184
185                 /* See if we have enough room. */
186         len = strlen(bindname) + 1 + strlen(type);
187         if (ctx->lhs)
188                 len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0);
189         len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0);
190         if (len > sizeof(bindname) - 1) {
191                 if (rhs_list)
192                         hesiod_free_list(context, rhs_list);
193                 errno = EMSGSIZE;
194                 return NULL;
195         }
196                 /* Put together the rest of the domain. */
197         strcat(bindname, ".");
198         strcat(bindname, type);
199                 /* Only append lhs if it isn't empty. */
200         if (ctx->lhs && ctx->lhs[0] != '\0' ) {
201                 if (ctx->lhs[0] != '.')
202                         strcat(bindname, ".");
203                 strcat(bindname, ctx->lhs);
204         }
205         if (rhs[0] != '.')
206                 strcat(bindname, ".");
207         strcat(bindname, rhs);
208
209                 /* rhs_list is no longer needed, since we're done with rhs. */
210         if (rhs_list)
211                 hesiod_free_list(context, rhs_list);
212
213                 /* Make a copy of the result and return it to the caller. */
214         ret = strdup(bindname);
215         if (!ret)
216                 errno = ENOMEM;
217         return ret;
218 }
219
220 /*
221  * hesiod_resolve --
222  *      Given a hesiod name and type, return an array of strings returned
223  *      by the resolver.
224  */
225 char **
226 hesiod_resolve(context, name, type)
227         void            *context;
228         const char      *name;
229         const char      *type;
230 {
231         struct hesiod_p *ctx = (struct hesiod_p *) context;
232         char            *bindname, **retvec;
233
234         bindname = hesiod_to_bind(context, name, type);
235         if (!bindname)
236                 return NULL;
237
238         retvec = get_txt_records(ctx->classes[0], bindname);
239         if (retvec == NULL && errno == ENOENT && ctx->classes[1])
240                 retvec = get_txt_records(ctx->classes[1], bindname);
241
242         free(bindname);
243         return retvec;
244 }
245
246 /*ARGSUSED*/
247 void 
248 hesiod_free_list(context, list)
249         void     *context;
250         char    **list;
251 {
252         char  **p;
253
254         if (list == NULL)
255                 return;
256         for (p = list; *p; p++)
257                 free(*p);
258         free(list);
259 }
260
261
262 /* read_config_file --
263  *      Parse the /etc/hesiod.conf file.  Returns 0 on success,
264  *      -1 on failure.  On failure, it might leave values in ctx->lhs
265  *      or ctx->rhs which need to be freed by the caller.
266  */
267 static int 
268 read_config_file(ctx, filename)
269         struct hesiod_p *ctx;
270         const char      *filename;
271 {
272         char    *key, *data, *p, **which;
273         char     buf[MAXDNAME + 7];
274         int      n;
275         FILE    *fp;
276
277                 /* Set default query classes. */
278         ctx->classes[0] = C_IN;
279         ctx->classes[1] = C_HS;
280
281                 /* Try to open the configuration file. */
282         fp = fopen(filename, "re");
283         if (!fp) {
284                 /* Use compiled in default domain names. */
285                 ctx->lhs = strdup(DEF_LHS);
286                 ctx->rhs = strdup(DEF_RHS);
287                 if (ctx->lhs && ctx->rhs)
288                         return 0;
289                 else {
290                         errno = ENOMEM;
291                         return -1;
292                 }
293         }
294         ctx->lhs = NULL;
295         ctx->rhs = NULL;
296         while (fgets(buf, sizeof(buf), fp) != NULL) {
297                 p = buf;
298                 if (*p == '#' || *p == '\n' || *p == '\r')
299                         continue;
300                 while (*p == ' ' || *p == '\t')
301                         p++;
302                 key = p;
303                 while (*p != ' ' && *p != '\t' && *p != '=')
304                         p++;
305                 *p++ = 0;
306
307                 while (isspace(*p) || *p == '=')
308                         p++;
309                 data = p;
310                 while (!isspace(*p))
311                         p++;
312                 *p = 0;
313
314                 if (strcasecmp(key, "lhs") == 0 ||
315                     strcasecmp(key, "rhs") == 0) {
316                         which = (strcasecmp(key, "lhs") == 0)
317                             ? &ctx->lhs : &ctx->rhs;
318                         *which = strdup(data);
319                         if (!*which) {
320                                 fclose(fp);
321                                 errno = ENOMEM;
322                                 return -1;
323                         }
324                 } else {
325                         if (strcasecmp(key, "classes") == 0) {
326                                 n = 0;
327                                 while (*data && n < 2) {
328                                         p = data;
329                                         while (*p && *p != ',')
330                                                 p++;
331                                         if (*p)
332                                                 *p++ = 0;
333                                         if (strcasecmp(data, "IN") == 0)
334                                                 ctx->classes[n++] = C_IN;
335                                         else
336                                                 if (strcasecmp(data, "HS") == 0)
337                                                         ctx->classes[n++] =
338                                                             C_HS;
339                                         data = p;
340                                 }
341                                 while (n < 2)
342                                         ctx->classes[n++] = 0;
343                         }
344                 }
345         }
346         fclose(fp);
347
348         if (!ctx->rhs || ctx->classes[0] == 0 ||
349             ctx->classes[0] == ctx->classes[1]) {
350                 errno = ENOEXEC;
351                 return -1;
352         }
353         return 0;
354 }
355
356 /*
357  * get_txt_records --
358  *      Given a DNS class and a DNS name, do a lookup for TXT records, and
359  *      return a list of them.
360  */
361 static char **
362 get_txt_records(qclass, name)
363         int              qclass;
364         const char      *name;
365 {
366         HEADER          *hp;
367         unsigned char    qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor;
368         char            *dst, **list;
369         int              ancount, qdcount, i, j, n, skip, type, class, len;
370
371                 /* Make sure the resolver is initialized. */
372         if ((_res.options & RES_INIT) == 0 && res_init() == -1)
373                 return NULL;
374
375                 /* Construct the query. */
376         n = res_mkquery(QUERY, name, qclass, T_TXT, NULL, 0,
377             NULL, qbuf, PACKETSZ);
378         if (n < 0)
379                 return NULL;
380
381                 /* Send the query. */
382         n = res_send(qbuf, n, abuf, MAX_HESRESP);
383         if (n < 0 || n > MAX_HESRESP) {
384                 errno = ECONNREFUSED; /* XXX */
385                 return NULL;
386         }
387                 /* Parse the header of the result. */
388         hp = (HEADER *) (void *) abuf;
389         ancount = ntohs(hp->ancount);
390         qdcount = ntohs(hp->qdcount);
391         p = abuf + sizeof(HEADER);
392         eom = abuf + n;
393
394                 /*
395                  * Skip questions, trying to get to the answer section
396                  * which follows.
397                  */
398         for (i = 0; i < qdcount; i++) {
399                 skip = dn_skipname(p, eom);
400                 if (skip < 0 || p + skip + QFIXEDSZ > eom) {
401                         errno = EMSGSIZE;
402                         return NULL;
403                 }
404                 p += skip + QFIXEDSZ;
405         }
406
407                 /* Allocate space for the text record answers. */
408         list = malloc((ancount + 1) * sizeof(char *));
409         if (!list) {
410                 errno = ENOMEM;
411                 return NULL;
412         }
413                 /* Parse the answers. */
414         j = 0;
415         for (i = 0; i < ancount; i++) {
416                 /* Parse the header of this answer. */
417                 skip = dn_skipname(p, eom);
418                 if (skip < 0 || p + skip + 10 > eom)
419                         break;
420                 type = p[skip + 0] << 8 | p[skip + 1];
421                 class = p[skip + 2] << 8 | p[skip + 3];
422                 len = p[skip + 8] << 8 | p[skip + 9];
423                 p += skip + 10;
424                 if (p + len > eom) {
425                         errno = EMSGSIZE;
426                         break;
427                 }
428                 /* Skip entries of the wrong class and type. */
429                 if (class != qclass || type != T_TXT) {
430                         p += len;
431                         continue;
432                 }
433                 /* Allocate space for this answer. */
434                 list[j] = malloc((size_t)len);
435                 if (!list[j]) {
436                         errno = ENOMEM;
437                         break;
438                 }
439                 dst = list[j++];
440
441                 /* Copy answer data into the allocated area. */
442                 eor = p + len;
443                 while (p < eor) {
444                         n = (unsigned char) *p++;
445                         if (p + n > eor) {
446                                 errno = EMSGSIZE;
447                                 break;
448                         }
449                         memcpy(dst, p, (size_t)n);
450                         p += n;
451                         dst += n;
452                 }
453                 if (p < eor) {
454                         errno = EMSGSIZE;
455                         break;
456                 }
457                 *dst = 0;
458         }
459
460                 /*
461                  * If we didn't terminate the loop normally, something
462                  * went wrong.
463                  */
464         if (i < ancount) {
465                 for (i = 0; i < j; i++)
466                         free(list[i]);
467                 free(list);
468                 return NULL;
469         }
470         if (j == 0) {
471                 errno = ENOENT;
472                 free(list);
473                 return NULL;
474         }
475         list[j] = NULL;
476         return list;
477 }
478
479                 /*
480                  *      COMPATIBILITY FUNCTIONS
481                  */
482
483 static int        inited = 0;
484 static void      *context;
485 static int        errval = HES_ER_UNINIT;
486
487 int
488 hes_init()
489 {
490         init_context();
491         return errval;
492 }
493
494 char *
495 hes_to_bind(name, type)
496         const char      *name;
497         const char      *type;
498 {
499         static  char    *bindname;
500         if (init_context() < 0)
501                 return NULL;
502         if (bindname)
503                 free(bindname);
504         bindname = hesiod_to_bind(context, name, type);
505         if (!bindname)
506                 translate_errors();
507         return bindname;
508 }
509
510 char **
511 hes_resolve(name, type)
512         const char      *name;
513         const char      *type;
514 {
515         static char     **list;
516
517         if (init_context() < 0)
518                 return NULL;
519
520         /*
521          * In the old Hesiod interface, the caller was responsible for
522          * freeing the returned strings but not the vector of strings itself.
523          */
524         if (list)
525                 free(list);
526
527         list = hesiod_resolve(context, name, type);
528         if (!list)
529                 translate_errors();
530         return list;
531 }
532
533 int
534 hes_error()
535 {
536         return errval;
537 }
538
539 void
540 hes_free(hp)
541         char **hp;
542 {
543         hesiod_free_list(context, hp);
544 }
545
546 static int
547 init_context()
548 {
549         if (!inited) {
550                 inited = 1;
551                 if (hesiod_init(&context) < 0) {
552                         errval = HES_ER_CONFIG;
553                         return -1;
554                 }
555                 errval = HES_ER_OK;
556         }
557         return 0;
558 }
559
560 static void
561 translate_errors()
562 {
563         switch (errno) {
564         case ENOENT:
565                 errval = HES_ER_NOTFOUND;
566                 break;
567         case ECONNREFUSED:
568         case EMSGSIZE:
569                 errval = HES_ER_NET;
570                 break;
571         case ENOMEM:
572         default:
573                 /* Not a good match, but the best we can do. */
574                 errval = HES_ER_CONFIG;
575                 break;
576         }
577 }