DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T a

⟦dfa5d88cb⟧ TextFile

    Length: 25793 (0x64c1)
    Types: TextFile
    Names: »atalkad.c«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦3b20aab50⟧ »EurOpenD3/network/snmp/kip-snmp.91.tar.Z« 
        └─⟦b503a39fe⟧ 
            └─⟦this⟧ »kip/etc/atalkad.c« 

TextFile

#ifndef lint
static char sccsid[] = "@(#)atalkad.c	1.2 (Stanford) 9/87";
#endif

/*
 * Appletalk administration daemon.
 *
 * Answers request packets from client appletalk gateways by supplying
 * the appletalk-to-ip network configuration table and the local
 * appletalk gateway configuration information.
 */

/*
 * (C) 1986, Stanford Univ.  CSLI.
 * May be used but not sold without permission.
 */


#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/time.h>
#include <netinet/in.h>
#define iaddr_t long
#include "gwctl.h"
#include "ab.h"

#include <signal.h>
#include <stdio.h>
#include <strings.h>
#include <errno.h>
#include <ctype.h>
#include <netdb.h>

/* for 4.2 systems */
#ifndef FD_SETSIZE
# define FD_SETSIZE sizeof(int)*8
# define NFDBITS sizeof(int)*8
# define howmany(x,y) (1)
# define FD_SET(n, p)  (p)->fds_bits[0] |= (1<<(n))
# define FD_CLR(n, p)  (p)->fds_bits[0] &= ~(1<<(n))
# define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
# define FD_ZERO(p) bzero((char *)p, sizeof(*p))
#endif

#ifndef ATALKATAB
#define	ATALKATAB "/etc/atalkatab"
#endif
#ifndef ATALKALOG
#define	ATALKALOG "/usr/adm/atalkalog"
#endif
#ifndef ATALKAPID
#define	ATALKAPID "/usr/adm/atalkapid"
#endif

int	debug;
extern	int errno;
struct	sockaddr_in sin = { AF_INET };
int	s;		/* socket fd */
struct	sockaddr_in fsin; /* foreign sockaddr_in */
int	fsinlen;
int	sig, sigint(), sighup();
struct aaconf aa;	/* receive packet */
struct aaconf aas;	/* send packet */
struct timeval tvwait;	/* timer for select */

/* Mask of high 3 bytes of a network address */
iaddr_t node_mask;
  
/*
 * Globals below are associated with the atalka database file (atalkatab).
 */
char	*atalkatab = ATALKATAB;
char	*atalkalog = ATALKALOG;
char	*atalkapid = ATALKAPID;
FILE	*fp;
char	line[256];	/* line buffer for reading atalkatab */
char	*linep;		/* pointer to 'line' */
int	linenum;	/* current line number in atalkatab */

#define	NANETS	64	/* max number of 'anets' structs */

struct anets {
	u_short	net;		/* atalk net */
	u_char	flags;		/* flags, see aroute* in gwctl.h */
	u_char	confsize;	/* size of databytes in conf below */
	iaddr_t	iaddr;		/* ip address */
	char	zone[32];	/* zone name */
	char	conf[64];	/* configuration info, if kbox */
} anets[NANETS];

/* Internal format of conf structure */
struct conf {
	iaddr_t	ipbroad;		/* broadcast addr on ether */
	iaddr_t	ipname;			/* address of name server */
	iaddr_t	ipdebug;		/* address of debug host */
	iaddr_t	ipfile;			/* address of file server */
	u_long	ipother[4];		/* other addresses passed via IPGP */
#define ipsmask anetet		        /* old use for this field */
	u_short	anetet;		        /* ethertalk net number of enet */
#define ipsshift startddpWKSUnix	/* old: subnet shift (unused) */
	u_short	startddpWKSUnix;	/* start of unix WKS udp ports */
	u_long	flags;			/* various bit flags */
#define	conf_stayinzone 0x1		/* no looking at other zones */
#define conf_laserfilter 0x2		/* NBP filtering for LaserWriters */
#define	conf_tildefilter 0x4		/* NBP filtering, "name~" */
	u_short	ipstatic;		/* number of static IP addrs */
	u_short	ipdynamic;		/* number of dynamic IP addrs */
	u_short	atneta;			/* atalk net #, appletalk */
	u_short	atnete;			/* atalk net #, ethernet */
} conf_proto;

