[36] in Hesiod

home help back first fref pref prev next nref lref last post

Re: getpwent, getgrent

daemon@ATHENA.MIT.EDU (Bruce Cole)
Tue Oct 2 21:59:24 1990

Date: Tue, 2 Oct 90 20:50:08 -0500
From: cole@cs.wisc.edu (Bruce Cole)
To: greg%duke.cs.unlv.edu@RELAY.CS.NET
Cc: treese@CRL.DEC.COM, hesiod@ATHENA.MIT.EDU, bind@UCBARPA.BERKELEY.EDU,
In-Reply-To: Greg Wohletz's message of Tue, 02 Oct 90 12:25:35 -0700

     I've twice implemented code to zone transfer Hesiod data from a
nameserver, and walk through all the data obtained from the zone transfer.
The first implementation was to provide machine database information to a
locally hacked version of rdist.  The second implementation was to extend
the amd automounter's support of hesiod maintained automount data.  Below
is some sample code from the first implementation (routines such as 
our_res_send are derived from BIND source).

      It is worth noting that named running on our Vax750s was not able to
service zone transfers of hesiod data in a reasonable amount of time for
interactive use (on the order of 1 minute for a transfer).  Named on a
Decstation 3100 services these zone transfers much more quickly (on the order
of a few seconds).

#ifndef lint
static char *RCSid = "$Header: /src/uw/genhostlist/RCS/genhostlist.c,v 1.7 90/07/29 18:57:41 cole Exp $";
#endif

#include <sys/time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <errno.h>
#include <stdio.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <strings.h>
#include <netdb.h>

int soacnt;
extern int errno;
#define MAXHSNS 8
#define MAXADDR 16
#define MAXDATA 8*1024
#define hs_server_addr(ns) nsaddr_list[ns]
struct in_addr nsaddr_list[MAXADDR];
static int hs_nscount;
struct timeval hs_timeout;
int servernum;

/* Bruce Cole, cole@cs.wisc.edu.  2/7/89 */

main(argc, argv)
int argc;
char **argv;
{
	int status;

	if (status = res_init())
		crash("In res_init.  Returned: %d", status);
	errno = 0;
	/* Determine which nameserver to contact for zone transfers */
	if (hs_get_ns_list("path.cs.wisc.edu") != 0)
		crash("Call to hs_get_ns_list");
	_res.retrans = 90;
	query_domain("path.cs.wisc.edu");
	exit(0);
}

query_domain(domain)
char *domain;
{
	int status, len;
	char buf[PACKETSZ], ansbuf[PACKETSZ];
	int class;

	class = C_HS;
	if ((len = res_mkquery(QUERY, domain, class, T_AXFR,
			       (char *)NULL, 0, NULL, buf, PACKETSZ)) == -1)
		crash("In res_mkquery");
	if ((status = our_res_send(buf, len, ansbuf, PACKETSZ)) == -1)
		if (errno)
			crash("In res_send");
}

