/*
 * $Id: atalkad.c,v 2.3beta $
 */

#define OBSOLETE 1	/* for removing "M" and proxy table stuff */

#ifndef lint
static char rcsid[]  = "@(#)$Id: atalkad.c,v 2.2 1993/05/05 07:35:21 tom Exp tom $";
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.
 */

/*
 * $Log: atalkad.c,v $
 *
 * Revision 2.3beta  2003jul02 lw
 * Experimental version - not for release.
 * Simple name change to avoid using same name in multiple ways
 *
 * Revision 2.2  1993/05/05  07:35:21  tom
 * Fix bug in getiaddr() that could core-dump when gethostbyname()
 * failed.
 *
 * Revision 2.1  1992/09/10  04:38:05  tom
 * Atalkad 2. Check-in to RCS.
 *
 * Revision 2.01.1 Beta 92/07/02 tae
 * Fixups from Mark Wahl at NASA - spotted "sizeof(an->zone.zone)"
 * (second ".zone" missing) in getfield.
 *
 * Revision 2.01 Beta 92/06/15 tae
 * Remove M-line and Proxy table code - controlled by "OBSOLETE".
 * Add Phase-2 network Network Range and Zone LIst parsing and
 * reporting code. Add Phase-2 "Double-Tuple" code as proposed
 * by Phil Budne at Shiva. Phil added KSTAR Phase-2 Configuration
 * printing code.
 *
 * Revision 1.25  91/01/25  16:48:17  kre
 * Support various OS bugs that we can't easily avoid.
 * Generate less noise in some cases, and add some reassurance
 * where appropriate.
 * 
 * Revision 1.24  90/06/08  09:50:06  kre
 * Log version info in various places (startup in log, in usage, with
 * output from -c).  Move "core" flag from M line to first following K line,
 * so the gateways actually get to see it.
 * 
 * Revision 1.23  90/02/27  06:07:47  kre
 * Fix stupid () botch. -- david@wolfen.cc.uow.oz.au
 * 
 * Revision 1.22  90/02/24  19:38:04  kre
 * Handle (old style) zone table overflow properly (or at least
 * rationally), and sort the proxy arp table properly on little
 * endian hosts.
 * 
 * Revision 1.21  90/01/04  02:05:50  djh
 * Fix minor bug in aaPROXY code.
 * 
 * Revision 1.20  89/09/04  00:09:20  djh
 * Add code to allow many more than 64 appletalk nets (an early KIP limitation)
 * Do this by designing a protocol to allow a gateway to know and
 * be able to ask for more than one aaROUTEI or aaPROXY packet (kre & djh)
 * 
 * Revision 1.15  89/04/07  02:05:35  kre
 * Add 'M' line type for multigates, to squeeze a few net numbers out of
 * the routing tables so we can add more before we run out of space.
 * Sort and compress the proxy table, uses much less space and is much
 * neater.  Alloy 0x for hex humbers (also 0h!) as well as the bare x or
 * h, and also leading 0 for octal (this may break atalkatab files).
 * Get rid of the _ -> space abomination and support quoting instead.
 * Don't add a trailing null to pascal type strings ('...') in the config
 * data section, this type was never documented, and is never used, but
 * should be done correctly.  Several straight out bug fixes.  Lots of
 * fluff removed (almost lint free now, just difference of opinion about
 * what type signal functions should be, void or int).  Also added VARARGS
 * support (defined with -DVARARGS) for hosts that need varargs to work.
 * 
 * Revision 1.14  89/03/21  00:29:08  kre
 * Upgrade for KIP0688 compatability - needed for Megan 1.14 ->
 * 
 * Revision 1.10  89/03/20  23:34:48  kre
 * Initial version (Megan 1.10->1.13)
 */

#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 unsigned long
#include "gwctl.h"
#include "atalk.h"

#include <signal.h>
#include <stdio.h>
#include <strings.h>
#ifdef applec
int readtab(), dumptab();
#define index(a, b) strchr(a, b)
#define bcopy(a, b, c) memcpy(b, a, c)
#include <string.h>
#include <StdLib.h>
#include <inet.h>
#endif
#include <errno.h>
#include <ctype.h>
#include <netdb.h>

#include <setjmp.h>

#ifdef SYSLOG
#include <syslog.h>
#endif /* SYSLOG defined */

#if (defined(SYSLOG) || defined(sparc)) && !defined(VARARGS)
/* sparc and SYSLOG both require VARARGS */
#define VARARGS
#endif

/* 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/spool/log/atalkalog"
#endif
#ifndef ATALKAPID
#define	ATALKAPID "/etc/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;

/*
 * jmp_buf & enabling flag for workaround of yet another SunOS 4.1 bug
 */
jmp_buf	xgoto;
int	rcving;
  
/*
 * 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 ROUTEPKTSIZ 512	/* the absolute maxm */
/*
 * Each "N", "K", "E" or "H" line in atalkatab requires one "anets"
 * and one "aroutes" entry. Each line that has a network range or
 * a "B" flag takes one extra "aroutes" entry. Each zone in a zonelist
 * apart from the initial one requires a "zone" entry. Edit the three
 * following definitions to be appropriate for your Internet.
 */
#define	NANETS	1024	/* max number of 'anets' structs */
#define	NAROUTES 1024	/* max number of 'aroutes' structs */
#define	NXZONES	100	/* max number of 'extra zones' structs */

struct zone_ent {
	struct zone_ent *next;
	char zone[33];
	char pad1[3];
};

struct zone_ent zonelist[NXZONES + 1];

struct anets {
	u_short	net;		/* atalk net */
	u_short	hnet;		/* atalk net high range */
	iaddr_t	iaddr;		/* ip address */
	struct zone_ent zone;	/* zone and pointer to more */
	iaddr_t broadcast;	/* extra broadcast mask */
	u_char	flags;		/* flags, see aroute* in gwctl.h */
	u_char	confsize;	/* size of databytes in conf below */
	u_short	pad;
	char	conf[64];	/* configuration info, if kbox */
} anets[NANETS];

#define	typeFlags	(arouteKbox|arouteNet|arouteHost|arouteBMask)

#ifndef OBSOLETE
#define	NMGATES	128	/* max number of multigates */

struct mgates {
	struct	anets	*first;
	struct	anets	*last;
} mgates[NMGATES];

#endif OBSOLETE

/* 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 */
	u_short	anetet;		        /* ethertalk net number of enet */
	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~" */