int	nanets;		/* current number of anets */
long	modtime;	/* last modification time of atalkatab */
long size;		/* last known size of file */
char	aroutes[512];	/* route tuples built by buildart() */
int	arouteslen;
char	azones[512];	/* zone table built by buildzone() */
int	azoneslen;

int scanonly = 0;


usage()
{
  printf("usage: atalkad [-c filename] [-debug] [route|boot|exit]\n");
  printf("usage:  -c means check file\n");
  exit(1);
}

main(argc, argv)
	char *argv[];
{
	register int n;
	FILE *filep;
	int pid;

	for (argc--, argv++ ; argc > 0 ; argc--, argv++) {
		if (argv[0][0] == '-') {
			switch (argv[0][1]) {
			case 'd':
				debug++;
				break;
			case 'c':
				if (argv[0][2]) {
				  atalkatab = argv[0]+2;
				} else if (argc > 1 && argv[1]) {
				  atalkatab = argv[1];
				  argc--, argv++;
				}
				scanonly++;
				break;
			default:
				usage();
			}
			continue;
		}
		if (scanonly)
		  usage();
		if (strcmp(argv[0], "boot") == 0)
			sig = SIGINT;
		else if (strcmp(argv[0], "route") == 0)
			sig = SIGHUP;
		else if (strcmp(argv[0], "exit") == 0)
			sig = SIGKILL;
		else
		  usage();
		if ((filep = fopen(atalkapid, "r")) == NULL
		    || fscanf(filep, "%d", &pid) != 1
		    || kill(pid, sig) < 0) {
			printf("failed to send signal to daemon\n");
			exit(1);
		} else {
			printf("sent signal to daemon\n");
			exit(0);
		}
	}
	
	if (scanonly) {
	  readtab();
	  dumptab();
	  exit(0);
	}

	if (debug == 0) {
		int t, f;
		if (fork())
			exit(0);
		for (f = 0; f < 10; f++)
			(void) close(f);
		(void) open("/", 0);
		(void) dup2(0, 1);
		(void) dup2(0, 2);
		t = open("/dev/tty", 2);	
		if (t >= 0) {
			ioctl(t, TIOCNOTTY, (char *)0);
			(void) close(t);
		}
	}

	log("###	ATALKA daemon starting");
	pid = getpid();
	if ((filep = fopen(atalkapid, "w")) == NULL) {
		log("couldnt create pid file %s\n", atalkapid);
		exit(1);
	}
	fprintf(filep, "%d", pid); 
	fclose(filep);
	signal(SIGHUP, sighup);
	signal(SIGINT, sigint);

	while ((s = socket(AF_INET, SOCK_DGRAM, 0, 0)) < 0) {
		log("socket call failed");
		sleep(5);
	}
	sin.sin_port = htons(aaPort);
	if (bind(s, (caddr_t)&sin, sizeof (sin), 0) < 0) {
		log("bind call failed");
		exit(1);
	}

	/* fill in node mask in a machine independent manner */
	node_mask = inet_addr("255.255.255.0");

	readtab();
	for (;;) {
		fsinlen = sizeof (fsin);
		n = recvfrom(s, (caddr_t)&aa, sizeof aa,
			0, (caddr_t)&fsin, &fsinlen);
		if (n < 0) {
			if (errno != EINTR) {
				log("recv failed");
				exit(1);
			}
			readtab();
			sendall(sig);
			continue;
		}
		if (n < aaconfMinSize || ntohl(aa.magic) != aaMagic)
			continue;
		readtab();
		sendreply();
	}
}


/*
 * Interrupts are used to signal type of sendall().
 */
sighup() { sig = aaROUTEI; }
sigint() { sig = aaRESTART; }


/*
 * Send reply to packet aa.
 */