our_res_send(buf, buflen, answer, anslen)
char *buf;
int buflen;
char *answer;
int anslen;
{
	register int n;
	int retry, ns;
	int gotsomewhere = 0, connected = 0;
	u_short id, len;
	char *cp;
	fd_set dsmask;
	HEADER *hp = (HEADER *) buf;
	HEADER *anhp = (HEADER *) answer;
	struct iovec iov[2];
	char junk[512];
	static int s = -1;
	int status;
	struct sockaddr_in server;

	soacnt = 0;
	id = hp->id;
	/*
	 * Send request, RETRY times, or until successful
	 */
	for (retry = _res.retry; retry > 0; retry--) {
		for (ns = 0; ns < hs_nscount; ns++) {
			hs_timeout.tv_sec =
				(_res.retrans << (_res.retry - retry))
				/ hs_nscount;
			if (hs_timeout.tv_sec <= 0)
				hs_timeout.tv_sec = 1;
			hs_timeout.tv_usec = 0;
			if (s < 0) {
				s = socket(AF_INET, SOCK_STREAM, 0);
				if (s < 0) {
					continue;
				}
				servernum = ns;
				bcopy(&hs_server_addr(ns), &server.sin_addr,
				      sizeof(struct in_addr));
				server.sin_family = AF_INET;
				server.sin_port = htons(NAMESERVER_PORT);
					
				if (connect(s, &server,
					    sizeof(struct sockaddr)) < 0) {
					(void) close(s);
					s = -1;
					continue;
				}
			}
			/*
			 * Send length & message
			 */
			len = htons((u_short)buflen);
			iov[0].iov_base = (caddr_t)&len;
			iov[0].iov_len = sizeof(len);
			iov[1].iov_base = buf;
			iov[1].iov_len = buflen;
			if (writev(s, iov, 2) != sizeof(len) + buflen) {
				(void) close(s);
				s = -1;
				continue;
			}
			status = 0;
			while (s != -1 && soacnt < 2 && status != -2) {
				if ((status =
				     readresp(s, answer, anslen)) == -1) {
					(void) close(s);
					s = -1;
					continue;
				}
			}
			if (status == -2) {
				/* There was a permanent error transfering this
				   zone.  Give up. */
				if (s != -1) {
					(void) close(s);
					s = -1;
				}
				return(-1);
			}
			if (s == -1)
				continue;
			return (0);
		}
	}
	if (errno == 0)
		errno = ETIMEDOUT;
	return (-1);
}

hs_parse(file, msg, eom)
FILE *file;
char *msg, *eom;
{
	register char *cp;
	register HEADER *hp;
	register int n, len;
	int qdcount, ancount;
	char buf[PACKETSZ];
	char *c;
	short type;

	hp = (HEADER *)msg;
	if (hp->rcode != NOERROR || hp->opcode != QUERY) {
		fprintf(stderr, "Bad response (%d) from nameserver %s\n",
			hp->rcode,
			inet_ntoa(nsaddr_list[servernum]));
		return(-1);
	}
	cp = msg + sizeof(HEADER);
	ancount = ntohs(hp->ancount);
	qdcount = ntohs(hp->qdcount);
	while (qdcount-- > 0)
		cp += dn_skipname(cp, eom) + QFIXEDSZ;
	if (soacnt == 0 && ancount == 0) {
		fprintf(stderr, "No SOA found\n");
		return(-2);
	}
	while (ancount-- > 0 && cp < eom) {
		if ((n = dn_expand(msg, eom, cp, buf, PACKETSZ)) < 0)
			break;
		cp += n;
		if ((type = _getshort(cp)) == T_SOA) {
			soacnt++;
		}
		cp += 2*sizeof(u_short) + sizeof(u_long);
		len = _getshort(cp);
		cp += sizeof(u_short);
		if (type == T_TXT) {
			if (c = index(buf, '.'))
				*c = '\0';
			fprintf(file, "%s ", buf);
			printtext(file, cp, len);
			fprintf(file, "\n");
		}
		cp += len;
		errno = 0;
	}
	return(0);
}

/* This routine is hairy in order to avoid arbitrary buffer length
   restrictions */
printtext(file, cp, len)
FILE *file;
char *cp;
int len;
{
	int cnt, nextcnt;
	char *orig;

	cnt = *cp++;
	orig = cp;
	while (cnt) {
		if (cnt > len)
			crash("TXT RR not of expected length (%d %d): %s",
			      cnt, len, orig);
		nextcnt = cp[cnt];
		cp[cnt] = '\0';
		fprintf(file, "%s", cp);
		len -= cnt+1;
		if (len == 0)
			break;
		cp = &cp[cnt+1];
		cnt = nextcnt;
	}
}

char *getdname(cp, msg, name, len)
char *cp, *msg, *name;
int len;
{
	int n;
	register int a,b;

	if ((n = dn_expand(msg, msg + 512, cp, name, len)) < 0)
		return (NULL);
	if (_getshort(cp + n) != T_A)
		return(NULL);
	return (cp + n + sizeof(u_short) * 2);
}