#define conf_etalk2	0x8		/* EtherTalk Phase 2 info present */
	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 */
	u_short anetet2start;		/* ethertalk2 start net # */
	u_short anetet2end;		/* ethertalk2 end net # */
} conf_proto;

int	nanets;		/* current number of anets */

#ifndef OBSOLETE
int	nmgates;	/* number of multigtates */
#endif

long	modtime;	/* last modification time of atalkatab */
long	size;		/* last known size of file */
			/* route tuples built by buildart() */
char	aroutes[NAROUTES * sizeof (struct arouteTuple)];
int	arouteslen;
char	azones[512];	/* zone table built by buildzone() */
int	azoneslen;
#ifndef OBSOLETE
char	aproxy[8192];	/* nbp proxy table built by buildproxy() (> 128*5*12) */
int	aproxylen;
#endif

int scanonly = 0;

#define	Printf	(void)printf		/* de lint */
#define	Fprintf	(void)fprintf		/* de lint */

char *
atvers()
{
	static char Vers[] = "$Revision: 2.2 $";
	register char *p;

	if (p = (char *)index(Vers+2, (int)'$')) {
		*p = '\0';
		if (*--p == ' ')
			*p = '\0';
	}

	if (p = (char *)index(Vers, (int)':')) {
		if (p[1] == ' ')
			return (p + 2);
		else
			return (p + 1);
	} else
		return ("unknown");
}

usage()
{
  Printf("atalkad version %s\n", atvers());
  Printf("usage: atalkad [-c filename] [-f 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;
	unsigned long inet_addr();

	for (argc--, argv++ ; argc > 0 ; argc--, argv++) {
		if (argv[0][0] == '-') {
			switch (argv[0][1]) {
			case 'd':
				debug++;
				break;
			case 'c':
				scanonly++;
				/* fall */
			case 'f':
				if (argv[0][2]) {
				  atalkatab = argv[0]+2;
				} else if (argc > 1 && argv[1]) {
				  atalkatab = argv[1];
				  argc--, argv++;
				}
				break;
			default:
				usage();
			}
			continue;
		}
		if (scanonly)
			usage();

#ifndef applec
		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);
		}
#endif
	}
	
	if (scanonly) {
		Printf("Atalkad version %s\n", atvers());
		readtab();
		dumptab();
		exit(0);
	}

#ifdef SYSLOG
#ifdef FACILITY
	openlog( "atalkad", LOG_PID, FACILITY );
#else  /* FACILITY not defined */
#ifdef LOG_LOCAL4
	openlog( "atalkad", LOG_PID, LOG_LOCAL4 );
#else  /* LOG_LOCAL4 not defined */
	openlog( "atalkad", LOG_PID );	/* 4.2 BSD for example */
#endif /* LOG_LOCAL4 not defined */
#endif /* FACILITY not defined */
#endif /* SYSLOG defined */

#ifndef applec
	if (debug == 0) {
		register int f;
		register char *p;
		extern char *rindex();

		if (fork())
			exit(0);
		for (f = getdtablesize(); --f >= 0; )
			(void) close(f);
		(void) open("/", 0);
		(void) dup2(0, 1);
		(void) dup2(0, 2);
#ifdef TIOCNOTTY
		f = open("/dev/tty", 2);	
		if (f >= 0) {
			(void)ioctl(f, TIOCNOTTY, (char *)0);
			(void) close(f);
		}
#endif
		p = rindex(atalkatab, '/');
		if (p)
			*p = 0;
		(void)chdir(*atalkatab ? atalkatab : "/");
		if (p)
			*p = '/';
	}

	(void)signal(SIGHUP, sighup);
	(void)signal(SIGINT, sigint);
	pid = getpid();

	while ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		log("ATALKAD(%d): socket call failed", pid);
		sleep(30);
	}
	sin.sin_port = htons(aaPort);
	if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
		log("ATALKAD(%d): bind call failed (another daemon?)", pid);
		exit(1);
	}

	log("###	ATALKA daemon (%s) starting (%d)", atvers(), pid);
	if ((filep = fopen(atalkapid, "w")) == NULL) {
		log("couldnt create pid file %s\n", atalkapid);
		exit(1);
	}
	Fprintf(filep, "%d\n", pid); 
	(void)fclose(filep);

	/* fill in node mask in a machine independent manner */
	node_mask = inet_addr("255.255.255.0");

	readtab();
	for (;;) {
		if (sig != 0) {
			readtab();
			sendall(sig);
			sig = 0;
		}
		if (setjmp(xgoto) != 0) {
			rcving = 0;
			continue;
		}
		rcving = 1;
		fsinlen = sizeof (fsin);
		n = recvfrom(s, (caddr_t)&aa, sizeof aa, 0,
		    (struct sockaddr *)&fsin, &fsinlen);
		rcving = 0;
		if (n < 0) {
			if (errno != EINTR) {
				log("recv failed");
				exit(1);
			}
			continue;
		}
		if (n < aaconfMinSize || ntohl(aa.magic) != aaMagic)
			continue;
		readtab();
		sendreply();
	}
#endif
}

#ifndef applec
/*
 * Interrupts are used to signal type of sendall().
 */
sighup()
{
	sig = aaROUTEI;
	if (rcving)
		longjmp(xgoto, 1);
}

sigint()
{
	sig = aaRESTART;
	if (rcving)
		longjmp(xgoto, 1);
}


/*
 * Send reply to packet aa.
 */
sendreply()
{
	register struct anets *an;
	register n;
	register offset;
	struct in_addr A;
	char *inet_ntoa();

	A.s_addr = aa.ipaddr;
	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(A));
		return;
found:
		log("aaCONF to %s", inet_ntoa(A));
		n = an->confsize;
		bcopy(an->conf, (char *)aa.stuff, n);
		break;
	case aaZONE:
		log("aaZONE to %s", inet_ntoa(A));
		n = azoneslen;
		bcopy(azones, (char *)aa.stuff, n);
		break;
	case aaZONEQ:
		log("aaZONEq from %s", inet_ntoa(A));
		if ((n = zipinput()) <= 0)
		  return;	/* drop */
		log("aaZONEq reply to %s (len %d)", inet_ntoa(A), n);
		break;
	case aaPROXYQ:
	case old_aaPROXYQ:		/*ZZZ* transition stuff */