sendreply()
{
	register struct anets *an;
	register n;

	switch (aa.type) {
	case aaCONF:
		for (an = &anets[0], n = 0 ; n < nanets ; an++, n++)
			if (an->iaddr == aa.ipaddr && an->confsize &&
			    (an->flags & arouteKbox) &&
			    (an->flags & arouteEtalk) == 0)
				goto found;
		log("aaCONF from %s ***** not in table", inet_ntoa(aa.ipaddr));
		return;
found:
		log("aaCONF to %s", inet_ntoa(aa.ipaddr));
		n = an->confsize;
		bcopy(an->conf, aa.stuff, n);
		break;
	case aaZONE:
		log("aaZONE to %s", inet_ntoa(aa.ipaddr));
		n = azoneslen;
		bcopy(azones, aa.stuff, n);
		break;
	case aaZONEQ:
		log("aaZONEq from %s", inet_ntoa(aa.ipaddr));
		if ((n = zipinput()) <= 0)
		  return;	/* drop */
		break;
	case aaROUTEI:
		log("aaROUTEI to %s", inet_ntoa(aa.ipaddr));
		n = arouteslen;
		bcopy(aroutes, aa.stuff, n);
		break;
	default:
		/* don't reply here! */
		return;
	}
	aa.count = htons(n);
	sendto(s, (caddr_t)&aa, aaconfMinSize + n, 0,
	       &fsin, sizeof fsin);
}


char *
netstring(n)
u_short n;
{
  int net;
  static char mynetstr[30];

  net = ntohs(n);
  sprintf(mynetstr, "%d.%d", (net>>8) & 0xff, net & 0xff);
  return(mynetstr);
}

/*
 * return pointer to zone name
 * NULL if none
*/
char *
zoneof(net)
u_short net;
{
  register struct anets *an;
  int n;

  for (an = &anets[0], n=0 ; n < nanets ; an++, n++)
    if (net == an->net)
      return(an->zone);
  return(NULL);
}

/*
 * return a value that is always printable
 *
*/
char *
pzoneof(net)
u_short net;
{
  char *p = zoneof(net);
  return(p == NULL ? "unknown" : p);
}

dumptab()
{
  register struct anets *an;
  register n, size;
  char *hostnameof();
  struct conf *conf;
  iaddr_t rs, re;

  size = 0;
  for (an = &anets[0], n = 0 ; n < nanets ; an++, n++) {
    printf("Route %s %s, ", netstring(an->net), an->zone);
    if ((an->flags & arouteKbox) && (an->flags & arouteEtalk) == 0)
      printf(" Kinetics box");
    if (an->flags & arouteCore)
      printf(" core gateway");
    if (an->flags & arouteHost)
      printf(" host is redirector");
    if (an->flags & arouteNet)
      if (an->flags & arouteBMask)
	printf(" net %d", an->flags & arouteBMask);
      else
	printf(" net 0: host is client");
    if ((an->flags & (arouteKbox|arouteEtalk)) == (arouteKbox|arouteEtalk))
      printf(" EtherTalk");
    printf(" at %s", hostnameof(an->iaddr));
    putchar('\n');
    if ((an->flags & arouteKbox) && (an->flags & arouteEtalk) == 0) {
      conf = (struct conf *)an->conf;
      printf("\tIP Broadcast: %s, ", inet_ntoa(conf->ipbroad));
      if (conf->ipname == conf->ipdebug) {
	printf("IP name and debug: %s\n", hostnameof(conf->ipname));
	printf("\tIP file server (unused)", hostnameof(conf->ipfile));
      } else {
	printf("IP name: %s\n",hostnameof(conf->ipname));
	printf("\tIP debug %s, ", hostnameof(conf->ipdebug));
	printf("IP file server (unused)", hostnameof(conf->ipfile));
      }
      if (ntohl(conf->flags) & conf_stayinzone)
	printf(", marked stay in zone");
      if (ntohl(conf->flags) & conf_laserfilter)
	printf(", laserwriters stay in zone");
      if (ntohl(conf->flags) & conf_tildefilter)
	printf(", tilde marked names stay in zone");
      putchar('\n');
      if (conf->startddpWKSUnix == htons(defddpWKSUnix))
	printf("\tUDP Port range for WKS starts at %d (old range)\n",
	       defddpWKSUnix);
      else
	printf("\tUDP Port range for WKS starts at %d\n",
	       ntohs(conf->startddpWKSUnix));
#ifdef SHORTFORMAT
      if (conf->ipstatic)
	printf("\t%d ", ntohs(conf->ipstatic));
      else
	printf("\tNo");
      printf(" static ip addresses, ");
      if (conf->ipdynamic)
	printf("\t%d ", ntohs(conf->ipdynamic));
      else
	printf("\tNo");
      printf(" dynamic ip addresses\n");
#else
      if (conf->ipstatic) {
	re = rs = ntohl(an->iaddr)+1;
	re += (ntohs(conf->ipstatic)-1);
	printf("\tIP static address range: %s",
	       inet_ntoa(htonl(rs)));
	printf(" %s\n",inet_ntoa(htonl(re)));
      } else {
	printf("\tIP static address range: empty\n");
	re = ntohl(an->iaddr);
      }
      if (conf->ipdynamic) {
	re = rs = re+1;
	re += (ntohs(conf->ipdynamic)-1);
	printf("\tIP dynamic address range: %s",
	       inet_ntoa(htonl(rs)));
	printf(" %s\n",inet_ntoa(htonl(re)));
      } else {
	printf("\tIP dynamic address range: empty\n");
	re = ntohl(an->iaddr);
      }

#endif
      printf("\tKbox interfaces:\n");
      printf("\t\tlocaltalk: %s zone %s\n", netstring(conf->atneta),
	     pzoneof(conf->atneta));
      printf("\t\tKIP: %s zone %s\n", netstring(conf->atnete),
	     pzoneof(conf->atnete));
      if (conf->anetet) {
	printf("\t\tEtherTalk: %s zone %s\n", netstring(conf->anetet),
	       pzoneof(conf->anetet));
      }
    }
  }
}