/* Returns:
   0: Success
   -1: Error
   -2: Permanent failure
*/
readresp(s, answer, anslen)
int s;
char *answer;
int anslen;
{
	register int len, n;
	char *cp;

	cp = answer;
	len = sizeof(short);
	while (len != 0 &&
	       (n = res_vcread(s, (char *)cp, (int)len, &hs_timeout)) > 0) {
		cp += n;
		len -= n;
	}
	if (n <= 0)
		return(-1);
	cp = answer;
	if ((len = _getshort(cp)) > anslen) {
		crash("Response too long: %d", len);
	}
	while (len != 0 &&
	       (n = res_vcread(s, (char *)cp, (int)len, &hs_timeout)) > 0) {
		cp += n;
		len -= n;
	}
	if (n <= 0)
		return(-1);
	return(hs_parse(stdout, answer, answer+PACKETSZ));
}

static int
res_vcread(sock, buf, buflen, timeout)
int sock, buflen;
char *buf;
struct timeval *timeout;
{
	register int n;

	if ((n = res_selwait(sock, timeout)) > 0)
		return(read(sock, buf, buflen));
	else
		return(n);
}

static int
res_selwait(sock, timeout)
int sock;
struct timeval *timeout;
{
	fd_set dsmask;
	register int n;

	/*
	 * Wait for reply
	 */
	FD_ZERO(&dsmask);
	FD_SET(sock, &dsmask);
	n = select(sock+1, &dsmask, (fd_set *)NULL,
		   (fd_set *)NULL, timeout);
	return(n);
}

static int
add_address(addr)
struct in_addr *addr;
{
	bcopy((char *)addr, &nsaddr_list[hs_nscount++],
	      sizeof(struct in_addr));
#ifdef DEBUG
	log("Adding NS address %s",
	    inet_ntoa(nsaddr_list[hs_nscount-1].s_addr));
#endif DEBUG
}

hs_get_ns_list(domain)
char *domain;
{
	register HEADER *hp;
	int qdcount, nscount;
	register char *cp;
	register int n, len;
	char key[PACKETSZ], name[PACKETSZ], msg[PACKETSZ], *eom;
	register long **hptr;
	struct hostent *ghp;
	int numns;
	char nsname[MAXHSNS][MAXDATA];
	int nshaveaddr[MAXHSNS], i;
	short type;

	if (hs_make_ns_query(domain, msg) == -1)
		return(-1);
	numns = hs_nscount = 0;
	eom = &msg[PACKETSZ];
	bzero(nsname, sizeof(nsname));
	hp = (HEADER *)msg;
	if (hp->rcode != NOERROR || hp->opcode != QUERY) {
		fprintf(stderr, "Bad response (%d) from nameserver %s\n",
			hp->rcode, inet_ntoa(hs_server_addr(servernum)));
		return(-1);
	}
	cp = msg + sizeof(HEADER);
	qdcount = ntohs(hp->qdcount);
	while (qdcount-- > 0)
		cp += dn_skipname(cp, eom) + QFIXEDSZ;
	nscount = ntohs(hp->ancount) + ntohs(hp->nscount) + ntohs(hp->arcount);
#ifdef DEBUG
	log("hs_get_ns_list: Processing %d response records", nscount);
#endif DEBUG
	for (;nscount; nscount--) {
		if ((n = dn_expand(msg, eom, cp, key, PACKETSZ)) < 0)
			break;
		cp += n;
		type = _getshort(cp);
		cp += 2*sizeof(u_short) + sizeof(u_long);
		len = _getshort(cp);
		cp += sizeof(u_short);
#ifdef DEBUG
		log("hs_get_ns_list: Record type: %d", type);
#endif DEBUG
		switch (type) {
		case T_NS:
			if (numns >= MAXHSNS || strcasecmp(domain, key) != 0)
				break;
			if ((n = dn_expand(msg, eom, cp, name, PACKETSZ)) < 0)
				break;
#ifdef DEBUG
			log("hs_get_ns_list: NS name: %s", name);
#endif DEBUG
			for (i = 0; i < numns; i++)
				if (strcasecmp(nsname[i], name) == 0)
					break;
			if (i == numns) {
#ifdef DEBUG
				log("hs_get_ns_list: Saving name %s", name);
#endif DEBUG
				strncpy(nsname[numns], name, MAXDATA);
				nshaveaddr[numns] = 0;
				numns++;
			}
			break;
		case T_A:
			if (hs_nscount == MAXADDR)
				break;
			for (i = 0; i < numns; i++) {
				if (strcasecmp(nsname[i], domain) == 0) {
					nshaveaddr[i]++;
					add_address(cp);
					break;
				}
			}
			break;
		default:
			break;
		}
		if (hs_nscount == MAXADDR)
			break;
		cp += len;
		errno = 0;
	}
#ifdef DEBUG
	log("hs_get_ns_list: Found %d NS records", numns);
#endif DEBUG
	for (i = 0; i < numns; i++) {
		if (nshaveaddr[i])
			continue;
		if ((ghp = gethostbyname(nsname[i])) == 0)
			continue;
		for (hptr = (long **)ghp->h_addr_list;
		     *hptr && hs_nscount < MAXADDR; hptr++) {
			add_address((char *)*hptr);
		}
	}
	if (hs_nscount)
		return(0);
#ifdef DEBUG
	log("No NS records found for %s", domain);
#endif DEBUG
	return(-1);
}