#ifndef OBSOLETE
		log("aaPROXY to %s", inet_ntoa(A));
		if(aa.flags == 0)
			offset = 0;
		else {
			offset = aa.flags*504;
			if(offset > aproxylen) { /* trouble */
				offset = 0;
				log("Bozo aaPROXYQ flag length");
			}
		}
		n = aproxylen - offset;
		if(n > 504)
			n = 504;
		bcopy(aproxy+offset, (char *)aa.stuff, n);
	/*ZZ				  ZZZ  transition stuff
		aa.type = aaPROXY;
	*ZZ*/	aa.type--;		/*ZZZ* transition stuff */
		aa.flags = (aproxylen / 504) + 1; /* 504/12 = 42 */
#else
		log("IGNORING aaPROXYQ from %s", inet_ntoa(A));
#endif
		break;
	case aaROUTEI:
	case aaROUTER:		/* extend the routing info a bit */
		if(aa.type == aaROUTEI) {
			offset = 0;
			if(arouteslen > ROUTEPKTSIZ)
				aa.flags=(arouteslen+ROUTEPKTSIZ-1)/ROUTEPKTSIZ;
			else
				aa.flags = 0;
			log("aaROUTEI(%d) to %s", aa.flags, inet_ntoa(A));
		} else {
			log("aaROUTER(%d) to %s", aa.flags, inet_ntoa(A));
			if((offset = aa.flags*ROUTEPKTSIZ) > arouteslen) {
				offset = arouteslen;
				log("Bozo aaROUTER request");
			}
			aa.type = aaROUTEM;
			/* and don't touch the flags */
		}
		n = arouteslen - offset;
		if(n > ROUTEPKTSIZ)
			n = ROUTEPKTSIZ;
		bcopy(aroutes+offset, (char *)aa.stuff, n);
		break;
	default:
		/* don't reply here! */
		return;
	}
	aa.count = htons((unsigned short)n);
	if (sendto(s, (caddr_t)&aa, aaconfMinSize+n, 0,
	    (struct sockaddr *)&fsin, sizeof fsin) < 0)
		log("Sendto to %s failed", inet_ntoa(fsin.sin_addr));
}
#endif

char *
netstring(n)
u_short n;
{
  int net;
  static char mynetstr[30];
#ifndef sun
  extern int sprintf();
#endif

  net = ntohs(n);
  (void)sprintf(mynetstr, "%d.%d", (net>>8) & 0xff, net & 0xff);
  return(mynetstr);
}

/*
 * return pointer to anet struct for passed network number
 * NULL if none
 */
struct anets *
anetof(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);
	return(NULL);
}

/*
 * return a value that is always printable
 */
char *
pzoneof(net)
	u_short net;
{
	struct anets *an;
	char *p = NULL;

	an = anetof(net);
	if (an != NULL)
		p = an->zone.zone;

	return(p == NULL ? "(unknown)" : p);
}

dumptab()
{
  register struct anets *an;
#ifndef OBSOLETE  
  register u_long *lp;
#endif
  register n;
#ifndef OBSOLETE
  register struct mgates *mg, *lmg;
#endif
  char *hostnameof();
  struct conf *conf;
  iaddr_t rs, re;
  struct in_addr A;
  struct zone_ent *nzp;

  size = 0;
#ifndef OBSOLETE
  mg = mgates;
  lmg = &mgates[nmgates];
#endif
  for (an = &anets[0], n = 0 ; n < nanets ; an++, n++) {
#ifndef OBSOLETE
    while (mg < lmg && mg->last < an)
	mg++;
#endif
    if (an->hnet == 0)
      Printf("\nRoute %s\t", netstring(an->net));
    else {
      Printf("\nRoute %s", netstring(an->net));
      Printf("-%s\t", netstring(an->hnet));
    }
    if ((an->flags & arouteKbox) && (an->flags & arouteEtalk) == 0) {
#ifndef OBSOLETE
      if (mg < lmg && an >= mg->first && an <= mg->last)
	if (an == mg->first)
	  Printf("Multigate");
	else
	  Printf("Multigate iface %d", an - mg->first - 1);
      else
#endif
	Printf("Kinetics box");
    }
    if (an->flags & arouteCore)
      Printf(" core gateway");
    if (an->flags & arouteHost) {
      Printf("Host is redirector");
      if (an->flags & arouteAsync)
	Printf(" to an async network");
    }
    if (an->flags & arouteNet)
	Printf(" net %d", an->flags & arouteBMask);
    if ((an->flags & (arouteKbox|arouteEtalk)) == (arouteKbox|arouteEtalk))
      Printf("EtherTalk");
#ifndef OBSOLETE
    if ((an->flags & typeFlags) == arouteKbox &&
	mg < lmg && an > mg->first && an <= mg->last) {
      A.s_addr = an->iaddr;
      Printf(" at %s", hostnameof(mg->first->iaddr));
      Printf(" interface %s", inet_ntoa(A));
    } else
#endif
      Printf(" at %s", hostnameof(an->iaddr));
    /*
     * Below the formatting gets complex. If there is an extra broadcast
     * address, then the Zone/Zone List is deferred to the next line.
     * Thus the \n and \t's depend on the broadcast being set.
     * Likewise, colcount counts how long the zone names are in order
     * to "word wrap" the list. This has to be initialized differently
     * when the zone list is pushed to the next line.
     */
    if (an->broadcast) {
      A.s_addr = an->broadcast;
      Printf(", Broadcast Mask: %s\n", inet_ntoa(A));
    }
    if (an->zone.next == 0)
      Printf("%sZone: \"%s\"", (an->broadcast) ? "\t\t" : ", ",
        an->zone.zone);
    else {
      int colcount = 0;

      nzp = &(an->zone);
      if (an->broadcast) {
        Printf("\t\tZone List: \"%s\"", nzp->zone);
	colcount = 11 + strlen(an->zone.zone);
      } else
        Printf(", Zone List: \"%s\"\n\t\t", nzp->zone);
	
      nzp = nzp->next;

      while (nzp) {
	if ((colcount += 4 + strlen(nzp->zone)) > 60) {
	  Printf("\n\t\t");;
	  colcount = strlen(nzp->zone);
	}
	Printf("\"%s\"", nzp->zone);
	if ((nzp = nzp->next) != 0) 
	  Printf(", ");
      }
    }
    (void)putchar('\n');

    if ((an->flags & arouteKbox) && (an->flags & arouteEtalk) == 0
      && an->confsize) {
      conf = (struct conf *)an->conf;
      A.s_addr = conf->ipbroad;
      Printf("\tIP Broadcast: %s, ", inet_ntoa(A));
      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");
      (void)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
      re = rs = ntohl(an->iaddr);
      if (conf->ipstatic) {
	rs++;
	A.s_addr = htonl(rs);
	Printf("\tIP static address range: %s", inet_ntoa(A));
	re += ntohs(conf->ipstatic);
	A.s_addr = htonl(re);
	Printf(" %s\n", inet_ntoa(A));
      } else
	Printf("\tIP static address range: empty\n");

      if (conf->ipdynamic) {
	rs = re + 1;
	re += ntohs(conf->ipdynamic);
	A.s_addr = htonl(rs);
	Printf("\tIP dynamic address range: %s", inet_ntoa(A));
	A.s_addr = htonl(re);
	Printf(" %s\n", inet_ntoa(A));
      } else
	Printf("\tIP dynamic address range: empty\n");
#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\tEtherTalk1: %s zone %s\n", netstring(conf->anetet),
	       pzoneof(conf->anetet));
      }
      if ((ntohl(conf->flags) & conf_etalk2) && conf->anetet2start) {
	Printf("\t\tEtherTalk2: %s-%s zone %s\n",
	       netstring(conf->anetet2start),
	       netstring(conf->anetet2end),
	       pzoneof(conf->anetet2start));	/* ?! */
      }
    }
  }