/*
 * Build arouteTuple's into area provided by caller.
 * Return byte count of tuples deposited.
 */
buildart(at, maxsize)
	register struct arouteTuple *at;
        int maxsize;
{
	register struct anets *an;
	register n, size;

	size = 0;
	for (an = &anets[0], n = 0 ; n < nanets ; an++, n++, at++) {
		size += sizeof *at;
		at->node = an->iaddr;
		at->net = an->net;
		at->flags = an->flags;
		at->hops = 0; /* start at one - incremented in gateway */
	}
	log("art build: %d entries, %d maximum",
	    size/(sizeof(struct arouteTuple)),
	    maxsize/(sizeof(struct arouteTuple)));
	if (size > maxsize)
		log("art build: routing table too big!!");
	if (size > (maxsize - 10) )
		log("art build: routing table size %d is near maximum of %d",
		    size, maxsize );
	return (size);
}


#define	MAXZ	32

/*
 * Build zones structure to be returned by aaZONE.
 */
buildzones(az, azlen)
	char *az;
	int azlen;
{
	char zname[MAXZ][33];
	int nzname = 0;
	char netzone[NANETS];
	register i, iz, n;
	register char *cp;
	int zonenamelen = 0;

	/* make a pass thru anets, finding all unique zone names. */

	for (i = 0 ; i < nanets ; i++) {
		for (iz = 0 ; iz < nzname ; iz++)
			if (strcmp(anets[i].zone, zname[iz]) == 0)
				goto found;
		/* not found, make a new name */
		strcpy(zname[iz], anets[i].zone);
		zonenamelen += strlen(zname[iz]) + 1;
		netzone[i] = iz;
		nzname++;
		continue;
		/* found it, note the zname index */
found:
		netzone[i] = iz;
	}
	/*
	 * Build structure to send to gateway.  Looks like:
	 * net# net# ... 0 zonename
	 * net# net# ... 0 zonename
	 * 0xFFFF.
	 */
	cp = az;
	for (iz = 0 ; iz < nzname ; iz++) {
		for (i = 0 ; i < nanets ; i++) {
			if (netzone[i] != iz)
				continue;
			/* anets[].net has been swapped by readtab */
			*cp++ = (ntohs(anets[i].net) >> 8);
			*cp++ = ntohs(anets[i].net);
		}
		*cp++ = 0;  *cp++ = 0;
		*cp++ = n = strlen(zname[iz]);
		bcopy(zname[iz], cp, n);
		cp += n;
	}
	*cp++ = 0xFF;  *cp++ = 0xFF;
	n = cp - az;		/* length of table */
	if (n > sizeof(azones)) {
	  log("buildzones: ZIP table too large");
	 log("buildzones: don't worry if you are using a KIP later than 1/88");
	} else if (n > (sizeof(azones)-10))
	  log("buildzones: ZIP table size %d approaching maximum of %d",
	      n, sizeof(azones));
	log("zone names take %d bytes in gateway");
	return (n);
}


