/* This file is part of the Hesiod library.
 *
 *	$Source: /afs/sipb.mit.edu/project/sipb-athena/repository/src/hesiod/hesiod.c,v $
 *	$Author: ghudson $
 *	$Athena: hesiod.c,v 1.5 88/08/07 22:00:44 treese Locked $
 *	$Log: hesiod.c,v $
 *	Revision 1.2  1996/06/01 18:46:32  ghudson
 *	Add reentrant interfaces.
 *
 * Revision 1.15  93/10/22  12:02:36  epeisach
 * Under POSIX, include stdlib.h so that calloc/getenv defined
 * properly for the OS.
 * Also, allow header files in current directory to define the
 * return type for hes_resolve and _resolve (instead of redefing)
 * 
 * Revision 1.14  93/10/22  08:17:40  probe
 * Use memmove [ANSI] instead of bcopy, except on the platforms where we
 * don't have memmove.
 * 
 * Revision 1.13  93/10/22  08:16:06  probe
 * ANSI says to use strchr, and even the BSD systems have this function.
 * 
 * Revision 1.12  93/10/21  14:35:55  mar
 * include string.h instead of strings.h
 * 
 * Revision 1.11  93/06/15  10:26:37  mar
 * handle empty LHS
 * 
 * Revision 1.10  93/04/27  14:03:44  vrt
 * compatibility index in solaris is braindamaged.
 * 
 * Revision 1.9  90/07/19  09:20:09  epeisach
 * Declare that getenv returns a char*
 * 
 * Revision 1.8  90/07/11  16:46:44  probe
 * Patches from <mar>
 * Support for HES_DOMAIN environment variable added
 * 
 * Revision 1.9  90/07/11  16:41:18  probe
 * Patches from <mar>
 * Added description about error codes and the HES_DOMAIN environment
 * variable
 * 
 * Revision 1.7  89/11/16  06:49:31  probe
 * Uses T_TXT, as defined in the RFC.
 * 
 * Revision 1.6.1.1  89/11/03  17:50:12  probe
 * Changes T_TXT to T_UNSPECA.
 * 
 * The BIND 4.8.1 implementation of T_TXT is incorrect; BIND 4.8.1 declares
 * it as a NULL terminated string.  The RFC defines T_TXT to be a length
 * byte followed by arbitrary changes.
 * 
 * Because of this incorrect declaration in BIND 4.8.1, when this bug is fixed,
 * T_TXT requests between machines running different versions of BIND will
 * not be compatible (nor is there any way of adding compatibility).
 * 
 * Revision 1.6  88/08/07  23:17:03  treese
 * Second-public-distribution
 * 
 * Revision 1.5  88/08/07  22:00:44  treese
 * Changed T_UNSPECA to T_TXT.
 * Changed C_HESIOD to C_HS.
 * Deleted ifdef's based on USE_HS_QUERY -- they're obsolete.
 * 
 * Revision 1.4  88/08/07  21:52:31  treese
 * First public distribution
 * 
 * Revision 1.3  88/06/12  00:52:58  treese
 * Cleaned up to work with Saber.
 * First public distribution.
 * 
 * Revision 1.2  88/06/11  22:36:38  treese
 * Cleaned up for public distribution.
 * 
 * 
 *
 * Copyright 1988 by the Massachusetts Institute of Technology.  See the
 * file <mit-copyright.h> for copying and distribution information.
 */

#include "mit-copyright.h"

#ifndef lint
static char rcsid_hesiod_c[] = "$Header: /afs/sipb.mit.edu/project/sipb-athena/repository/src/hesiod/hesiod.c,v 1.2 1996/06/01 18:46:32 ghudson Exp $";
#endif

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <netdb.h>
#include <pwd.h>
#ifdef POSIX
#include <stdlib.h>
#else
extern char *malloc(), *calloc(), *getenv();
#endif
#include "resscan.h"
#include "hesiod.h"

#if defined(vax)
#define memcpy(a, b, c) bcopy(b, a, c)
#endif