#ifndef OBSOLETE
  Printf("\nNBP proxy table:\n");
  for (lp = (u_long *)aproxy; lp < (u_long *)&aproxy[aproxylen]; ) {
    rs = (iaddr_t) *lp++;
    if (rs == 0)
      break;
    re = (iaddr_t) *lp++;
    n = *lp++;
    if (n == 0)
      Printf("\tAppletalk: ");
    else
      Printf("\tExternal:  ");
    A.s_addr = rs;
    Printf("%s .. ", inet_ntoa(A));
    A.s_addr = re;
    Printf("%s\n", inet_ntoa(A));
  }
#endif
}

/*
 * 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;
	int pass;
#define P_DOUBLE 1
#define P_SINGLE 2
#ifndef OBSOLETE
	register struct mgates *mg, *lmg;
#endif
	register n, size, i;

	size = 0;
#ifndef OBSOLETE
	mg = mgates;
	lmg = &mgates[nmgates];
#endif
	/*
	 * The info in the anets structs are copied into the arouteTuple
	 * structs in exactly the right format required to send to
	 * the requesting IPTalk gateways. This saves the work of
	 * regenerating the data each time.
	 *
	 * There are two passes. In the first pass the "double-tuples"
	 * are entered (the ones with network ranges or defined broadcast
	 * addresses). This is so that they cannot possibly be split
	 * across a packet boundary, meaning that no special-case code
	 * is required to accommodate this possibility.
	 */
	for (pass = P_DOUBLE; pass <= P_SINGLE; pass++) {
		for (an = &anets[0], n = 0 ; n < nanets ; an++, n++) {
#ifndef OBSOLETE
			while (mg < lmg && mg->last < an)
				mg++;
			if ((an->flags & typeFlags) == arouteKbox &&
			    mg < lmg && an == mg->first)
				continue;
#endif
			/*
			 * On P_DOUBLE pass, only add double nets and v.v
			 */
			if ((an->hnet != 0) || (an->broadcast != 0)) {
				if (pass == P_SINGLE)
					continue;
			} else {
				if (pass == P_DOUBLE)
					continue;
			}
			i = ((pass == P_DOUBLE) ? 2 : 1) * sizeof *at;
			size += i;
			if (size > maxsize) {
				size -= i;
				log("art build: routing table too big!!");
		log("Recompile atalkad with NAROUTES larger.");
				break;
			}
#ifndef OBSOLETE
			if ((an->flags & typeFlags) == arouteKbox &&
			    mg < lmg && an > mg->first && an <= mg->last) {
				at->node = mg->first->iaddr;
				an->flags &= ~arouteCore;
				if (an == mg->first + 1)
					an->flags |= 
					    mg->first->flags & arouteCore;
			} else
#endif
				at->node = an->iaddr;

			at->net = an->net;
			at->flags = an->flags;
#ifdef KIP0688
			at->hops = 0;	/* start at 1 - incremented in gw */
#else
			at->hops = 1;	/* not 0, so route appears to be 
					 * 2 hops in gw. This causes 
					 * gateway to prefer ethertalk
					 * route */
#endif
			if (pass == P_DOUBLE)
				at->hops |= 0x80;

			at++;
			if (pass == P_DOUBLE) {
				at->node = an->broadcast;
				at->net = an->hnet;
				at->flags = 0;
				at->hops = 0x80;
				at++;
			}
		}
	}
	log("art build: %d entries, %d maximum",
	    size/(sizeof(struct arouteTuple)),
	    maxsize/(sizeof(struct arouteTuple)));
	if (size > (maxsize - 40)) {
		log("art build: routing table size %d is near maximum of %d",
		    size, maxsize);
		log("Recompile atalkad with NAROUTES larger.");
	}
	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;
	register len;
	int zonenamelen = 0;
	int longerr = 1;

	/*
	 * make a pass thru anets, finding all unique zone names.
	 * Does NOT walk down the zone lists (yet, if ever)
	 */

	for (i = 0 ; i < nanets ; i++) {
		if (anets[i].zone.zone[0] == 0) {
			netzone[i] = MAXZ+1;
			continue;
		}
		for (iz = 0 ; iz < nzname ; iz++)
			if (strcmp(anets[i].zone.zone, zname[iz]) == 0)
				goto found;

		n = strlen(anets[i].zone.zone) + 1;
		if (nzname >= MAXZ || zonenamelen + n >= azlen) {
			if (longerr) {
				log("zone name table overflow");
				log(
"ignore this if running KIP 06/88, Megan 1.14, K-STAR 9.1 or later versions");
			}
			longerr = 0;
			netzone[i] = MAXZ+1;
			continue;
		}
		/* not found, make a new name */
		(void)strcpy(zname[iz], anets[i].zone.zone);
		zonenamelen += n;
		nzname++;

	found:;	/* found or inserted it, note the zname index */
		netzone[i] = iz;
	}
	/*
	 * Build structure to send to gateway.  Looks like:
	 * net# net# ... 0 zonename
	 * net# net# ... 0 zonename
	 * 0xFFFF.
	 */
	cp = az;
	len = 0;
	for (iz = 0 ; iz < nzname ; iz++) {
		n = strlen(zname[iz]);
		for (i = 0 ; i < nanets ; i++) {
			if (netzone[i] != iz)
				continue;
			n += 2;
		}
		/*
		 * if there's no space for this zone, then just
		 * skip it, and for simplicity, all after it,
		 * but keep counting the length so we can indicate
		 * just how big it is.
		 */
		if ((len += n + 3) >= azlen - 2)
			continue;
		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;
	}
	len += 2;
	*cp++ = 0xFF;  *cp++ = 0xFF;
	if (len > azlen) {
	  log("buildzones: ZIP table too large (%d bytes > max %d)",
	      len, azlen);
	  log(
"Don't worry if you are using KIP 06/88, Megan 1.14, K-STAR 9.1 or later");
	} else if (len > (azlen-30)) {
	  log("buildzones: ZIP table size %d approaching maximum of %d",
	      len, azlen);
   log("Don't worry if you are using KIP 06/88, Megan 1.14, or later versions");
	} else
	  log("zone names take %d bytes in gateway", len);
	return (cp - az);
}