/*
 * Send aaRESTART or aaROUTEI to all gateways.
 */
sendall(sig)
{
	register struct anets *an;
	register n;
	int trys, count, rcount;
	fd_set fds;
	struct sockaddr_in tsin;

	tsin = sin;
	log("sendall(%s)", sig == aaRESTART ? "aaRESTART" : "aaROUTEI");
	aas.magic = htonl(aaMagic); /* setup send packet */
	aas.type = sig;
	aas.flags = 0;
	if (sig == aaROUTEI) {
		bcopy(aroutes, aas.stuff, arouteslen);
		count = arouteslen;
	} else {
		count = 0;
	}
	aas.count = htons(count);
	count += aaconfMinSize;
	/*
	 * send to each kbox in the table.
	 */
	for (an = &anets[0], n = 0 ; n < nanets ; an++, n++) {
		if ((an->flags & arouteKbox) == 0 || (an->flags & arouteEtalk))
			continue;
		trys = 0;
		tsin.sin_addr.s_addr = an->iaddr;
		sendto(s, (caddr_t)&aas, count, 0, &tsin, sizeof tsin);
		/*
		 * receive until we get a good reply or timeout.
		 */
		for (;;) {
			FD_ZERO(&fds);
			FD_SET(s, &fds);
			tvwait.tv_sec = 2;  /* select waits 2 seconds */
			tvwait.tv_usec = 0;
			if (select(NFDBITS, &fds, 0, 0, &tvwait) != 1) {
				/* timeout */
				if (++trys < 4) {
					sendto(s, (caddr_t)&aas, 
					    count, 0, &tsin, sizeof tsin);
					continue;
				}
				log("no response from %s",
				    inet_ntoa(an->iaddr));
				break;
			}
			fsinlen = sizeof fsin;
			rcount = recvfrom(s, (caddr_t)&aa, sizeof aa,
			    0, (caddr_t)&fsin, &fsinlen);
			if (rcount < 0) {
				if (errno == EINTR)
					continue;
				log("recv failed");
				exit(1);
			}
			if (rcount < aaconfMinSize 
			    || ntohl(aa.magic) != aaMagic)
				continue;
			if (aa.ipaddr != an->iaddr) {
				sendreply();
				continue;
			}
			/* our request got thru! */
			sendreply();
			break;
		}
	}
	log("sendall(%s) complete",sig==aaRESTART ? "aaRESTART" : "aaROUTEI");
}

/*
 * check over configurations
 *
*/
checkconfigs()
{
  int  i;
  struct anets *an;
  struct conf *cp;
  int rangestart = 0;
  int rangeconflict = 0;
  int badrange = 0;

  for (an = anets, i = 0; i < nanets; i++, an++) {
    if (an->confsize >= sizeof(struct conf)) {
      cp = (struct conf *)an->conf;
      if (!cp->startddpWKSUnix) {
	cp->startddpWKSUnix = htons(defddpWKSUnix);
	if (rangestart) {
	  if (rangestart != cp->startddpWKSUnix)
	    rangeconflict = 1;
	} else rangestart = cp->startddpWKSUnix;
      } else {
	if (rangestart) {
	  if (rangestart != cp->startddpWKSUnix)
	    rangeconflict = 1;
	} else rangestart = cp->startddpWKSUnix;
      }
    }
  }
  rangestart = ntohs(rangestart);
  if (rangeconflict)
    log("Conflict in UDP WKS range start!");
  else if (rangestart != 200 && rangestart != defddpWKSUnix) {
    log("WARNING - UDP WKS range start %d is non-standard",
	ntohs(rangestart));
  }
}

/*
 * Read atalkatab database file.  Avoid rereading the file if the
 * write date hasnt changed since the last time we read it.
 */
