(root)/
glibc-2.38/
hesiod/
hesiod.c
       1  /* Copyright (C) 1997-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  /*
      19   * Copyright (c) 1996,1999 by Internet Software Consortium.
      20   *
      21   * Permission to use, copy, modify, and distribute this software for any
      22   * purpose with or without fee is hereby granted, provided that the above
      23   * copyright notice and this permission notice appear in all copies.
      24   *
      25   * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
      26   * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
      27   * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
      28   * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
      29   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
      30   * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
      31   * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
      32   * SOFTWARE.
      33   */
      34  
      35  /*
      36   * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>.
      37   */
      38  
      39  /*
      40   * hesiod.c --- the core portion of the hesiod resolver.
      41   *
      42   * This file is derived from the hesiod library from Project Athena;
      43   * It has been extensively rewritten by Theodore Ts'o to have a more
      44   * thread-safe interface.
      45   */
      46  
      47  /* Imports */
      48  
      49  #include <sys/types.h>
      50  #include <netinet/in.h>
      51  #include <arpa/nameser.h>
      52  
      53  #include <errno.h>
      54  #include <netdb.h>
      55  #include <resolv.h>
      56  #include <stdio.h>
      57  #include <stdlib.h>
      58  #include <string.h>
      59  
      60  #include "hesiod.h"
      61  #include "hesiod_p.h"
      62  
      63  #define _PATH_HESIOD_CONF "/etc/hesiod.conf"
      64  
      65  /* Forward */
      66  
      67  static int	parse_config_file(struct hesiod_p *ctx, const char *filename);
      68  static char **	get_txt_records(struct hesiod_p *ctx, int class,
      69  				const char *name);
      70  
      71  /* Public */
      72  
      73  /*
      74   * This function is called to initialize a hesiod_p.
      75   */
      76  int
      77  hesiod_init(void **context) {
      78  	struct hesiod_p *ctx;
      79  	const char *configname;
      80  	char *cp;
      81  
      82  	ctx = malloc(sizeof(struct hesiod_p));
      83  	if (ctx == 0)
      84  		return (-1);
      85  
      86  	ctx->LHS = NULL;
      87  	ctx->RHS = NULL;
      88  	/* Set default query classes. */
      89  	ctx->classes[0] = C_IN;
      90  	ctx->classes[1] = C_HS;
      91  
      92  	configname = __libc_secure_getenv("HESIOD_CONFIG");
      93  	if (!configname)
      94  	  configname = _PATH_HESIOD_CONF;
      95  	if (parse_config_file(ctx, configname) < 0) {
      96  		goto cleanup;
      97  	}
      98  	/*
      99  	 * The default RHS can be overridden by an environment
     100  	 * variable.
     101  	 */
     102  	if ((cp = __libc_secure_getenv("HES_DOMAIN")) != NULL) {
     103  		free(ctx->RHS);
     104  		ctx->RHS = malloc(strlen(cp)+2);
     105  		if (!ctx->RHS)
     106  			goto cleanup;
     107  		if (cp[0] == '.')
     108  			strcpy(ctx->RHS, cp);
     109  		else {
     110  			ctx->RHS[0] = '.';
     111  			strcpy(ctx->RHS + 1, cp);
     112  		}
     113  	}
     114  
     115  	/*
     116  	 * If there is no default hesiod realm set, we return an
     117  	 * error.
     118  	 */
     119  	if (!ctx->RHS) {
     120  		__set_errno(ENOEXEC);
     121  		goto cleanup;
     122  	}
     123  
     124  	*context = ctx;
     125  	return (0);
     126  
     127   cleanup:
     128  	hesiod_end(ctx);
     129  	return (-1);
     130  }
     131  
     132  /*
     133   * This function deallocates the hesiod_p
     134   */
     135  void
     136  hesiod_end(void *context) {
     137  	struct hesiod_p *ctx = (struct hesiod_p *) context;
     138  	int save_errno = errno;
     139  
     140  	free(ctx->RHS);
     141  	free(ctx->LHS);
     142  	free(ctx);
     143  	__set_errno(save_errno);
     144  }
     145  
     146  /*
     147   * This function takes a hesiod (name, type) and returns a DNS
     148   * name which is to be resolved.
     149   */
     150  char *
     151  hesiod_to_bind(void *context, const char *name, const char *type) {
     152  	struct hesiod_p *ctx = (struct hesiod_p *) context;
     153  	char *bindname;
     154  	char **rhs_list = NULL;
     155  	const char *RHS, *cp;
     156  	char *endp;
     157  
     158  	/* Decide what our RHS is, and set cp to the end of the actual name. */
     159  	if ((cp = strchr(name, '@')) != NULL) {
     160  		if (strchr(cp + 1, '.'))
     161  			RHS = cp + 1;
     162  		else if ((rhs_list = hesiod_resolve(context, cp + 1,
     163  		    "rhs-extension")) != NULL)
     164  			RHS = *rhs_list;
     165  		else {
     166  			__set_errno(ENOENT);
     167  			return (NULL);
     168  		}
     169  	} else {
     170  		RHS = ctx->RHS;
     171  		cp = name + strlen(name);
     172  	}
     173  
     174  	/*
     175  	 * Allocate the space we need, including up to three periods and
     176  	 * the terminating NUL.
     177  	 */
     178  	if ((bindname = malloc((cp - name) + strlen(type) + strlen(RHS) +
     179  	    (ctx->LHS ? strlen(ctx->LHS) : 0) + 4)) == NULL) {
     180  		if (rhs_list)
     181  			hesiod_free_list(context, rhs_list);
     182  		return NULL;
     183  	}
     184  
     185  	/* Now put together the DNS name. */
     186  	endp = (char *) __mempcpy (bindname, name, cp - name);
     187  	*endp++ = '.';
     188  	endp = (char *) __stpcpy (endp, type);
     189  	if (ctx->LHS) {
     190  		if (ctx->LHS[0] != '.')
     191  			*endp++ = '.';
     192  		endp = __stpcpy (endp, ctx->LHS);
     193  	}
     194  	if (RHS[0] != '.')
     195  		*endp++ = '.';
     196  	strcpy (endp, RHS);
     197  
     198  	if (rhs_list)
     199  		hesiod_free_list(context, rhs_list);
     200  
     201  	return (bindname);
     202  }
     203  
     204  /*
     205   * This is the core function.  Given a hesiod (name, type), it
     206   * returns an array of strings returned by the resolver.
     207   */
     208  char **
     209  hesiod_resolve(void *context, const char *name, const char *type) {
     210  	struct hesiod_p *ctx = (struct hesiod_p *) context;
     211  	char *bindname = hesiod_to_bind(context, name, type);
     212  	char **retvec;
     213  
     214  	if (bindname == NULL)
     215  		return (NULL);
     216  
     217  	retvec = get_txt_records(ctx, ctx->classes[0], bindname);
     218  
     219  	if (retvec == NULL && (errno == ENOENT || errno == ECONNREFUSED) && ctx->classes[1])
     220  		retvec = get_txt_records(ctx, ctx->classes[1], bindname);
     221  
     222  
     223  	free(bindname);
     224  	return (retvec);
     225  }
     226  
     227  void
     228  hesiod_free_list(void *context, char **list) {
     229  	char **p;
     230  
     231  	for (p = list; *p; p++)
     232  		free(*p);
     233  	free(list);
     234  }
     235  
     236  /*
     237   * This function parses the /etc/hesiod.conf file
     238   */
     239  static int
     240  parse_config_file(struct hesiod_p *ctx, const char *filename) {
     241  	char buf[MAXDNAME+7];
     242  	FILE *fp;
     243  
     244  	/*
     245  	 * Clear the existing configuration variable, just in case
     246  	 * they're set.
     247  	 */
     248  	free(ctx->RHS);
     249  	free(ctx->LHS);
     250  	ctx->RHS = ctx->LHS = 0;
     251  	/* Set default query classes. */
     252  	ctx->classes[0] = C_IN;
     253  	ctx->classes[1] = C_HS;
     254  
     255  	/*
     256  	 * Now open and parse the file...
     257  	 */
     258  	if (!(fp = fopen(filename, "rce")))
     259  		return (-1);
     260  
     261  	while (fgets(buf, sizeof(buf), fp) != NULL) {
     262  		char *key, *data, *cp, **cpp;
     263  
     264  		cp = buf;
     265  		if (*cp == '#' || *cp == '\n' || *cp == '\r')
     266  			continue;
     267  		while(*cp == ' ' || *cp == '\t')
     268  			cp++;
     269  		key = cp;
     270  		while(*cp != ' ' && *cp != '\t' && *cp != '=')
     271  			cp++;
     272  		*cp++ = '\0';
     273  
     274  		while(*cp == ' ' || *cp == '\t' || *cp == '=')
     275  			cp++;
     276  		data = cp;
     277  		while(*cp != ' ' && *cp != '\n' && *cp != '\r')
     278  			cp++;
     279  		*cp++ = '\0';
     280  
     281  		cpp = NULL;
     282  		if (strcasecmp(key, "lhs") == 0)
     283  			cpp = &ctx->LHS;
     284  		else if (strcasecmp(key, "rhs") == 0)
     285  			cpp = &ctx->RHS;
     286  		if (cpp) {
     287  			*cpp = strdup(data);
     288  			if (!*cpp)
     289  				goto cleanup;
     290  		} else if (strcasecmp(key, "classes") == 0) {
     291  			int n = 0;
     292  			while (*data && n < 2) {
     293  				cp = strchrnul(data, ',');
     294  				if (*cp != '\0')
     295  					*cp++ = '\0';
     296  				if (strcasecmp(data, "IN") == 0)
     297  					ctx->classes[n++] = C_IN;
     298  				else if (strcasecmp(data, "HS") == 0)
     299  					ctx->classes[n++] = C_HS;
     300  				data = cp;
     301  			}
     302  			if (n == 0) {
     303  				/* Restore the default.  Better than
     304  				   nother at all.  */
     305  				ctx->classes[0] = C_IN;
     306  				ctx->classes[1] = C_HS;
     307  			} else if (n == 1
     308  				   || ctx->classes[0] == ctx->classes[1])
     309  				ctx->classes[1] = 0;
     310  		}
     311  	}
     312  	fclose(fp);
     313  	return (0);
     314  
     315   cleanup:
     316  	fclose(fp);
     317  	free(ctx->RHS);
     318  	free(ctx->LHS);
     319  	ctx->RHS = ctx->LHS = 0;
     320  	return (-1);
     321  }
     322  
     323  /*
     324   * Given a DNS class and a DNS name, do a lookup for TXT records, and
     325   * return a list of them.
     326   */
     327  static char **
     328  get_txt_records(struct hesiod_p *ctx, int class, const char *name) {
     329  	struct {
     330  		int type;		/* RR type */
     331  		int class;		/* RR class */
     332  		int dlen;		/* len of data section */
     333  		u_char *data;		/* pointer to data */
     334  	} rr;
     335  	HEADER *hp;
     336  	u_char qbuf[MAX_HESRESP], abuf[MAX_HESRESP];
     337  	u_char *cp, *erdata, *eom;
     338  	char *dst, *edst, **list;
     339  	int ancount, qdcount;
     340  	int i, j, n, skip;
     341  
     342  	/*
     343  	 * Construct the query and send it.
     344  	 */
     345  	n = res_mkquery(QUERY, name, class, T_TXT, NULL, 0,
     346  			 NULL, qbuf, MAX_HESRESP);
     347  	if (n < 0) {
     348  		__set_errno(EMSGSIZE);
     349  		return (NULL);
     350  	}
     351  	n = res_send(qbuf, n, abuf, MAX_HESRESP);
     352  	if (n < 0) {
     353  		__set_errno(ECONNREFUSED);
     354  		return (NULL);
     355  	}
     356  	if (n < HFIXEDSZ) {
     357  		__set_errno(EMSGSIZE);
     358  		return (NULL);
     359  	}
     360  
     361  	/*
     362  	 * OK, parse the result.
     363  	 */
     364  	hp = (HEADER *) abuf;
     365  	ancount = ntohs(hp->ancount);
     366  	qdcount = ntohs(hp->qdcount);
     367  	cp = abuf + sizeof(HEADER);
     368  	eom = abuf + n;
     369  
     370  	/* Skip query, trying to get to the answer section which follows. */
     371  	for (i = 0; i < qdcount; i++) {
     372  		skip = dn_skipname(cp, eom);
     373  		if (skip < 0 || cp + skip + QFIXEDSZ > eom) {
     374  			__set_errno(EMSGSIZE);
     375  			return (NULL);
     376  		}
     377  		cp += skip + QFIXEDSZ;
     378  	}
     379  
     380  	list = malloc((ancount + 1) * sizeof(char *));
     381  	if (!list)
     382  		return (NULL);
     383  	j = 0;
     384  	for (i = 0; i < ancount; i++) {
     385  		skip = dn_skipname(cp, eom);
     386  		if (skip < 0) {
     387  			__set_errno(EMSGSIZE);
     388  			goto cleanup;
     389  		}
     390  		cp += skip;
     391  		if (cp + 3 * INT16SZ + INT32SZ > eom) {
     392  			__set_errno(EMSGSIZE);
     393  			goto cleanup;
     394  		}
     395  		rr.type = ns_get16(cp);
     396  		cp += INT16SZ;
     397  		rr.class = ns_get16(cp);
     398  		cp += INT16SZ + INT32SZ;	/* skip the ttl, too */
     399  		rr.dlen = ns_get16(cp);
     400  		cp += INT16SZ;
     401  		if (rr.dlen == 0 || cp + rr.dlen > eom) {
     402  			__set_errno(EMSGSIZE);
     403  			goto cleanup;
     404  		}
     405  		rr.data = cp;
     406  		cp += rr.dlen;
     407  		if (rr.class != class || rr.type != T_TXT)
     408  			continue;
     409  		if (!(list[j] = malloc(rr.dlen)))
     410  			goto cleanup;
     411  		dst = list[j++];
     412  		edst = dst + rr.dlen;
     413  		erdata = rr.data + rr.dlen;
     414  		cp = rr.data;
     415  		while (cp < erdata) {
     416  			n = (unsigned char) *cp++;
     417  			if (cp + n > eom || dst + n > edst) {
     418  				__set_errno(EMSGSIZE);
     419  				goto cleanup;
     420  			}
     421  			memcpy(dst, cp, n);
     422  			cp += n;
     423  			dst += n;
     424  		}
     425  		if (cp != erdata) {
     426  			__set_errno(EMSGSIZE);
     427  			goto cleanup;
     428  		}
     429  		*dst = '\0';
     430  	}
     431  	list[j] = NULL;
     432  	if (j == 0) {
     433  		__set_errno(ENOENT);
     434  		goto cleanup;
     435  	}
     436  	return (list);
     437  
     438   cleanup:
     439  	for (i = 0; i < j; i++)
     440  		free(list[i]);
     441  	free(list);
     442  	return (NULL);
     443  }