#ifndef OBSOLETE
/*
 * Build NBP proxy table, to be returned by aaPROXYQ
 */
buildproxy(pp, nl)
	long *pp;
	register nl;
{
	register long *lp = pp;
	register struct anets *ap;
	register i = nanets;
	register long *cp, *np, *op;
	int proxcmp();
	extern char *malloc();

#define	Conf(p)	((struct conf *)(p))

	ap = anets;
	while (nl > 2) {	/* each entry requires 3 longs */
		if (--i < 0)
			break;
		*lp++ = ap->iaddr;
		if (ap->flags & arouteKbox) {
			if (ap->confsize >= sizeof(u_short) +
			  (char *)(&Conf(ap->conf)->ipdynamic)-(char *)ap->conf)
				*lp++ = htonl(ntohl(ap->iaddr) +
				    ntohs(Conf(ap->conf)->ipstatic) +
				    ntohs(Conf(ap->conf)->ipdynamic));
			else
				*lp++ = ap->iaddr;
			*lp++ = htonl((u_long)0);
		} else if (ap->flags & arouteHost) {
			*lp++ = ap->iaddr;
			*lp++ = htonl((u_long)1);
		} else if (ap->flags & arouteNet) {
			/*
			 * this fails completely for N0, but, we can't have
			 * everything (the info just isn't available)
			 */
			*lp++ = htonl(ntohl(ap->iaddr) +
			    (1 << (ap->flags & arouteBMask)*8) - 1);
			*lp++ = htonl((u_long)1);
		}
		ap++;
		nl -= 3;
	}
	if (nl > 0) {
		*lp++ = htonl((u_long)0);
		if (nl < 10) {
			log("Warning: approaching proxy table overflow");
			log(
			"Ignore this unless running Megan, and version < 1.24");
		}
	} else {
		log("Proxy table overflow");
		log("Ignore this unless running Megan, and version < 1.24");
	}

	i = (caddr_t)lp - (caddr_t)pp;
	if (i == 0)
		return (0);

	qsort((char *)pp, i / (3 * sizeof(long)), 3 * sizeof(long), proxcmp);

	op = lp;
	np = (long *)malloc((unsigned)i);

	lp = pp;
	cp = np;

	*cp++ = *lp++;
	*cp++ = *lp++;
	*cp++ = *lp++;

	while (lp < op) {
		if (lp[0] == lp[-3] && lp[1] == lp[-2]) {
			cp[-1] = lp[2];
			lp += 3;
			continue;
		}
		if (ntohl((u_long)lp[-2]) + 1 == ntohl((u_long)lp[0]) &&
		    lp[-1] == lp[2]) {
			cp[-2] = lp[1];
			lp += 3;
			continue;
		}
		if (ntohl((u_long)lp[-2]) + 3 == ntohl((u_long)lp[0]) &&
		    lp[-1] == lp[2]) {
			i = ntohl((u_long)lp[-2]) ^ ntohl((u_long)lp[0]);
			if (i ^ (i + 1) == 0) {
				cp[-2] = lp[1];
				lp += 3;
				continue;
			}
		}
		*cp++ = *lp++;
		*cp++ = *lp++;
		*cp++ = *lp++;
	}

	i = (caddr_t)cp - (caddr_t)np;
	bcopy((char *)np, (char *)pp, i);
	free((char *)np);

	log("Proxy table uses %d bytes", i);
	return (i);
}

proxcmp(a, b)
	char *a, *b;
{
	register unsigned long *al, *bl;

#define	N2HU(x)		((unsigned long)(ntohl(x)))

	al = (unsigned long *)a;
	bl = (unsigned long *)b;

	if (N2HU(al[0]) < N2HU(bl[0]))
		return (-1);
	if (N2HU(al[0]) > N2HU(bl[0]))
		return (1);
	if (N2HU(al[1]) < N2HU(bl[1]))
		return (-1);
	if (N2HU(al[1]) > N2HU(bl[1]))
		return (1);
	if (al[2] == bl[2])		/* no need for N2HU ... obviously */
		return (0);
	if (N2HU(al[2]) == 1)
		return (-1);

#undef	N2HU

	return (1);
}
#endif

#ifndef applec
/*
 * Send aaRESTART or aaROUTEI to all gateways.
 */