readtab()
{
	struct stat sbuf1, sbuf;
	register char *sp, *cpp;
	int v;
	register i;
	char st[128], *cp;
	register struct anets *an;
	iaddr_t iaddr;

	if (fp == 0) {
		if ((fp = fopen(atalkatab, "r")) == NULL) {
			log("can't open %s", atalkatab);
			exit(1);
		}
	}
	if (fstat(fileno(fp), &sbuf1) < 0)
	  goto reopen;
	if (stat(atalkatab, &sbuf) < 0)
	  goto reopen;
	if (sbuf1.st_dev == sbuf.st_dev && /* different... */
	    sbuf1.st_ino == sbuf.st_ino && /* ...file */
	    sbuf1.st_size == size && /* nec? */
	    sbuf.st_size == size &&
	    sbuf1.st_mtime == modtime && /* nec? */
	    sbuf.st_mtime == modtime)
	  return;
reopen:	
  	if (fp)
          fclose(fp);
	if ((fp = fopen(atalkatab, "r")) == NULL) {
		log("can't open %s", atalkatab);
		exit(1);
	}
	fstat(fileno(fp), &sbuf);
	log("(re)reading %s", atalkatab);
	modtime = sbuf.st_mtime;
	size = sbuf.st_size;
	nanets = 0;
	an = &anets[-1];
	linenum = 0;

	/*
	 * read and parse each line in the file.
	 */
	for (;;) {
		if (fgets(line, sizeof line, fp) == NULL)
			break;	/* done */
		if ((i = strlen(line)))
			line[i-1] = 0;	/* remove trailing newline */
		linep = line;
		linenum++;
		if (line[0] == '#' || line[0] == 0)
			continue;	/* skip comment lines */
		if (line[0] == ' ' || line[0] == '\t') 
			goto confinfo;
		/*
		 * lines not beginning with white space 
		 * represent a new net #
		 */
		if (++nanets > NANETS) {
			log("'anets' table length exceeded");
			exit(1);
		}
		an++;
		cp = an->conf;	/* store following lines here */
		an->confsize = 0;
		getfield(st, sizeof st);
		an->net = htons(getashort(st));
		getfield(st, sizeof st);
		i = 0;
		/* parse flags */
		for (cpp = st ; *cpp ; cpp++) {
			if (isupper(*cpp))
				*cpp = tolower(*cpp);
			switch (*cpp) {
			case 'c':
				i |= arouteCore;  break;
			case 'k':
				i |= arouteKbox;  break;
			case 'h':
				i |= arouteHost;  break;
			case 'n':
				i |= arouteNet;  break;
 		        case 'e':
				i |= (arouteEtalk|arouteKbox); break;
			case '0': case '1': case '2': case '3':
				i |= (*cpp - '0');  break;
			default:
				log("bad switch %s, linenum %d", st, linenum);
			}
		}
		an->flags = i;
		getfield(st, sizeof st);
		an->iaddr = getiaddr(st);
		getfield(an->zone, sizeof(an->zone));
		continue;
confinfo:
		/*
		 * lines beginning with white space
		 * are configuration data for gateway.
		 */
		for (;;) {	/* for each field in line */
			int len;
			getfield(st, sizeof st);
			sp = st;
			if (*sp == 0)
				break;
			if (isupper(*sp))
				*sp = tolower(*sp);
			switch (*sp++) {
			case '%':
			  /* escape code, one of 'n' */
			  if (isupper(*sp))
			    *sp = tolower(*sp);
			  switch (*sp++) {
			    int flag;
			  case 'n':
			    /* decide which type of network based on */
			    /* the offset in the config structure */
			    if (cp - an->conf ==
				(char *)&conf_proto.atnete-(char *)&conf_proto)
			      flag = arouteNet;
			    else if (cp - an->conf ==
				(char*)&conf_proto.anetet-(char*)&conf_proto)
			      flag = arouteKbox|arouteEtalk;
			    else if (cp - an->conf ==
				(char*)&conf_proto.atneta-(char*)&conf_proto)
			      flag = arouteKbox;
			    else {
			      log("%%N at unexpected offset %d, line %d\n",
				  cp - an->conf, linenum);
			      flag = arouteKbox;
			    }

			    if ((*(short *)cp = 
				 getanet(flag, an->iaddr)) == 0)
			      log("%%N not set, line %d\n", linenum);
			    cp += 2;
			    an->confsize += 2;
			    break;

			  default:
			    log("bad field type %s, linenum %d", st, linenum);
			    break;
			  }
			  break;
			  
			case 'i':
				/* IP address name or number */
				iaddr = getiaddr(sp);
				bcopy((caddr_t)&iaddr, cp, sizeof iaddr);
				cp += sizeof iaddr;
				an->confsize += sizeof iaddr;
				break;

			case 'l':
				if ((int)cp & 1) 
					goto badalign;
				*(long *)cp = htonl(atoii(sp));
				cp += 4;
				an->confsize += 4;
				break;

			case 's':
				if ((int)cp & 1) 
					goto badalign;
				*(short *)cp = htons(getashort(sp));
				cp += 2;
				an->confsize += 2;
				break;
			
			case 'c':
				*cp = atoii(sp);
				cp++;
				an->confsize++;
				break;

			case '"':
				len = strlen(sp) - 1; /* drop trailing " */
				bcopy(sp, cp, len);
				cp += len;
				*cp++ = 0;
				len++;
				an->confsize += len;
				break;

			case '\'': /* pascal string */
				len = strlen(sp) - 1; /* drop trailing ' */
				*cp++ = len;
				bcopy(sp, cp, len);
				cp += len;
				len++; /* skip over length field */
				*cp++ = 0;
				len++;
				an->confsize += len;
				break;
			
			default:
				log("bad field type %s, linenum %d",
					st, linenum);
				break;
			}
			continue;	/* get next field in line */
badalign:
			log("long/short bad alignment %s, linenum %d",
				st, linenum);
			break;
		}
		/* get next line */
	}
	/* end of file */
	checkconfigs();
	arouteslen = buildart((struct arouteTuple *)aroutes, sizeof aroutes);
	azoneslen = buildzones(azones, sizeof azones);
}