#define USE_HS_QUERY	/* undefine this if your higher-level name servers */
			/* don't know class HS */

retransXretry_t NoRetryTime = { 0, 0};

char *HesConfigFile = HESIOD_CONF;
static char *Hes_LHS;
static char *Hes_RHS;
int Hes_Errno = HES_ER_UNINIT;

static void __hes_init();

int
hes_init()
{
	register FILE *fp;
	register char *key, *cp, **cpp;
	int len;
	char buf[MAXDNAME+7];

	if (Hes_Errno != HES_ER_UNINIT && Hes_Errno != HES_ER_CONFIG)
	    return 0;
	res_init();
	Hes_Errno = HES_ER_UNINIT;
	Hes_LHS = NULL; Hes_RHS = NULL;

	if ((fp = fopen(HesConfigFile, "r")) == NULL) {
		/* use defaults compiled in */
		/* no file or no access uses defaults */
		/* but poorly formed file returns error */
		Hes_LHS = DEF_LHS; Hes_RHS = DEF_RHS;
	} else {
		while(fgets(buf, MAXDNAME+7, fp) != NULL) {
			cp = buf;
			if (*cp == '#' || *cp == '\n') continue;
			while(*cp == ' ' || *cp == '\t') cp++;
			key = cp;
			while(*cp != ' ' && *cp != '\t' && *cp != '=') cp++;
			*cp++ = '\0';
			if (strcmp(key, "lhs") == 0) cpp = &Hes_LHS;
			else if (strcmp(key, "rhs") == 0) cpp = &Hes_RHS;
			else continue;
			while(*cp == ' ' || *cp == '\t' || *cp == '=') cp++;
			if (*cp != '.' && *cp != '\n') {
				Hes_Errno = HES_ER_CONFIG;
				fclose(fp);
			}
			len = strlen(cp);
			*cpp = calloc((unsigned int) len, sizeof(char));
			(void) strncpy(*cpp, cp, len-1);
		}
		fclose(fp);
	}
	/* see if the RHS is overridden by environment variable */
	if ((cp = getenv("HES_DOMAIN")) != NULL)
		Hes_RHS = strcpy(malloc(strlen(cp)+1),cp);
	/* the LHS may be null, the RHS must not be null */
	if (Hes_RHS == NULL)
		Hes_Errno = HES_ER_CONFIG;
	else
		Hes_Errno = HES_ER_OK;
	return(Hes_Errno);
}

char *
hes_to_bind(name, type)
char *name, *type;
{
	static char buffer[MAXDNAME];
	int status;

	if (Hes_Errno == HES_ER_UNINIT || Hes_Errno == HES_ER_CONFIG)
		(void) hes_init();
	if (Hes_Errno == HES_ER_CONFIG) return(NULL);
	status = hes_to_bind_r(name, type, buffer, MAXDNAME);
	if (status == HES_ER_OK) {
		return(buffer);
	} else {
		Hes_Errno = status;
		return(NULL);
	}
}