sendall(sig)
{
	register struct anets *an;
#ifndef OBSOLETE
	register struct mgates *mg, *lmg;
#endif
	register n;
	iaddr_t last_ip_sent;
	int trys, count, rcount;
	fd_set fds;
	struct sockaddr_in tsin;

	last_ip_sent = 0;
	tsin = sin;
	log("sendall(%s)", sig == aaRESTART ? "aaRESTART" : "aaROUTEI");
	aas.magic = htonl(aaMagic); /* setup send packet */
	aas.type = sig;
	if (sig == aaROUTEI) {
		if(arouteslen > ROUTEPKTSIZ) {
			count = ROUTEPKTSIZ;
			aas.flags = (arouteslen+ROUTEPKTSIZ-1)/ROUTEPKTSIZ;
		} else {
			count = arouteslen;
			aas.flags = 0;
		}
		bcopy(aroutes, (char *)aas.stuff, count);
	} else {
		aas.flags = 0;
		count = 0;
	}
	aas.count = htons((u_short)count);
	count += aaconfMinSize;
	/*
	 * send to each kbox in the table.
	 */
#ifndef OBSOLETE
	mg = mgates;
	lmg = &mgates[nmgates];
#endif
	for (an = &anets[0], n = 0 ; n < nanets ; an++, n++) {
#ifndef OBSOLETE
		while (mg < lmg && mg->last < an)
			mg++;
		if ((an->flags & typeFlags) == arouteKbox &&
		    mg < lmg && an > mg->first && an <= mg->last)
			continue;
#endif
		/*
		 * The Webster MPG has multiple AppleTalk interfaces, all
		 * with the same IP address - only hit the box once.
		 */
		if (an->iaddr == last_ip_sent)
			continue;
		if ((an->flags & arouteKbox) == 0 || (an->flags & arouteEtalk))
			continue;

		last_ip_sent = an->iaddr;
		trys = 0;
		tsin.sin_addr.s_addr = an->iaddr;
		if (sendto(s, (caddr_t)&aas, count, 0,
		    (struct sockaddr *)&tsin, sizeof tsin) < 0) {
		    	log("sendto %s failed (sendall)",
			    inet_ntoa(tsin.sin_addr));
			continue;
		}
		/*
		 * 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, (fd_set *)0, (fd_set *)0,
			    &tvwait) != 1) {
				/* timeout */
				if (++trys < 4) {
					(void)sendto(s, (caddr_t)&aas, count, 0,
					    (struct sockaddr *)&tsin,
					    sizeof tsin);
					continue;
				}
				log("no response from %s",
				    inet_ntoa(tsin.sin_addr));
				break;
			}
			fsinlen = sizeof fsin;
			rcount = recvfrom(s, (caddr_t)&aa, sizeof aa,
			    0, (struct sockaddr *)&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");
}
#endif

/*
 * check over configurations
 *
*/
checkconfigs()
{
  int  i;
  struct anets *an;
  struct conf *cp;
  int rangestart = 0;
  int rangeconflict = 0;

  for (an = anets, i = 0; i < nanets; i++, an++) {
    cp = (struct conf *)an->conf;
    if (an->confsize >= (sizeof(struct conf) - (sizeof(cp->anetet2start) 
      + sizeof(cp->anetet2end)))) {
      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((u_short)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((u_short)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;
	register i;
	char st[128], *cp, *mp;
	register struct anets *an;
	int cur_zone;
#ifndef OBSOLETE
	struct mgates *mg;
#endif
	iaddr_t iaddr;
	short sh;
	long l;

	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)
          (void)fclose(fp);
	if ((fp = fopen(atalkatab, "r")) == NULL) {
		log("can't open %s", atalkatab);
		exit(1);
	}
	(void)fstat(fileno(fp), &sbuf);
	log("(re)reading %s", atalkatab);
	modtime = sbuf.st_mtime;
	size = sbuf.st_size;
	nanets = 0;
	an = &anets[-1];
	cur_zone = 0;
#ifndef OBSOLETE
	mg = NULL;
	nmgates = 0;
#endif
	linenum = 0;
	cp = 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)	/* skip comment lines */
			continue;
		if (line[0] == ' ' || line[0] == '\t') 	/* process conf info */
			goto confinfo;
		/*
		 * lines not beginning with white space 
		 * represent a new net #
		 */
		if (++nanets > NANETS) {
			log("'anets' table length exceeded");
			log("Recompile atalkad with NANETS larger.");
			exit(1);
		}
		an++;
		cp = an->conf;			/* store following lines here */
		an->confsize = 0;
		getfield(st, sizeof st, 0);
		/*
		 * Now have to parse a NET RANGE or a NET. This code
		 * will accept "n-n", "n- n", "n -n" and "n - n".
		 * First, assume there isn't a net range.
		 */
		an->hnet = 0;
		if ((mp = (char *)index(st, (int)'-')) == 0) {
			/* No '-' in first one read */
			an->net = htons((u_short)getashort(st));
			getfield(st, sizeof st, 0);
			if ((mp = (char *)index(st, (int)'-')) != 0) {
				/* Second field contains '-' */
				if (mp[1] == 0) {
					/* '-' on its own - next is hnet */
					getfield(st, sizeof st, 0);
					an->hnet = 
					    htons((u_short)getashort(st));
				} else {
					/* '-' attached to hnet */
					an->hnet = 
					    htons((u_short)getashort(st+1));
				}
				getfield(st, sizeof st, 0);
			} else {
				/* No '-' in second field  => phase-1 */
				;
			}
		} else {
			/* '-' in/on first field */
			if (mp[1] == 0) {
				/* trailing '-' - remove it */
				*mp = 0;
				an->net = htons((u_short)getashort(st));
				getfield(st, sizeof st, 0);
				an->hnet = htons((u_short)getashort(st));
			} else {
				/* nn-nn, have to parse BOTH */
				*mp = 0;
				an->net = htons((u_short)getashort(st));
				an->hnet = htons((u_short)getashort(mp+1));
			}
			getfield(st, sizeof st, 0);
		}
		i = 0;
		an->broadcast = 0; /* default is no broadcast */
		/* 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;
#ifndef OBSOLETE
			case 'm':
				if (mg != NULL)
					mg->last = an - 1;
				if (an->net == 0) {
					nanets--;
					an--;
					mg = NULL;
					cp = 0;
					goto nextline;
				}
				if (nmgates >= NMGATES-1) {
					log("'mgates' table length exceeded");
					exit(1);
				}
				mg = &mgates[nmgates++];
				mg->first = an;
				i |= arouteKbox;
				break;
#endif
			case 'n':
				i |= arouteNet;  break;
 		        case 'e':
				i |= (arouteEtalk|arouteKbox); break;
			case 'a':
				i |= (arouteHost|arouteAsync); break;
			case '0': case '1': case '2': case '3':
				i |= (*cpp - '0');  break;
			case 'b':
				an->broadcast = 1; break;
			default:
				log("bad flag (%c) in %s, linenum %d",
				    *cpp, st, linenum);
				break;
			}
		}
		an->flags = i;
		getfield(st, sizeof st, 0);
		an->iaddr = getiaddr(st);
		/* if there is an extra broadcast mask, get it */
		if (an->broadcast != 0) {
			getfield(st, sizeof st, 0);
			an->broadcast = getiaddr(st);
		}
		getfield(an->zone.zone, sizeof(an->zone.zone), 0);
		an->zone.next = 0;
		
		/*
		 * Get zone list and link in to anet structure
		 */
		while (getfield(zonelist[cur_zone].zone,
		    sizeof(zonelist[cur_zone].zone), 0), (cur_zone < NXZONES)
		    && (zonelist[cur_zone].zone[0] != 0)) {
			if (an->zone.next)
				zonelist[cur_zone - 1].next 
				    = &zonelist[cur_zone];
			else
				an->zone.next = &zonelist[cur_zone];
				
			if (++cur_zone >= NXZONES) {
				log("'zonelist' table length exceeded");
				log("Recompile atalkad with NXZONES larger.");
				exit(1);
			}
		}

	nextline:;
		continue;

		/*
		 * lines beginning with white space
		 * are configuration data for gateway.
		 */
	confinfo:;
		if (cp == 0) {
			log("misplaced configuration information, linenum %d",
			    linenum);
			continue;
		}
		for (;;) {	/* for each field in line */
			int len;

			getfield(st, sizeof st, 1);
			sp = st;
			if (*sp == 0)
				break;
			if (an->confsize >= sizeof an->conf) {
				log("Excess config info, linenum %d", linenum);
				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 */
#ifndef OBSOLETE
			    if (mg != NULL)
			      iaddr = mg->first->iaddr;
			    else
			      iaddr = an->iaddr;
#endif
			    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;
			      iaddr = an->iaddr;
			    } else {
			      log("%%N at unexpected offset %d, line %d\n",
				  cp - an->conf, linenum);
			      flag = arouteKbox;
			    }

			    if ((sh = getanet(flag, iaddr)) == 0)
				log("%%N not set, line %d\n", linenum);
			    bcopy((caddr_t)&sh, cp, sizeof(short));
			    cp += sizeof(short);
			    an->confsize += sizeof(short);
			    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':
				l = htonl((u_long)atoii(sp));
				bcopy((caddr_t)&l, cp, 4);
				cp += 4;
				an->confsize += 4;
				break;

			case 's':
				sh = htons((u_short)getashort(sp));
				bcopy((caddr_t)&sh, cp, 2);
				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 */
				an->confsize += len;
				break;
			
			default:
				log("bad field type %s, linenum %d",
					st, linenum);
				break;
			}
			continue;	/* get next field in line */
		}
		/* get next line */
	}
	/* end of file */
	checkconfigs();
	arouteslen = buildart((struct arouteTuple *)aroutes, sizeof aroutes);
	azoneslen = buildzones(azones, sizeof azones);
#ifndef OBSOLETE
	aproxylen = buildproxy((long *)aproxy, sizeof aproxy/sizeof(long));
#endif
}


/*
 * Get next field from 'line' buffer into 'str'.  'linep' is the 
 * pointer to current position.
 *
 * Fields are white space separated, except within quoted strings.
 * If 'quote' is true the quotes of such a string are retained, otherwise
 * they are stripped.  Quotes are included in strings by doubling them.
 *
 * Also handles "\\n" as a continuation character after blanks.
 */
getfield(str, len, quote)
	char *str;
{
	register char *lp = linep;
	register char *cp = str;
	int i;

	while (*lp == ' ' || *lp == '\t')
		lp++;	/* skip spaces/tabs */
	if (*lp == 0 || *lp == '#') {
		*cp = 0;
		return;
	}
	if ((*lp == '\\') && (lp[1] == 0)) {
		/* Continuation line - have to read a new one */
		if (fgets(line, sizeof line, fp) == NULL) {
			*cp = 0;
			return;
		}
		if ((i = strlen(line)))
			line[i-1] = 0;  /* remove trailing newline */
		linep = line;
		linenum++;
		/* call getfield (recursive I know) to parse */
		getfield(str, len, quote);
		return;
	}

	len--;	/* save a spot for a null */

	if (*lp == '"' || *lp == '\'') {		/* quoted string */
		register term = *lp;

		if (quote) {
			*cp++ = term;
			len -= 2;	/* one for now, one for later */
		}
		lp++;
		while (*lp) {
			if (*lp == term) {
				if (lp[1] == term)
					lp++;
				else
					break;
			}
			*cp++ = *lp++;
			if (--len <= 0) {
				log("string truncated: %s, linenum %d",
				    str, linenum);
				if (quote)
					*cp++ = term;
				*cp = 0;
				linep = lp;
				return;
			}
		}
		if (!*lp)
			log("unterminated string: %s, linenum %d",
			    str, linenum);
		else {
			lp++;	/* skip the terminator */

			if (*lp && *lp != ' ' && *lp != '\t' && *lp != '#') {
				log("garbage after string: %s, linenum %d",
				    str, linenum);
				while (*lp && *lp != ' ' &&
				    *lp != '\t' && *lp != '#')
					lp++;
			}
		}
		if (quote)
			*cp++ = term;
	} else {
		while (*lp && *lp != ' ' && *lp != '\t' && *lp != '#') {
			*cp++ = *lp++;
			if (--len <= 0) {
				log("string truncated: %s, linenum %d",
				    str, linenum);
				break;
			}
		}
	}
	*cp = 0;
	linep = lp;
}


/*
 * Ascii to integer, with base check.
 */
atoii(s)
	register char *s;
{
	int v;
	char *c;

	if (isupper(*s))
		*s = tolower(*s);
	c = "%d";
	if (*s == '0') {
		c = "%o";
		if (*++s == 0)
			return (0);
	}
	if (*s == 'x' || *s == 'h') {
		c = "%x";
		s++;
	}
	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 = 0;
	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);
		else
			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 = (char *)index(st, (int)'.')) == 0)
		return (atoii(st));
	*cp++ = 0;
	return ((atoii(st)<<8) | atoii(cp));
}