/*
 * Get next field from 'line' buffer into 'str'.  'linep' is the 
 * pointer to current position.  No imbeded spaces are allowed in the
 * fields;  however, an underscore '_' will convert to a space.  (To get
 * a real underscore, double it.)
 */
getfield(str, len)
	char *str;
{
	register char *cp = str;
	register und;

	for ( ; *linep && (*linep == ' ' || *linep == '\t') ; linep++)
		;	/* skip spaces/tabs */
	if (*linep == 0 || *linep == '#') {
		*cp = 0;
		return;
	}
	und = 0;
	len--;	/* save a spot for a null */
	for ( ; *linep && *linep != ' ' 
	    && *linep != '\t' && *linep != '#' ; linep++) {
		if (*linep == '_') {
			if (und) {
				und = 0;  cp--;  len++;
			} else {
				und++;  *linep = ' ';
			}
		} else {
			und = 0;
		}
		*cp++ = *linep;
		if (--len <= 0) {
			*cp = 0;
			log("string truncated: %s, linenum %d", str, linenum);
			return;
		}
	}
	*cp = 0;
}


/*
 * Ascii to integer, with base check.
 */
atoii(s)
	register char *s;
{
	int v;
	char *c;

	if (isupper(*s))
		*s = tolower(*s);
	if (*s == 'x' || *s == 'h') {
		c = "%x";
		s++;
	} else {
		c = "%d";
	}
	if (sscanf(s, c, &v) != 1)
		log("bad numeric field %s, linenum %d", s, linenum);
	return (v);
}


/*
 * Get an internet address as a hostname or dot format string.
 */
getiaddr(st)
	register char *st;
{
	iaddr_t iaddr;
	register struct hostent *host;

	if (isdigit(*st)) {
		if ((iaddr = inet_addr(st)) == -1 || iaddr == 0)
			log("bad ipaddress %s, linenum %d", st, linenum);
	} else {
		if ((host = gethostbyname(st)) == 0)
			log("bad hostname %s, linenum %d", st, linenum);
		bcopy(host->h_addr, (caddr_t)&iaddr, sizeof iaddr);
	}
	return (iaddr);
}


/*
 * Get a short number or address.
 */
