[36] in Hesiod
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);
}