#ifdef VARARGS
#include <varargs.h>

/*
 * log an error message		(new version, for intelligent hosts)
 */
/* VARARGS */
log(va_alist)
	va_dcl
{
	register char *fmt;
	va_list args;
	FILE *fp;
	long time(), tloc;
	struct tm *ptm, *localtime();
	char line[1024];

	if (debug)
	  fp = stderr;
	else if (scanonly)
	  fp = stdout;
#ifndef SYSLOG
	else if ((fp = fopen(atalkalog, "a+")) == NULL)
	  return;
#endif /* SYSLOG defined */

	va_start(args);
	fmt = va_arg(args, char *);
	(void)vsprintf(line, fmt, args);
	va_end(args);

#ifdef SYSLOG
	syslog( LOG_INFO, "%s", line );
	if( !debug )
	    return;
#endif /* SYSLOG defined */

	(void)time(&tloc);
	ptm = localtime(&tloc);
	Fprintf(fp, "%d/%d %02.2d:%02.2d %s\n",
		ptm->tm_mon + 1, ptm->tm_mday,
		ptm->tm_hour, ptm->tm_min, line);

	if (fp != stderr && fp != stdout)
		(void)fclose(fp);
}

#else

/*
 * log an error message  (ancient version for hosts without VARARGS)
 */
