Back to index

glibc  2.9
hesiod.c
Go to the documentation of this file.
00001 #if defined(LIBC_SCCS) && !defined(lint)
00002 static const char rcsid[] = "$BINDId: hesiod.c,v 1.21 2000/02/28 14:51:08 vixie Exp $";
00003 #endif
00004 
00005 /*
00006  * Copyright (c) 1996,1999 by Internet Software Consortium.
00007  *
00008  * Permission to use, copy, modify, and distribute this software for any
00009  * purpose with or without fee is hereby granted, provided that the above
00010  * copyright notice and this permission notice appear in all copies.
00011  *
00012  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
00013  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
00014  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
00015  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
00016  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
00017  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
00018  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
00019  * SOFTWARE.
00020  */
00021 
00022 /*
00023  * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>.
00024  */
00025 
00026 /*
00027  * hesiod.c --- the core portion of the hesiod resolver.
00028  *
00029  * This file is derived from the hesiod library from Project Athena;
00030  * It has been extensively rewritten by Theodore Ts'o to have a more
00031  * thread-safe interface.
00032  */
00033 
00034 /* Imports */
00035 
00036 #include <sys/types.h>
00037 #include <netinet/in.h>
00038 #include <arpa/nameser.h>
00039 
00040 #include <errno.h>
00041 #include <netdb.h>
00042 #include <resolv.h>
00043 #include <stdio.h>
00044 #include <stdlib.h>
00045 #include <string.h>
00046 
00047 #include "hesiod.h"
00048 #include "hesiod_p.h"
00049 #undef DEF_RHS
00050 
00051 #define _PATH_HESIOD_CONF "/etc/hesiod.conf"
00052 
00053 /* Forward */
00054 
00055 int           hesiod_init(void **context);
00056 void          hesiod_end(void *context);
00057 char *        hesiod_to_bind(void *context, const char *name,
00058                             const char *type);
00059 char **              hesiod_resolve(void *context, const char *name,
00060                             const char *type);
00061 void          hesiod_free_list(void *context, char **list);
00062 
00063 static int    parse_config_file(struct hesiod_p *ctx, const char *filename);
00064 static char **       get_txt_records(struct hesiod_p *ctx, int class,
00065                             const char *name);
00066 static int    init(struct hesiod_p *ctx);
00067 
00068 /* Public */
00069 
00070 /*
00071  * This function is called to initialize a hesiod_p.
00072  */
00073 int
00074 hesiod_init(void **context) {
00075        struct hesiod_p *ctx;
00076        const char *configname;
00077        char *cp;
00078 
00079        ctx = malloc(sizeof(struct hesiod_p));
00080        if (ctx == 0)
00081               return (-1);
00082 
00083        ctx->LHS = NULL;
00084        ctx->RHS = NULL;
00085        ctx->res = NULL;
00086        /* Set default query classes. */
00087        ctx->classes[0] = C_IN;
00088        ctx->classes[1] = C_HS;
00089 
00090        configname = __secure_getenv("HESIOD_CONFIG");
00091        if (!configname)
00092          configname = _PATH_HESIOD_CONF;
00093        if (parse_config_file(ctx, configname) < 0) {
00094 #ifdef DEF_RHS
00095               /*
00096                * Use compiled in defaults.
00097                */
00098               ctx->LHS = malloc(strlen(DEF_LHS)+1);
00099               ctx->RHS = malloc(strlen(DEF_RHS)+1);
00100               if (ctx->LHS == 0 || ctx->RHS == 0)
00101                      goto cleanup;
00102               strcpy(ctx->LHS, DEF_LHS);
00103               strcpy(ctx->RHS, DEF_RHS);
00104 #else
00105               goto cleanup;
00106 #endif
00107        }
00108        /*
00109         * The default RHS can be overridden by an environment
00110         * variable.
00111         */
00112        if ((cp = __secure_getenv("HES_DOMAIN")) != NULL) {
00113               free(ctx->RHS);
00114               ctx->RHS = malloc(strlen(cp)+2);
00115               if (!ctx->RHS)
00116                      goto cleanup;
00117               if (cp[0] == '.')
00118                      strcpy(ctx->RHS, cp);
00119               else {
00120                      ctx->RHS[0] = '.';
00121                      strcpy(ctx->RHS + 1, cp);
00122               }
00123        }
00124 
00125        /*
00126         * If there is no default hesiod realm set, we return an
00127         * error.
00128         */
00129        if (!ctx->RHS) {
00130               __set_errno(ENOEXEC);
00131               goto cleanup;
00132        }
00133 
00134 #if 0
00135        if (res_ninit(ctx->res) < 0)
00136               goto cleanup;
00137 #endif
00138 
00139        *context = ctx;
00140        return (0);
00141 
00142  cleanup:
00143        hesiod_end(ctx);
00144        return (-1);
00145 }
00146 
00147 /*
00148  * This function deallocates the hesiod_p
00149  */
00150 void
00151 hesiod_end(void *context) {
00152        struct hesiod_p *ctx = (struct hesiod_p *) context;
00153        int save_errno = errno;
00154 
00155        if (ctx->res)
00156               res_nclose(ctx->res);
00157        free(ctx->RHS);
00158        free(ctx->LHS);
00159        if (ctx->res && ctx->free_res)
00160               (*ctx->free_res)(ctx->res);
00161        free(ctx);
00162        __set_errno(save_errno);
00163 }
00164 
00165 /*
00166  * This function takes a hesiod (name, type) and returns a DNS
00167  * name which is to be resolved.
00168  */
00169 char *
00170 hesiod_to_bind(void *context, const char *name, const char *type) {
00171        struct hesiod_p *ctx = (struct hesiod_p *) context;
00172        char *bindname;
00173        char **rhs_list = NULL;
00174        const char *RHS, *cp;
00175        char *endp;
00176 
00177        /* Decide what our RHS is, and set cp to the end of the actual name. */
00178        if ((cp = strchr(name, '@')) != NULL) {
00179               if (strchr(cp + 1, '.'))
00180                      RHS = cp + 1;
00181               else if ((rhs_list = hesiod_resolve(context, cp + 1,
00182                   "rhs-extension")) != NULL)
00183                      RHS = *rhs_list;
00184               else {
00185                      __set_errno(ENOENT);
00186                      return (NULL);
00187               }
00188        } else {
00189               RHS = ctx->RHS;
00190               cp = name + strlen(name);
00191        }
00192 
00193        /*
00194         * Allocate the space we need, including up to three periods and
00195         * the terminating NUL.
00196         */
00197        if ((bindname = malloc((cp - name) + strlen(type) + strlen(RHS) +
00198            (ctx->LHS ? strlen(ctx->LHS) : 0) + 4)) == NULL) {
00199               if (rhs_list)
00200                      hesiod_free_list(context, rhs_list);
00201               return NULL;
00202        }
00203 
00204        /* Now put together the DNS name. */
00205        endp = (char *) __mempcpy (bindname, name, cp - name);
00206        *endp++ = '.';
00207        endp = (char *) __stpcpy (endp, type);
00208        if (ctx->LHS) {
00209               if (ctx->LHS[0] != '.')
00210                      *endp++ = '.';
00211               endp = __stpcpy (endp, ctx->LHS);
00212        }
00213        if (RHS[0] != '.')
00214               *endp++ = '.';
00215        strcpy (endp, RHS);
00216 
00217        if (rhs_list)
00218               hesiod_free_list(context, rhs_list);
00219 
00220        return (bindname);
00221 }
00222 
00223 /*
00224  * This is the core function.  Given a hesiod (name, type), it
00225  * returns an array of strings returned by the resolver.
00226  */
00227 char **
00228 hesiod_resolve(void *context, const char *name, const char *type) {
00229        struct hesiod_p *ctx = (struct hesiod_p *) context;
00230        char *bindname = hesiod_to_bind(context, name, type);
00231        char **retvec;
00232 
00233        if (bindname == NULL)
00234               return (NULL);
00235        if (init(ctx) == -1) {
00236               free(bindname);
00237               return (NULL);
00238        }
00239 
00240        retvec = get_txt_records(ctx, ctx->classes[0], bindname);
00241 
00242        if (retvec == NULL && (errno == ENOENT || errno == ECONNREFUSED) && ctx->classes[1])
00243               retvec = get_txt_records(ctx, ctx->classes[1], bindname);
00244 
00245 
00246        free(bindname);
00247        return (retvec);
00248 }
00249 
00250 void
00251 hesiod_free_list(void *context, char **list) {
00252        char **p;
00253 
00254        for (p = list; *p; p++)
00255               free(*p);
00256        free(list);
00257 }
00258 
00259 /*
00260  * This function parses the /etc/hesiod.conf file
00261  */
00262 static int
00263 parse_config_file(struct hesiod_p *ctx, const char *filename) {
00264        char buf[MAXDNAME+7];
00265        FILE *fp;
00266 
00267        /*
00268         * Clear the existing configuration variable, just in case
00269         * they're set.
00270         */
00271        free(ctx->RHS);
00272        free(ctx->LHS);
00273        ctx->RHS = ctx->LHS = 0;
00274        /* Set default query classes. */
00275        ctx->classes[0] = C_IN;
00276        ctx->classes[1] = C_HS;
00277 
00278        /*
00279         * Now open and parse the file...
00280         */
00281        if (!(fp = fopen(filename, "r")))
00282               return (-1);
00283 
00284        while (fgets(buf, sizeof(buf), fp) != NULL) {
00285               char *key, *data, *cp, **cpp;
00286 
00287               cp = buf;
00288               if (*cp == '#' || *cp == '\n' || *cp == '\r')
00289                      continue;
00290               while(*cp == ' ' || *cp == '\t')
00291                      cp++;
00292               key = cp;
00293               while(*cp != ' ' && *cp != '\t' && *cp != '=')
00294                      cp++;
00295               *cp++ = '\0';
00296 
00297               while(*cp == ' ' || *cp == '\t' || *cp == '=')
00298                      cp++;
00299               data = cp;
00300               while(*cp != ' ' && *cp != '\n' && *cp != '\r')
00301                      cp++;
00302               *cp++ = '\0';
00303 
00304               cpp = NULL;
00305               if (strcasecmp(key, "lhs") == 0)
00306                      cpp = &ctx->LHS;
00307               else if (strcasecmp(key, "rhs") == 0)
00308                      cpp = &ctx->RHS;
00309               if (cpp) {
00310                      *cpp = strdup(data);
00311                      if (!*cpp)
00312                             goto cleanup;
00313               } else if (strcasecmp(key, "classes") == 0) {
00314                      int n = 0;
00315                      while (*data && n < 2) {
00316                             cp = strchrnul(data, ',');
00317                             if (*cp != '\0')
00318                                    *cp++ = '\0';
00319                             if (strcasecmp(data, "IN") == 0)
00320                                    ctx->classes[n++] = C_IN;
00321                             else if (strcasecmp(data, "HS") == 0)
00322                                    ctx->classes[n++] = C_HS;
00323                             data = cp;
00324                      }
00325                      if (n == 0) {
00326                             /* Restore the default.  Better than
00327                                nother at all.  */
00328                             ctx->classes[0] = C_IN;
00329                             ctx->classes[1] = C_HS;
00330                      } else if (n == 1
00331                                || ctx->classes[0] == ctx->classes[1])
00332                             ctx->classes[1] = 0;
00333               }
00334        }
00335        fclose(fp);
00336        return (0);
00337 
00338  cleanup:
00339        fclose(fp);
00340        free(ctx->RHS);
00341        free(ctx->LHS);
00342        ctx->RHS = ctx->LHS = 0;
00343        return (-1);
00344 }
00345 
00346 /*
00347  * Given a DNS class and a DNS name, do a lookup for TXT records, and
00348  * return a list of them.
00349  */
00350 static char **
00351 get_txt_records(struct hesiod_p *ctx, int class, const char *name) {
00352        struct {
00353               int type;            /* RR type */
00354               int class;           /* RR class */
00355               int dlen;            /* len of data section */
00356               u_char *data;        /* pointer to data */
00357        } rr;
00358        HEADER *hp;
00359        u_char qbuf[MAX_HESRESP], abuf[MAX_HESRESP];
00360        u_char *cp, *erdata, *eom;
00361        char *dst, *edst, **list;
00362        int ancount, qdcount;
00363        int i, j, n, skip;
00364 
00365        /*
00366         * Construct the query and send it.
00367         */
00368        n = res_nmkquery(ctx->res, QUERY, name, class, T_TXT, NULL, 0,
00369                       NULL, qbuf, MAX_HESRESP);
00370        if (n < 0) {
00371               __set_errno(EMSGSIZE);
00372               return (NULL);
00373        }
00374        n = res_nsend(ctx->res, qbuf, n, abuf, MAX_HESRESP);
00375        if (n < 0) {
00376               __set_errno(ECONNREFUSED);
00377               return (NULL);
00378        }
00379        if (n < HFIXEDSZ) {
00380               __set_errno(EMSGSIZE);
00381               return (NULL);
00382        }
00383 
00384        /*
00385         * OK, parse the result.
00386         */
00387        hp = (HEADER *) abuf;
00388        ancount = ntohs(hp->ancount);
00389        qdcount = ntohs(hp->qdcount);
00390        cp = abuf + sizeof(HEADER);
00391        eom = abuf + n;
00392 
00393        /* Skip query, trying to get to the answer section which follows. */
00394        for (i = 0; i < qdcount; i++) {
00395               skip = dn_skipname(cp, eom);
00396               if (skip < 0 || cp + skip + QFIXEDSZ > eom) {
00397                      __set_errno(EMSGSIZE);
00398                      return (NULL);
00399               }
00400               cp += skip + QFIXEDSZ;
00401        }
00402 
00403        list = malloc((ancount + 1) * sizeof(char *));
00404        if (!list)
00405               return (NULL);
00406        j = 0;
00407        for (i = 0; i < ancount; i++) {
00408               skip = dn_skipname(cp, eom);
00409               if (skip < 0) {
00410                      __set_errno(EMSGSIZE);
00411                      goto cleanup;
00412               }
00413               cp += skip;
00414               if (cp + 3 * INT16SZ + INT32SZ > eom) {
00415                      __set_errno(EMSGSIZE);
00416                      goto cleanup;
00417               }
00418               rr.type = ns_get16(cp);
00419               cp += INT16SZ;
00420               rr.class = ns_get16(cp);
00421               cp += INT16SZ + INT32SZ;    /* skip the ttl, too */
00422               rr.dlen = ns_get16(cp);
00423               cp += INT16SZ;
00424               if (cp + rr.dlen > eom) {
00425                      __set_errno(EMSGSIZE);
00426                      goto cleanup;
00427               }
00428               rr.data = cp;
00429               cp += rr.dlen;
00430               if (rr.class != class || rr.type != T_TXT)
00431                      continue;
00432               if (!(list[j] = malloc(rr.dlen)))
00433                      goto cleanup;
00434               dst = list[j++];
00435               edst = dst + rr.dlen;
00436               erdata = rr.data + rr.dlen;
00437               cp = rr.data;
00438               while (cp < erdata) {
00439                      n = (unsigned char) *cp++;
00440                      if (cp + n > eom || dst + n > edst) {
00441                             __set_errno(EMSGSIZE);
00442                             goto cleanup;
00443                      }
00444                      memcpy(dst, cp, n);
00445                      cp += n;
00446                      dst += n;
00447               }
00448               if (cp != erdata) {
00449                      __set_errno(EMSGSIZE);
00450                      goto cleanup;
00451               }
00452               *dst = '\0';
00453        }
00454        list[j] = NULL;
00455        if (j == 0) {
00456               __set_errno(ENOENT);
00457               goto cleanup;
00458        }
00459        return (list);
00460 
00461  cleanup:
00462        for (i = 0; i < j; i++)
00463               free(list[i]);
00464        free(list);
00465        return (NULL);
00466 }
00467 
00468 struct __res_state *
00469 __hesiod_res_get(void *context) {
00470        struct hesiod_p *ctx = context;
00471 
00472        if (!ctx->res) {
00473               struct __res_state *res;
00474               res = (struct __res_state *)calloc(1, sizeof *res);
00475               if (res == NULL)
00476                      return (NULL);
00477               __hesiod_res_set(ctx, res, free);
00478        }
00479 
00480        return (ctx->res);
00481 }
00482 
00483 void
00484 __hesiod_res_set(void *context, struct __res_state *res,
00485                 void (*free_res)(void *)) {
00486        struct hesiod_p *ctx = context;
00487 
00488        if (ctx->res && ctx->free_res) {
00489               res_nclose(ctx->res);
00490               (*ctx->free_res)(ctx->res);
00491        }
00492 
00493        ctx->res = res;
00494        ctx->free_res = free_res;
00495 }
00496 
00497 static int
00498 init(struct hesiod_p *ctx) {
00499 
00500        if (!ctx->res && !__hesiod_res_get(ctx))
00501               return (-1);
00502 
00503        if (__res_maybe_init (ctx->res, 0) == -1)
00504               return (-1);
00505 
00506        return (0);
00507 }