hs_make_ns_query(domain, ansbuf)
char *domain;
char *ansbuf;
{
	int status, len;
	char buf[PACKETSZ];
	int class;

	class = C_HS;
	if ((len = res_mkquery(QUERY, domain, class, T_NS,
			       (char *)NULL, 0, NULL, buf, PACKETSZ)) == -1) {
		fprintf(stderr, "hs_make_ns_query: res_mkquery failed\n");
		errno = 0;
		return(-1);
	}
	if ((status = res_send(buf, len, (char *)ansbuf, PACKETSZ)) == -1) {
		fprintf(stderr, "hs_make_ns_query: res_send failed. status %d errno %d",
			status, errno);
		errno = 0;
		return(-1);
	}
	return(0);
}

/*
  crash() logs an error message via logerr(), and terminates the calling
  program.  crash()'s arguments are formated identically to the arguments
  for the system call printf.
  
  control: String to printout before exiting.
  arg1, arg2, arg3, arg4: Optional arguments which modify the control string.

  crash() does not return.
*/

crash(control, arg1, arg2, arg3, arg4)
char *control;
int arg1, arg2, arg3, arg4;
{
	logerr(control, arg1, arg2, arg3, arg4);
	exit(1);
}

/*
  logerr() logs an error message to the terminal's error output.
  logerr()'s arguments are formated identically to the arguments
  for the system call printf.
  
  control: String to print out
  arg1, arg2, arg3, arg4: Optional arguments which modify the control string.
*/

logerr(control, arg1, arg2, arg3, arg4)
char *control;
int arg1, arg2, arg3, arg4;
{
	fprintf(stderr, "Error: \t");
	flog(stderr, control, arg1, arg2, arg3, arg4);
}

/*
  flog() is the underlying routine called by log() and logerr() to perform
  the actual message typeout.  In addition to printing the user supplied
  string, flog() will print out the most recent system error condition
  (if applicable) which has been set.

  fd: The file descriptor to send the output to
  control: String to printout
  arg1, arg2, arg3, arg4: Optional arguments which modify the control string.
*/

flog(fd, control, arg1, arg2, arg3, arg4)
FILE *fd;
char *control;
int arg1, arg2, arg3, arg4;
{
	fprintf(fd, control, arg1, arg2, arg3, arg4);
	fprintf(fd, "\n");
	if (errno)
		perror("    System error string");
	errno = 0;
	fflush(fd);
}

home help back first fref pref prev next nref lref last post