/* VARARGS 1 */
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;

	(void)time(&tloc);
	tm = localtime(&tloc);
	Fprintf(fp, "%d/%d %02.2d:%02.2d ", tm->tm_mon + 1, tm->tm_mday,
		tm->tm_hour, tm->tm_min);
	_doprnt(fmt, &args, fp);
	(void)putc('\n', fp);
	if (fp != stderr && fp != stdout)
		(void)fclose(fp);
}
#endif

/*
 * getanet - scan through the table so far and find an appletalk network 
 * number. Called from the %n 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 | 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.
 */
/* ARGSUSED */				/* or might be one day */
match_net(flags, a1, a2)
     unsigned flags;
     iaddr_t a1, a2;
{
  return((a1 & node_mask) == (a2 & node_mask));
}

char *
hostnameof(addr)
	u_long addr;
{
	struct hostent *host;
	struct in_addr ipaddr;

	ipaddr.s_addr = addr;
	host = gethostbyaddr((char *)&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;
#if 0
	int icount;
#endif
	int len;
	char *p;
	char *zn;
	struct anets *an;
	int zl;
	struct ZIP zipin;
	struct ZIP *z;
	struct in_addr A;

	A.s_addr = aa.ipaddr;
#if 0
	icount = ntohs(aa.count);	/* remember incoming count */
#endif
	bcopy((char *)aa.stuff, (char *)&zipin, sizeof(struct ZIP));
	if (zipin.command != zipQuery || zipin.count == 0)
		return (-1);
#if 0 /* count is a byte, max value 255 * 2 + header 2 bytes.. must fit */
	if (zipin.count >
	    (sizeof(aa.stuff)-sizeof(struct ZIP))/sizeof(u_short))
		 return (-1);
#endif
	/* get the list of networks */
	bcopy((char *)(aa.stuff+sizeof(struct ZIP)), (char *)buf,
		(int)(zipin.count * sizeof(u_short)));
	z = (struct ZIP *)aa.stuff;
	z->command = zipReply;
	z->count = 0;
	p = (char *)(z+1);
	len = sizeof(struct ZIP);
	for (bp = buf; zipin.count; zipin.count--, bp++) {
		if ((an = (anetof(*bp))) == NULL)
			continue;

		zn = an->zone.zone;
		if (an->zone.next != 0) {
			zipsendXreply(an);
			continue;
		}
		zl = strlen(zn);	/* get zone length */
		if (len + sizeof(u_short) + 1 + zl > 512) {
			aa.count = htons((unsigned short)len);
			if (sendto(s, (caddr_t)&aa, aaconfMinSize + len, 0,
			    (struct sockaddr *)&fsin, sizeof fsin) < 0)
				log("zipinput reply to %s failed",
				    inet_ntoa(fsin.sin_addr));
			
			log("aaZONEq reply to %s (len %d)", inet_ntoa(A),
			   len);
			z->count = 0;
			len = sizeof(struct ZIP);
			p = (char *)(z+1);
		}
		bcopy((char *)bp, p, sizeof(u_short)); /* copy in network */
		p += sizeof(u_short);	/* move along */
		*(p++) = zl;			/* length of zone name */
		bcopy(zn, p, zl);		/* copy in zone name */
		p += zl;			/* bounce p ...*/
		len += sizeof(u_short) + 1 + zl;	/* move along */
		z->count++;
	}
	return((z->count) ? len : 0);
}

/*
 * zipsendXreply. Called to send all zone lists in response to zone
 * queries. Only called from zipinput(), and relies on some of the
 * data structures (outgoing packet) set up by zipinput().
 */
zipsendXreply(an)
	struct anets *an;
{
	struct aaconf aaz;
	struct ZIP *z;
	register struct zone_ent *zp;
	register char *cp;
	u_short zone_count;
	register len;
	struct in_addr A;

	A.s_addr = aa.ipaddr;

	/*
	 * count how many zones are in the list
	 */
	zone_count = 1;		/* first zone must be there */
	zp = &(an->zone);
	while (zp->next != NULL) {
		zone_count++;
		zp = zp->next;
	}
	/*
	 * Copy zipinput() outgoing packet into our one.
	 */
	bcopy((char *)&aa, (char *)&aaz, aaconfMinSize + sizeof (struct ZIP));
	z = (struct ZIP *)aaz.stuff;
	
	cp = (char *)z;
	zp = &(an->zone);
	while (zp != 0) {
		z->command = zipXReply;
		z->count = zone_count;
		len = sizeof(struct ZIP);

		while (zp != 0) {
			int zonelen = strlen(zp->zone);

			if (len + zonelen > 512 - 3)
				break;
			cp[len++] = an->net >> 8;
			cp[len++] = an->net;
			cp[len++] = zonelen;
			bcopy((caddr_t)zp->zone, (caddr_t)&cp[len],
			    zonelen);
			len += zonelen;
			zp = zp->next;
		}
		aaz.count = htons((unsigned short)len);
		if (sendto(s, (caddr_t)&aaz, aaconfMinSize + len, 0,
		    (struct sockaddr *)&fsin, sizeof fsin) < 0)
			log("zipsendXreply to %s failed",
			    inet_ntoa(fsin.sin_addr));

		log("aaZONEq Xreply to %s (len %d)", inet_ntoa(A), len);
	}
}

#ifdef inet_ntoa
/*
 * this nonsense is for SunOS 4.1.1 ??, which is a truly broken system if
 * ever there was one.
 */
char *
inet_ntoa(A)
	struct in_addr A;
{
#undef inet_ntoa
	extern char *inet_ntoa();

	return inet_ntoa(&A);
}
#endif

#if applec
/* Dummy routines so it can run on a Mac */

fstat(a, b)
	int a;
	char *b;
{
	return 1;
}

stat(a, b)
	char *a;
	char *b;
{
	return 1;
}

char net_return[20];

char *
inet_ntoa(a)
{
	sprintf(net_return, "%d.%d.%d.%d", (a >> 24) & 0xff,
	    (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff);
	return (net_return);
}

struct hostent *
gethostbyaddr(a, b, c)
{
	return (0);
}
#define ip_addr unsigned long
ip_addr dummy_ip = 0x11223344;
ip_addr *addrPtrs[2] = { &dummy_ip, 0 };

struct hostent  unixHost = 
{
	0,
	0,
	0,
	sizeof(ip_addr),
	addrPtrs
};

struct hostent *
gethostbyname(a)
	char *a;
{
	return (&unixHost);
}

unsigned long
inet_addr(a)
	char *a;
{
	int i, j, k, l;
	sscanf(a, "%d.%d.%d.%d", &i, &j, &k, &l);
	return (i << 24) + (j << 16) + (k << 8) + l;
}

#endif