getashort(st)
	register char *st;
{
	register char *cp;

	if ((cp = index(st, '.')) == 0)
		return (atoii(st));
	*cp++ = 0;
	return ((atoii(st)<<8) | atoii(cp));
}


/*
 * log an error message 
 */
log(fmt, args)
char *fmt;
{
	FILE *fp;
	long time(), tloc;
	struct tm *tm, *localtime();

	if (debug)
	  fp = stderr;
	else if (scanonly)
	  fp = stdout;
	else if ((fp = fopen(atalkalog, "a+")) == NULL)
	  return;
	time(&tloc);
	tm = localtime(&tloc);
	fprintf(fp, "%d/%d %02d:%02d ", tm->tm_mon + 1, tm->tm_mday,
		tm->tm_hour, tm->tm_min);
	_doprnt(fmt, &args, fp);
	putc('\n', fp);
	if (fp != stderr && fp != stdout)
		fclose(fp);
}
\f


/*
 * getanet - scan through the table so far and find an appletalk network 
 * number. Called from the %x fields of the config structure
 *
 * flag field:
 *    arouteNet - match an IP address, scan for a net route that matches addr
 *    arouteKbox - scan for a route using this host
 *    arouteKbox|arouteETalk - scan for an Ethertalk net using this host.
 *
 * Limitations: Only one bridge can be configured with a given Ethertalk
 * network number this way. Network numbers referenced must be defined before
 * use.
 */
getanet(flags, ia)
     iaddr_t ia;
{
  register int i;
  struct anets *a;

  /* Scan table in reverse order - slightly more efficent */
  for(i = nanets-1; i >= 0; --i) {
    a = &anets[i];

    if ((flags == arouteNet &&
	 ((a->flags & arouteNet) != 0 || (a->flags & arouteHost) != 0) &&
	 match_net(a->flags, ia, a->iaddr)) ||
	(flags == arouteKbox &&
	 (a->flags & arouteKbox|arouteEtalk) == arouteKbox &&
	 a->iaddr == ia) ||
	(flags == arouteKbox|arouteEtalk &&
	 (a->flags & flags) == flags &&
	 a->iaddr == ia))
      return(a->net);
  } 
  log("unable to match appletalk net (%x), line %d\n", flags, linenum);
  return(0);
}

/*
 * match_net - match a network number, using mask field stored in
 * flags.
 */
match_net(flags, a1, a2)
     iaddr_t a1, a2;
{
  return((a1 & node_mask) == (a2 & node_mask));
}

char *
hostnameof(ipaddr)
struct in_addr ipaddr;
{
  struct hostent *host;

  host = gethostbyaddr(&ipaddr.s_addr, sizeof(ipaddr.s_addr), AF_INET);
  if (!host)
    return((char *)inet_ntoa(ipaddr));
  return(host->h_name);
}

zipinput()
{
  u_short buf[1000];
  u_short *bp;
  int icount;
  int len;
  u_char *p;
  char *zn;
  int zl;
  struct ZIP zipin;
  struct ZIP *z;

  icount = ntohs(aa.count);		/* remember incoming count */
  bcopy(aa.stuff, &zipin, sizeof(struct ZIP));
  if (zipin.command != zipQuery || zipin.count == 0)
    return(-1);
  /* get the list of networks */
  bcopy(aa.stuff+sizeof(struct ZIP), buf, zipin.count * sizeof(u_short));
  bp = buf;
  z = (struct ZIP *)aa.stuff;
  z->command = zipReply;
  z->count = 0;
  len = sizeof(struct ZIP);
  for (p = (u_char *)(z+1); len<512&&zipin.count; zipin.count--, bp++) {
    if ((zn = zoneof(*bp)) == NULL)
      continue;
    bcopy(bp, p, sizeof(u_short)); /* copy in network */
    p+= sizeof(u_short);	/* move along */
    len+= sizeof(u_short);	/* move along */
    zl = *p = strlen(zn);	/* get zone length */
    p++;			/* bounce */
    len++;
    z->count++;
    bcopy(zn, p, zl);		/* copy in zone name */
    p += zl;			/* bounce p ...*/
    len += zl;			/* ... and len */
  }
  return(len);
}