int
hes_to_bind_r(name, type, buffer, bufsize)
char *name, *type, *buffer;
int bufsize;
{
	register char *cp;
	char *cpp[2];
	char *RHS;
	int len;

	*cpp = NULL;
	if (Hes_Errno == HES_ER_CONFIG)
		return(HES_ER_CONFIG);
	if (Hes_Errno == HES_ER_UNINIT) {
		fprintf(stderr, "Library not initialized in hes_to_bind_r().\n");
		abort();
	}
	if (cp = strchr(name,'@')) {
		if (cp - name > bufsize - 1)
			return(HES_ER_RANGE);
		if (strchr(cp + 1,'.'))
			RHS = cp + 1;
		else
			if (hes_resolve_r(cp + 1, "rhs-extension", cpp, 2) == HES_ER_OK
				&& *cpp != NULL)
				RHS = *cpp;
			else {
				return(HES_ER_NOTFOUND);
			}
		(void) strncpy(buffer,name,cp - name);
		buffer[cp - name] = '\0';
	} else {
		RHS = Hes_RHS;
		if ((int) strlen(name) > bufsize - 1)
			return(HES_ER_RANGE);
		(void) strcpy(buffer, name);
	}
	len = strlen(buffer) + 1 + strlen(type);
	if (Hes_LHS)
		len += strlen(Hes_LHS) + ((Hes_LHS[0] != '.') ? 1 : 0);
	len += strlen(RHS) + ((RHS[0] != '.') ? 1 : 0);
	if (len > bufsize - 1) {
		if (*cpp)
			free(*cpp);
		return(HES_ER_RANGE);
	}
	(void) strcat(buffer, ".");
	(void) strcat(buffer, type);
	if (Hes_LHS) {
		if (Hes_LHS[0] != '.')
			(void) strcat(buffer,".");
		(void) strcat(buffer, Hes_LHS);
	}
	if (RHS[0] != '.')
		(void) strcat(buffer,".");
	(void) strcat(buffer, RHS);
	if (*cpp)
		free(*cpp);
	return(HES_ER_OK);
}

char **
hes_resolve(name, type)
char *name, *type;
{
	static char *retvec[100];
	int status;

	if (Hes_Errno == HES_ER_UNINIT || Hes_Errno == HES_ER_CONFIG)
		(void) hes_init();
	if (Hes_Errno == HES_ER_CONFIG) return(NULL);
	status = hes_resolve_r(name, type, retvec, 100);
	if (status == HES_ER_OK) {
		return(retvec);
	} else {
		Hes_Errno = status;
		return(NULL);
	}
}

int
hes_resolve_r(name, type, retvec, retveclen)
char *name, *type, **retvec;
int retveclen;
{
	register char *cp;
	char buffer[MAXDNAME], nmsgbuf[NMSGSIZE], databuf[DATASIZE];
	char *ocp, *dst;
	int i, j, n;
	struct nsmsg *ns;
	rr_t *rp;
	extern int errno;

	if (Hes_Errno == HES_ER_CONFIG)
		return(HES_ER_CONFIG);
	if (Hes_Errno == HES_ER_UNINIT) {
		fprintf(stderr, "Library not initialized in hes_resolve_r().\n");
		abort();
	}
	i = hes_to_bind_r(name, type, buffer, MAXDNAME);
	if (i != HES_ER_OK) return(i);
	cp = buffer;
	errno = 0;
	ns = _resolve(cp, C_HS, T_TXT, NoRetryTime, nmsgbuf, databuf);
	if (ns == NULL && (errno == ETIMEDOUT || errno == ECONNREFUSED))
		return(HES_ER_NET);
	if (ns == NULL || ns->ns_off <= 0)
		return(HES_ER_NOTFOUND);
	for(i = j = 0, rp = &ns->rr; i < ns->ns_off; rp++, i++) {
		if (j >= retveclen) {
			for (i = 0; i < j; i++)
				free(retvec[i]);
			return(HES_ER_RANGE);
		}
		if (
		    rp->class == C_HS &&
		    rp->type == T_TXT) { /* skip CNAME records */
			retvec[j] = calloc(rp->dlen + 1, sizeof(char));
			if (retvec[j] == NULL) {
				for (i = 0; i < j; i++)
					free(retvec[i]);
				return(HES_ER_NOMEM);
			}
			dst = retvec[j];
			ocp = cp = rp->data;
			while (cp < ocp + rp->dlen) {
			    n = (unsigned char) *cp++;
			    (void) memcpy(dst, cp, n);
			    cp += n;
			    dst += n;
			}
			*dst = 0;
			j++;
		}
	}
	if (j >= retveclen) {
		for (i = 0; i < j; i++)
			free(retvec[i]);
		return(HES_ER_RANGE);
	}
	retvec[j] = 0;
	return(HES_ER_OK);
}

int
hes_error()
{
	return Hes_Errno;
}

