/*
 * Copyright (c) 1986 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of California at Berkeley. The name of the University
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1986 Regents of the University of California.\n\
 All rights reserved.\n";
#endif /* not lint */

#ifndef lint
static char sccsid[] = "@(#)ns_main.c 1.1 94/10/31 SMI"; /* from UCB 4.30 3/7/88 */
#endif /* not lint */

/*
* Internet Name server (see rfc883 & others).
*/

#include <sys/param.h>
#if defined(SYSV)
#include <fcntl.h>
#endif SYSV
#include <sys/file.h>
#include <sys/time.h>
#if !defined(SYSV)
#include <sys/wait.h>
#endif !SYSV
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <stdio.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>
#include <netdb.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <resolv.h>
#undef nsaddr				/* XXX */
#include "ns.h"
#include "db.h"

#ifdef BOOTFILE 			/* default boot file */
char	*bootfile = BOOTFILE;
#else
char	*bootfile = "/etc/named.boot";
#endif

#ifdef DEBUGFILE 			/* default debug output file */
char	*debugfile = DEBUGFILE;
#else
char	*debugfile = "/usr/tmp/named.run";
#endif

#ifdef PIDFILE 				/* file to store current named PID */
char	*PidFile = PIDFILE;
#else
char	*PidFile = "/etc/named.pid";	
#endif

#ifndef FD_SET
#define	NFDBITS		32
#define	FD_SETSIZE	32
#define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#define FD_ZERO(p)	bzero((char *)(p), sizeof(*(p)))
#endif

FILE	*fp;  				/* file descriptor for pid file */

#ifdef DEBUG
FILE	*ddt;
#endif

int	debug = 0;			/* debugging flag */
int	ds;				/* datagram socket */
int	read_interrupted = 0;		/* flag for read timer */
int	needreload = 0;			/* received SIGHUP, need to reload db */
int	needmaint = 0;			/* need to call ns_maint()*/
int     needToDoadump = 0;              /* need to dump database */
int     needToChkpt = 0;	        /* need to checkpoint cache */
int	needStatsDump = 0;		/* need to dump statistics */
#ifdef ALLOW_UPDATES
int     needToExit = 0;                 /* need to exit (may need to doadump
					 * first, if database has changed since
					 * it was last dumped/booted). Gets
					 * set by shutdown signal handler
					 *  (onintr)
					 */
#endif ALLOW_UPDATES

/* 
 * indicates a need to load a zone after xfer is done.  When not NULL, points
 * to the zone to load.  numZonesToLoad may have to be declared volatile.
 */
int numZonesToLoad = 0;
struct zoneinfo *needLoadZones[MAX_XFERS_RUNNING];

/*
 * A flag indicating if an ns_xfer_update() should be called, values:
 *    0 == no update needed
 *    1 == update due to timer ringing
 *    2 == update due to SIGCHLD (child finished) being received
 */
int   needXferUpdate = 0;

int	priming = 0;			/* is cache being primed */

#ifdef SO_RCVBUF
int	rbufsize = 8 * 1024;		/* UDP recive buffer size */
#endif

struct	qstream *streamq = QSTREAM_NULL; /* list of open streams */
struct	qdatagram *datagramq = QDATAGRAM_NULL; /* list of datagram interfaces */
struct	qdatagram *dqp, *sqp;
struct	sockaddr_in nsaddr;
struct	timeval tt;
struct	netinfo *nettab = NULL;
struct	netinfo *fnettab = NULL;
struct	netinfo *onettab = NULL;
struct	netinfo netloop;
short	ns_port;
struct	sockaddr_in from_addr;		/* Source addr of last packet */
int	from_len;			/* Source addr size of last packet */
time_t	boottime, resettime;		/* Used by ns_stats */
fd_set	mask;				/* select mask of open descriptors */

char		**Argv = NULL;		/* pointer to argument vector */
char		*LastArg = NULL;	/* end of argv */

extern int errno;

#if defined(SYSV)
getdtablesize()
{
	return(FD_SETSIZE);
}
#endif SYSV

main(argc, argv, envp)
	int argc;
	char *argv[], *envp[];
{
	register int n, udpcnt;
	register char *arg;
	register struct qstream *sp;
	int vs;
	int nfds;
	int on = 1;
	int rfd, size;
	u_long lasttime, maxctime;
	u_long nnn, nm;
	char buf[BUFSIZ];

	fd_set tmpmask;

	struct timeval t, *tp;
	struct qstream *candidate = QSTREAM_NULL;
	struct qstream *nextsp;
	struct ifconf ifc;
	struct ifreq ifreq, *ifr;
	struct netinfo *ntp;
	struct netinfo *ntip;
	struct netinfo *ontp;
	struct netinfo *ontip;
	extern int onintr(), maint_alarm(), reapchild(), setdumpflg(), onhup();
	extern int setIncrDbgFlg(), setNoDbgFlg(), sigprof();
	extern int setchkptflg(), setstatsflg();
	extern struct qstream *sqadd();
	extern struct qinfo *qhead; 
	extern char Version[];

	ns_port = htons(NAMESERVER_PORT);
	_res.options &= ~(RES_DEFNAMES | RES_DNSRCH | RES_RECURSE);

	/*
	**  Save start and extent of argv for setproctitle.
	*/

	Argv = argv;
	if (envp == 0 || *envp == 0)
		envp = argv;
	while (*envp)
		envp++;
	LastArg = envp[-1] + strlen(envp[-1]);

	(void) umask(022);
	while (--argc > 0) {
		arg = *++argv;
		if (*arg == '-') {
			while (*++arg)
				switch (*arg) {
				case 'b':
					if (--argc <= 0)
						usage();
					bootfile = *++argv;
					break;

  				case 'd':
 					++argv;

 					if (*argv != 0) {
 					    if (**argv == '-') {
 						argv--;
 						break;
 					    }
 					    debug = atoi(*argv);
 					    --argc;
 					}
					if (debug <= 0)
						debug = 1;
					setdebug(1);
					break;

				case 'p':
					if (--argc <= 0)
						usage();
					ns_port = htons((u_short)atoi(*++argv));
					break;

				default:
					usage();
				}
		} else
			bootfile = *argv;
	}

	if (!debug) {
		if (fork())
			exit(0);
		for (n = getdtablesize() - 1; n >= 0; n--)
			(void) close(n);
		(void) open("/dev/null", O_RDONLY);
		(void) dup2(0, 1);
		(void) dup2(0, 2);
		n = open("/dev/tty", O_RDWR);
		if (n > 0) {
#ifndef SYSV
			(void) ioctl(n, TIOCNOTTY, (char *)NULL);
#else
			setpgrp();
#endif
			(void) close(n);
		}
	}
#ifdef DEBUG
	else {
		fprintf(ddt,"Debug turned ON, Level %d\n",debug);
		fprintf(ddt,"Version = %s\t",Version);
		fprintf(ddt,"bootfile = %s\n",bootfile);
	}		
#endif

#ifdef LOG_DAEMON
	openlog("named", LOG_PID|LOG_CONS|LOG_NDELAY, LOG_DAEMON);
#else
	openlog("named", LOG_PID);
#endif

	/* tuck my process id away */
	fp = fopen(PidFile, "w");
	if (fp != NULL) {
		fprintf(fp, "%d\n", getpid());
		(void) fclose(fp);
	}
	syslog(LOG_NOTICE, "restarted\n");

	nsaddr.sin_family = AF_INET;
	nsaddr.sin_addr.s_addr = INADDR_ANY;
	nsaddr.sin_port = ns_port;
	/*
	** Initialize and load database.
	*/
	gettime(&tt);
	buildservicelist();
	buildprotolist();
	ns_init(bootfile);
	time(&boottime);
	resettime = boottime;

	(void) signal(SIGALRM, maint_alarm);
	(void) signal(SIGHUP, onhup);
#if defined(SYSV)
	(void) signal(SIGCLD, reapchild);
#else
	(void) signal(SIGCHLD, reapchild);
#endif SYSV
	(void) signal(SIGPIPE, SIG_IGN);
	(void) signal(SIGSYS, sigprof);
	(void) signal(SIGINT, setdumpflg);
	(void) signal(SIGQUIT, setchkptflg);
	(void) signal(SIGIOT, setstatsflg);

#ifdef ALLOW_UPDATES
        /* Catch SIGTERM so we can dump the database upon shutdown if it
           has changed since it was last dumped/booted */
        (void) signal(SIGTERM, onintr);
#endif ALLOW_UPDATES

#if defined(SIGUSR1) && defined(SIGUSR2)
	(void) signal(SIGUSR1, setIncrDbgFlg);
	(void) signal(SIGUSR2, setNoDbgFlg);
#else	SIGUSR1&&SIGUSR2
	(void) signal(SIGEMT, setIncrDbgFlg);
	(void) signal(SIGFPE, setNoDbgFlg);
#endif SIGUSR1&&SIGUSR2

#ifdef DEBUG
	if (debug) {
		fprintf(ddt,"database initialized\n");
	}
#endif
	/*
	** Open stream port.
	*/
	if ((vs = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		syslog(LOG_ERR, "socket(SOCK_STREAM): %m");
		exit(1);
	}	
	(void)setsockopt(vs, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
	if (bind(vs, (struct sockaddr *)&nsaddr, sizeof(nsaddr))) {
		syslog(LOG_ERR, "bind(vs, %s[%d]): %m",
			inet_ntoa(nsaddr.sin_addr), ntohs(nsaddr.sin_port));
		exit(1);
	}
	ifc.ifc_len = sizeof(buf);
	ifc.ifc_buf = buf;
	if (ioctl(vs, SIOCGIFCONF, (char *)&ifc) < 0) {
		syslog(LOG_ERR, "get interface configuration: %m");
		exit(1);
	}
	n = ifc.ifc_len/sizeof(struct ifreq);
	ntp = NULL;
	for (ifr = ifc.ifc_req; n > 0; n--, ifr++) {
		int dup_addr;

		if (ifr->ifr_addr.sa_family != AF_INET)
			continue;
		ifreq = *ifr;
		if (ioctl(vs, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
			syslog(LOG_ERR, "get interface flags: %m");
			continue;
		}
		if ((ifreq.ifr_flags & (IFF_UP|IFF_RUNNING)) == 0)
			continue;
		if (ioctl(vs, SIOCGIFADDR, (char *)&ifreq) < 0) {
			syslog(LOG_ERR, "get interface addr: %m");
			continue;
		}
		/* build datagram queue */
		if ((dqp = (struct qdatagram *)calloc(1,
		    sizeof(struct qdatagram))) == NULL) {
#ifdef DEBUG
			if (debug >= 5)
				fprintf(ddt,"main: malloc error\n");
#endif
			syslog(LOG_ERR, "main: Out Of Memory");
			exit(12);
		}
		dqp->dq_next = datagramq;
		dqp->dq_addr = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
		/* 
		 * look for an already existing source interface address.  If
		 * the machine has multiple point to point interfaces, then 
		 * the local address may appear more than once.
		 */		   
		dup_addr = 0;
		for (sqp=datagramq; sqp != NULL; sqp = sqp->dq_next)
		  if (dqp->dq_addr.s_addr == sqp->dq_addr.s_addr) {
		    dup_addr = 1;
		    break;
		  }
		if (!dup_addr)
		      datagramq = dqp;
		else {
#ifdef DEBUG
		  if (debug)
		    fprintf(ddt, "main: dup interface address %s on %s\n",
			    inet_ntoa(dqp->dq_addr), ifreq.ifr_name);
#endif		      
		  free((char *) dqp);
		}

		if (ntp == NULL)
			ntp = (struct netinfo *)malloc(sizeof(struct netinfo));
		ntp->my_addr = 
		    ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
#ifdef SIOCGIFNETMASK
		if (ioctl(vs, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
		    syslog(LOG_ERR, "get netmask: %m (using default)");
		    ntp->mask = net_mask(ntp->my_addr);
		}
		ntp->mask = ((struct sockaddr_in *)
		    &ifreq.ifr_addr)->sin_addr.s_addr;
#else
		/* 4.2 does not support subnets */
		ntp->mask = net_mask(ntp->my_addr);
#endif
		if (ioctl(vs, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
			syslog(LOG_ERR, "get interface flags: %m");
			continue;
		}
#ifdef IFF_LOOPBACK
		if ((ifreq.ifr_flags & IFF_LOOPBACK))
#else
		/* test against 127.0.0.1 (yuck!!) */
		if (ntp->my_addr.s_addr == htonl(0x7F000001))
#endif
		{
		    netloop.my_addr = ntp->my_addr;
		    netloop.mask = 0xffffffff;
		    netloop.net = ntp->my_addr.s_addr;
		    netloop.next = NULL;
#ifdef DEBUG
		    if (debug) 
			fprintf(ddt,"loopback address: x%lx\n",
				netloop.my_addr.s_addr);
#endif DEBUG
		    continue;
		} else if ((ifreq.ifr_flags & IFF_POINTOPOINT)) {
			if (ioctl(vs, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
		    	    syslog(LOG_ERR, "get dst addr: %m");
		            continue;
			}
			ntp->mask = 0xffffffff;
			ntp->net = ((struct sockaddr_in *)
		    	    &ifreq.ifr_addr)->sin_addr.s_addr;
		} else {
			ntp->net = ntp->mask & ntp->my_addr.s_addr;
		}
		ntp->next = NULL;
		if (nettab == NULL)
			nettab = ntip = ntp;
		else {
			ntip->next = ntp;
			ntip = ntp;
		}
		ntp = NULL;
	}
	if (ntp)
		(void) free((char *)ntp);

	/*
	 * Create separate qdatagram structure for socket
	 * wildcard address.
	 */
	if ((dqp = (struct qdatagram *)calloc(1, sizeof(struct qdatagram)))
	     == NULL) {
#ifdef DEBUG
		if (debug >= 5)
			fprintf(ddt,"main: malloc error\n");
#endif
		syslog(LOG_ERR, "main: Out Of Memory");
		exit(12);
	}
	dqp->dq_next = datagramq;
	datagramq = dqp;
	dqp->dq_addr.s_addr = INADDR_ANY;

	/*
	 * Compute other networks for sorting based on network configuration
	 */
	ontp = NULL;
	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
		nm = net_mask(ntp->my_addr);
		if (nm != ntp->mask) {
			nnn = ntp->my_addr.s_addr & nm;
			for (ontp=onettab; ontp != NULL; ontp=ontp->next) {
				if ((ontp->net == nnn) &&
				    (ontp->mask == nm))
					goto outerloop;
				ontip = ontp;
			}
			ontp = (struct netinfo *) 
				malloc(sizeof(struct netinfo));
			ontp->net = nnn;
			ontp->mask = nm;
			ontp->my_addr = ntp->my_addr;
			ontp->next = NULL;
			if (onettab == NULL )
				onettab = ontp;
			else
				ontip->next = ontp;
		}
outerloop:
		;
	}
	/*
	 * Merge sort list from network configuration
	 * with list we computed from network configuration
	 */
	for (ntp = nettab; ntp != NULL; ntp = ntp->next){
		if (ntp->next == NULL) {
			ntp->next = onettab;
			break;
		}
	}
	/* 
	 * Merge sort list from boot file with list from 
	 * network configuration
	 */
	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
	    for (ontp = fnettab; ontp != NULL; ontp = ontp->next) {
		if ((ontp->mask == ntp->mask) && (ontp->net == ntp->net)) {
		    if( fnettab == ontp )  {
		        ontip = ontp;
			fnettab = ontp->next;
			free((char *)ontip);
			break;
		    } else {
		   	for (ontip = fnettab; ontip != NULL;ontip = ontip->next)
			   if (ontip->next == ontp) break;
			ontip->next = ontp->next;
			free((char *)ontp);
			ontp = ontip;
			break;
		    }
		}
	    }
	    if (ntp->next == NULL)
		break;
	}
	if (ntp) {
		ntp->next = fnettab;
	} else {
		nettab = fnettab;
	}
#ifdef DEBUG
	if (debug)
		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
			fprintf(ddt,"net x%lx mask x%lx", ntp->net, ntp->mask);
			fprintf(ddt," my_addr x%lx", ntp->my_addr.s_addr);
			fprintf(ddt," %s\n", inet_ntoa(ntp->my_addr));
		}
#endif DEBUG
	(void) listen(vs, 5);

	/*
	 * Open datagram ports for each interface
	 * (the first is un-addressed datagram port).
	 */
	for (dqp = datagramq; dqp != QDATAGRAM_NULL; dqp = dqp->dq_next) {
		nsaddr.sin_addr = dqp->dq_addr;
		if ((dqp->dq_dfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			syslog(LOG_ERR, "socket(SOCK_DGRAM): %m");
			exit(1);
		}	
#ifdef DEBUG
		if (debug)
			fprintf(ddt,"dqp->dq_addr %s d_dfd %d\n",
			    inet_ntoa(dqp->dq_addr), dqp->dq_dfd);
#endif DEBUG
		(void)setsockopt(dqp->dq_dfd, SOL_SOCKET, SO_REUSEADDR,
		    (char *)&on, sizeof(on));
#ifdef SO_RCVBUF
		(void)setsockopt(dqp->dq_dfd, SOL_SOCKET, SO_RCVBUF,
		    (char *)&rbufsize, sizeof(rbufsize));
#endif SO_RCVBUF
		(void) fcntl(dqp->dq_dfd, F_SETFL, FNDELAY);
/*
 *   NOTE: Some versions of SunOS have problems with the following
 *   call to bind.  Bind still seems to function on these systems
 *   if you comment out the exit inside the if.  This may cause
 *   Suns with multiple interfaces to reply strangely.
 */
		if (bind(dqp->dq_dfd, (struct sockaddr *)&nsaddr, sizeof(nsaddr))) {
			syslog(LOG_ERR, "bind(dfd %d, %s[%d]): %m",
				dqp->dq_dfd, inet_ntoa(nsaddr.sin_addr),
				ntohs(nsaddr.sin_port));
#if !defined(sun)
			exit(1);
#endif
		}
	}
	ds = datagramq->dq_dfd;

	t.tv_usec = 0;

#ifdef DEBUG
	if (debug)
		fprintf(ddt,"Ready to answer queries.\n");
#endif
	prime_cache();
	nfds = getdtablesize();       /* get the number of file descriptors */
	if (nfds > FD_SETSIZE) {
		nfds = FD_SETSIZE;	/* Bulletproofing */
		syslog(LOG_ERR, "Return from getdtablesize() > FD_SETSIZE");
#ifdef DEBUG
		if (debug)
		      fprintf(ddt,"Return from getdtablesize() > FD_SETSIZE\n");
#endif
	}
	FD_ZERO(&mask);
	FD_SET(vs, &mask);
	FD_SET(ds, &mask);
	for (dqp = datagramq; dqp != QDATAGRAM_NULL; dqp = dqp->dq_next)
		FD_SET(dqp->dq_dfd, &mask);
	for (;;) {
#ifdef ALLOW_UPDATES
                if (needToExit) {
			struct zoneinfo *zp;
			sigblock(~0);   /*
					 * Block all blockable signals
					 * to ensure a consistant
					 * state during final dump
					 */
#ifdef DEBUG
			if (debug)
				fprintf(ddt, "Received shutdown signal\n");                     
#endif DEBUG
			for (zp = zones; zp < &zones[nzones]; zp++) {
				if (zp->hasChanged)
					zonedump(zp);
                        }
                        exit(0);
                }        
#endif ALLOW_UPDATES
		if(needreload) {
			needreload = 0;
			db_reload();
		}
#ifdef STATS
		if(needStatsDump) {
			needStatsDump = 0;
			ns_stats();
		}
#endif STATS
		if(numZonesToLoad > 0) {
			int res;
#if !defined(SYSV)
			int omask;
again:
			omask = sigblock(sigmask(SIGCHLD));
#endif	/* SYSV */
			for (n = 0; n < numZonesToLoad; n++) {
				/* mark old RR's */
		        	mark_zone(hashtab, needLoadZones[n] - zones, 1);
if (needLoadZones[n]->z_source==NULL) {
	(void) syslog(LOG_ERR, "NULL z_source attempted!");
	continue;
  }
if (needLoadZones[n]->z_origin == NULL) {
	(void) syslog(LOG_ERR, "NULL z_origin attempted!");
	continue;
  }
		    		res = db_load(needLoadZones[n]->z_source, 
					      needLoadZones[n]->z_origin,
					      needLoadZones[n]);
				if (res == 0) {
					needLoadZones[n]->z_auth = 1;
					/* delete old RR's */
					clean_zone(hashtab,
					    needLoadZones[n] - zones,1);
					(void) syslog(LOG_INFO,
					    "reloaded zone %s",
					    needLoadZones[n]->z_origin);
				} else {
					/* delete new RR's */
					clean_zone(hashtab,
					    needLoadZones[n] - zones,0);
					/* mark old RR's current again */
					mark_zone(hashtab,
					    needLoadZones[n] - zones,0);
					(void)syslog(LOG_ERR,
					    "reload of xfer'd zone %s failed",
					    needLoadZones[n]->z_origin);
				}
			}
			numZonesToLoad = 0;
#if !defined(SYSV)
			(void)sigsetmask(omask & ~sigmask(SIGCHLD));
			/*
			 * There may be some magic in here when we release
			 * the child interrupt, since reapchild() may be
			 * immediately called.  Go back for more if so.
			 */
			if (numZonesToLoad > 0)
				goto again;
#endif	/* SYSV */
                }
		if(needXferUpdate) {
		        (void)ns_xfer_update(needXferUpdate);
		        needXferUpdate = 0;
		}
		/*
		 * Make sure a timer is on if any xfers are running
		 */
		if (xfer_running_cnt > 0)
		    timer_on(MAX_XFER_TIME, TIME_XFER);

		if(needmaint) {
                        needmaint = 0;
                        ns_maint();
                }
	        if(needToChkpt) {
                        needToChkpt = 0;
                        doachkpt();
	        }
                if(needToDoadump) {
                        needToDoadump = 0;
                        doadump();
                }
		/*
		** Wait until a query arrives
		*/
		if (retryqp != NULL) {
			gettime(&tt);
			t.tv_sec = (long) retryqp->q_time - tt.tv_sec;
			if (t.tv_sec <= 0) {
				retry(retryqp);
				continue;
			}
			tp = &t;
		} else
			tp = NULL;
		tmpmask = mask;
		n = select(nfds, &tmpmask, (fd_set *)NULL, (fd_set *)NULL, tp);
		if (n < 0) {
			if (errno == EINTR)
				continue;
			syslog(LOG_ERR, "select: %m");
#ifdef DEBUG
			if (debug)
				fprintf(ddt,"select error\n");
#endif
			;
		}
		if (n == 0)
			continue;

		for (dqp = datagramq; dqp != QDATAGRAM_NULL;
		    dqp = dqp->dq_next) {
		    if (FD_ISSET(dqp->dq_dfd, &tmpmask))
		        for(udpcnt = 0; udpcnt < 25; udpcnt++) {
			    from_len = sizeof(from_addr);
			    if ((n = recvfrom(dqp->dq_dfd, buf, sizeof(buf), 0,
				(struct sockaddr *)&from_addr, &from_len)) < 0)
			    {
				if ((n == -1) && (errno == EWOULDBLOCK))
					break;
				syslog(LOG_WARNING, "recvfrom: %m");
				break;
			    }
#ifdef STATS
			    stats[S_INPKTS].cnt++;
#endif
#ifdef DEBUG
			    if (debug)
				fprintf(ddt,
				  "\ndatagram from %s port %d, fd %d, len %d\n",
				    inet_ntoa(from_addr.sin_addr),
				    ntohs(from_addr.sin_port), ds, n);
			    if (debug >= 10)
				fp_query(buf, ddt);
#endif
			    /*
			     * Consult database to get the answer.
			     */
			    gettime(&tt);
			    ns_req(buf, n, PACKETSZ, QSTREAM_NULL, &from_addr,
				    dqp->dq_dfd);
		        }
		}
		/*
		** Process stream connection
		*/
		if (FD_ISSET(vs, &tmpmask)) {
			from_len = sizeof(from_addr);
			rfd = accept(vs, (struct sockaddr *)&from_addr, &from_len);
			gettime(&tt);
			if (rfd < 0 && errno == EMFILE && streamq != NULL) {
				maxctime = 0;
				candidate = QSTREAM_NULL;
				for (sp = streamq; sp != QSTREAM_NULL;
				   sp = nextsp) {
					nextsp = sp->s_next;
					if (sp->s_refcnt != 0)
					    continue;
					lasttime = tt.tv_sec - sp->s_time;
					if (lasttime >= 900)
					    sqrm(sp, &mask);
					else if (lasttime > maxctime) {
					    candidate = sp;
					    maxctime = lasttime;
					}
				}
				rfd = accept(vs, (struct sockaddr *)&from_addr, &from_len);
				if ((rfd < 0) && (errno == EMFILE) &&
				    candidate != QSTREAM_NULL) {
					sqrm(candidate, &mask);
					rfd = accept(vs, (struct sockaddr *)&from_addr, &from_len);
				}
			}
			if (rfd < 0) {
				syslog(LOG_WARNING, "accept: %m");
				continue;
			}
			(void) fcntl(rfd, F_SETFL, FNDELAY);
			(void) setsockopt(rfd, SOL_SOCKET, SO_KEEPALIVE,
				(char *)&on, sizeof(on));
			if ((sp = sqadd()) == QSTREAM_NULL) {
				(void) close(rfd);
				continue;
			}
			sp->s_rfd = rfd;	/* stream file descriptor */
			sp->s_size = -1;	/* amount of data to receive */
			gettime(&tt);
			sp->s_time = tt.tv_sec;	/* last transaction time */
			sp->s_from = from_addr;	/* address to respond to */
			sp->s_bufsize = 0;
			sp->s_bufp = (char *)&sp->s_tempsize;
			sp->s_refcnt = 0;
			FD_SET(rfd, &mask);
			FD_SET(rfd, &tmpmask);
#ifdef DEBUG
			if (debug) {
				fprintf(ddt,
				   "\nTCP connection from %s port %d (fd %d)\n",
					inet_ntoa(sp->s_from.sin_addr),
					ntohs(sp->s_from.sin_port), rfd);
			}
#endif
		}
#ifdef DEBUG
		if (debug > 2 && streamq)
			fprintf(ddt,"streamq  = x%x\n",streamq);
#endif
			    for (sp = streamq; sp != QSTREAM_NULL; sp = nextsp) {
				nextsp = sp->s_next;
			    if (FD_ISSET(sp->s_rfd, &tmpmask)) {
#ifdef DEBUG
				if (debug > 5) {
				    fprintf(ddt,
					"sp x%x rfd %d size %d time %d ",
					sp, sp->s_rfd, sp->s_size,
					sp->s_time );
				    fprintf(ddt," next x%x \n", sp->s_next );
				    fprintf(ddt,"\tbufsize %d",sp->s_bufsize);
				    fprintf(ddt," buf x%x ",sp->s_buf);
				    fprintf(ddt," bufp x%x\n",sp->s_bufp);
				}
#endif DEBUG
			    if (sp->s_size < 0) {
			        size = sizeof(u_short) -
				   (sp->s_bufp - (char *)&sp->s_tempsize);
			        while (size > 0 &&
			           (n = read(sp->s_rfd, sp->s_bufp, size)) > 0){
					    sp->s_bufp += n;
					    size -= n;
			        }
			        if ((n == -1) && (errno == EWOULDBLOCK))
					    continue;
			        if (n <= 0) {
					    sp->s_refcnt = 0;
					    sqrm(sp, &mask);
					    continue;
			        }
			        if ((sp->s_bufp - (char *)&sp->s_tempsize) ==
					sizeof(u_short)) {
					sp->s_size = htons(sp->s_tempsize);
					if (sp->s_bufsize == 0) {
					    if ( (sp->s_buf = malloc(BUFSIZ))
						== NULL) {
						    sp->s_buf = buf;
						    sp->s_size  = sizeof(buf);
					    } else {
						    sp->s_bufsize = BUFSIZ;
					    }
					}
					if (sp->s_size > sp->s_bufsize &&
					  sp->s_bufsize != 0) {
					    if ((sp->s_buf = realloc(
						(char *)sp->s_buf,
						(unsigned)sp->s_size)) == NULL){
						    sp->s_buf = buf;
						    sp->s_bufsize = 0;
						    sp->s_size  = sizeof(buf);
					   } else {
						    sp->s_bufsize = sp->s_size;
					   }
					}
					sp->s_bufp = sp->s_buf;	
				    }
			    }
			    gettime(&tt);
			    sp->s_time = tt.tv_sec;
			    while (sp->s_size > 0 &&
			      (n = read(sp->s_rfd, sp->s_bufp, sp->s_size)) > 0)
			    {
				    sp->s_bufp += n;
				    sp->s_size -= n;
			    }
			    /*
			     * we don't have enough memory for the query.
			     * if we have a query id, then we will send an
			     * error back to the user.
			     */
			    if (sp->s_bufsize == 0 &&
				(sp->s_bufp - sp->s_buf > sizeof(u_short))) {
				    HEADER *hp;

				    hp = (HEADER *)sp->s_buf;
				    hp->qr = 1;
				    hp->ra = 1;
				    hp->ancount = 0;
				    hp->qdcount = 0;
				    hp->nscount = 0;
				    hp->arcount = 0;
				    hp->rcode = SERVFAIL;
				    (void) writemsg(sp->s_rfd, sp->s_buf,
					sizeof(HEADER));
				    continue;
			    }
			    if ((n == -1) && (errno == EWOULDBLOCK))
				    continue;
			    if (n <= 0) {
				    sp->s_refcnt = 0;
				    sqrm(sp, &mask);
				    continue;
			    }
			    /*
			     * Consult database to get the answer.
			     */
			    if (sp->s_size == 0) {
				    sp->s_refcnt++;
				    ns_req(sp->s_buf,
					sp->s_bufp - sp->s_buf,
					sp->s_bufsize, sp,
					&sp->s_from, -1);
				    sp->s_bufp = (char *)&sp->s_tempsize;
				    sp->s_size = -1;
				    continue;
			    }
		    }
		}
	}
	/* NOTREACHED */
}

usage()
{
	fprintf(stderr, "Usage: named [-d #] [-p port] [{-b} bootfile]\n");
	exit(1);
}

/*
** Set flag saying to reload database upon receiving SIGHUP.
** Must make sure that someone isn't walking through a data
** structure at the time.
*/

onhup()
{
#if defined(SYSV)
	(void)signal(SIGHUP, onhup);
#endif SYSV
	needreload = 1;
}

/*
** Set flag saying to call ns_maint()
** Must make sure that someone isn't walking through a data
** structure at the time.
*/

maint_alarm()
{
#if defined(SYSV)
	(void)signal(SIGALRM, maint_alarm);
#endif SYSV
    int i = handle_timer();
    if (i & TIME_XFER)
	needXferUpdate = 1;	/* indicate should update due to timer */
    if (i & TIME_NS_MAINT)
    	needmaint = 1; 
#ifdef DEBUG
    if (i == 0)
        if (debug) fprintf(ddt, "BUG! timer but nothing being timed\n");
#endif /* DEBUG */
}

#ifdef ALLOW_UPDATES
/*
 * Signal handler to schedule shutdown.  Just set flag, to ensure a consistent
 * state during dump.
 */
onintr()
{
        needToExit = 1;
}
#endif ALLOW_UPDATES

/*
 * Signal handler to schedule a data base dump.  Do this instead of dumping the
 * data base immediately, to avoid seeing it in a possibly inconsistent state
 * (due to updates), and to avoid long disk I/O delays at signal-handler
 * level
 */
int setdumpflg()
{
#if defined(SYSV)
	(void)signal(SIGINT, setdumpflg);
#endif SYSV
        needToDoadump = 1;
}

/*
** Set flag saying to read was interrupted
** used for a read timer
*/

read_alarm()
{
	extern int read_interrupted;
	read_interrupted = 1;
}

reapchild()
{
#if defined(SYSV)
	(void)wait(0);
	(void)signal(SIGCLD, reapchild);
#else
	union wait status;
	register struct zoneinfo *zp;
	int pid;

	while ((pid = wait3(&status, WNOHANG, (struct rusage *)NULL)) > 0) {
#ifdef DEBUG
	    if (debug > 1) 
	      fprintf(ddt, "child %d returned status=%d termsig=%d\n", 
		      pid, status.w_retcode,status.w_termsig);
#endif /* DEBUG */
	    for (zp = zones; zp < &zones[nzones]; zp++) {
		if (zp->xfer_pid == pid) {
		    /*
		     * The child that died was an xfer.
		     * Note that this child will never be a child that I
		     * killed with a kill(#,9).
		     * following exit codes are returned:
		     *	0 - everything went ok
		     *  1 - could not reach the servers for the xfer
		     *  2 - some other error
		     * 
		     */
		    xfer_running_cnt--;
		    zp->xfer_pid = 0;
		    if (needXferUpdate == 0)
		    	needXferUpdate = 2;	/* says: child died */
		    if (status.w_termsig != 0) {
#ifdef DEBUG
			if (debug && status.w_termsig != 9)
			    fprintf(ddt, "\tBad child termination!\n");
#endif /* DEBUG */
		    } else if (status.w_retcode == 0) {
		      /*
		       * This assumes we will never have more than
		       * MAX_XFERS_RUNNING zones to load.  Should give this
		       * some thought.
		       */
		      needLoadZones[numZonesToLoad] = zp;
		      numZonesToLoad++;
		    } else if (status.w_retcode == 1) {
#ifdef DEBUG
			if (debug) fprintf(ddt,
			"zoneref: Masters for secondary zone %s unreachable\n",
					   zp->z_origin);
#endif /* DEBUG */
			syslog(LOG_WARNING,
			  "zoneref: Masters for secondary zone %s unreachable",
			       zp->z_origin);
		    } else if (status.w_retcode == 2) {
			struct timeval tt;
#ifdef DEBUG
			if (debug) (void)fprintf(ddt,
			    "zoneref: secondary zone %s reported up to date\n",
					   zp->z_origin);
#endif /* DEBUG */
			/*
			 * Must update time and refresh value.  Do sanity
			 * check to make sure we should really be here.
			 */
			if (zp->z_auth && zp->z_soa_refresh != 0) {
				gettime(&tt);
				zp->z_lastupdate = tt.tv_sec;
				zp->z_refresh = zp->z_soa_refresh;
			}
#ifdef DEBUG
			else if (debug) (void)fprintf(ddt,
		"zoneref: secondary zone %s incorrectly reported up to date\n",
					   zp->z_origin);
#endif /* DEBUG */
		    } else {
#ifdef DEBUG
			if (debug > 1) fprintf(ddt, "xfer returned %d",
					       status.w_retcode);
#endif /* DEBUG */
			syslog(LOG_ERR,"xfer returned %d", status.w_retcode);
		    }
		    break;
		}
	    }
	}

#endif SYSV
}

/*
** Turn on or off debuging by open or closeing the debug file
*/

setdebug(code)
int code;
{
#if defined(lint) && !defined(DEBUG)
	code = code;
#endif
#ifdef DEBUG

	if (code) {
		ddt = freopen(debugfile, "w+", stderr);
		if ( ddt == NULL) {
			syslog(LOG_WARNING, "can't open debug file: %m");
			debug = 0;
		}
		else {
#if defined(SYSV)
			setvbuf(ddt, NULL, _IOLBF, BUFSIZ);
#else
			setlinebuf(ddt);
#endif
			(void) fcntl(fileno(ddt), F_SETFL, FAPPEND);
		}
	}
	else {
		if (ddt) {
			fprintf(ddt,"Debug turned OFF, Level %d\n",debug);
			(void) fclose(ddt);
		}
		debug = 0;
	}
#endif
}

/*
** Catch a special signal and set debug level.
**
**  If debuging is off then turn on debuging else increment the level.
**
** Handy for looking in on long running name servers.
*/

setIncrDbgFlg()
{
#if defined(SYSV)
	(void)signal(SIGUSR1, setIncrDbgFlg);
#endif SYSV
#ifdef DEBUG
	if (debug == 0) {
		debug++;
		setdebug(1);
	}
	else {
		debug++;
	}
	fprintf(ddt,"Debug turned ON, Level %d\n",debug);
#endif
}

/*
** Catch a special signal to turn off debugging
*/

setNoDbgFlg()
{
#if defined(SYSV)
	(void)signal(SIGUSR2, setNoDbgFlg);
#endif SYSV
	setdebug(0);
}

/*
** Set flag for statistics dump
*/
setstatsflg()
{
#if defined(SYSV)
	(void)signal(SIGIOT, setstatsflg);
#endif SYSV
	needStatsDump = 1;
}

int setchkptflg()
{
#if defined(SYSV)
	(void)signal(SIGQUIT, setchkptflg);
#endif SYSV
	needToChkpt = 1;
}

/*
** Catch a special signal SIGSYS
**
**  this is setup to fork and exit to drop to /usr/tmp/gmon.out
**   and keep the server running
*/

sigprof()
{
#if defined(SYSV)
	(void)signal(SIGSYS, sigprof);
#endif SYSV
#ifdef DEBUG
	if (debug)
		fprintf(ddt,"sigprof()\n");
#endif
	if ( fork() == 0)
	{
		(void) chdir("/usr/tmp");
		exit(1);
	}
}

/*
** Routines for managing stream queue
*/

struct qstream *
sqadd()
{
	register struct qstream *sqp;

	if ((sqp = (struct qstream *)calloc(1, sizeof(struct qstream)))
	    == NULL ) {
#ifdef DEBUG
		if (debug >= 5)
			fprintf(ddt,"sqadd: malloc error\n");
#endif
		syslog(LOG_ERR, "sqadd: Out Of Memory");
		return(QSTREAM_NULL);
	}
#ifdef DEBUG
	if (debug > 3)
		fprintf(ddt,"sqadd(x%x)\n", sqp);
#endif

	sqp->s_next = streamq;
	streamq = sqp;
	return(sqp);
}

sqrm(qp, mask)
	register struct qstream *qp;
	fd_set *mask;
{
	register struct qstream *qsp;
	extern void qremove_stream();

#ifdef DEBUG
	if (debug > 1) {
		fprintf(ddt,"sqrm(%#x, %d ) rfcnt=%d\n",
		    qp, qp->s_rfd, qp->s_refcnt);
	}
#endif
	if (qp->s_refcnt != 0)
		return;

	qremove_stream(qp);
	if (qp->s_bufsize != 0)
		(void) free(qp->s_buf);
	FD_CLR(qp->s_rfd, mask);
	(void) close(qp->s_rfd);
	if (qp == streamq) {
		streamq = qp->s_next;
	} else {
		for (qsp = streamq; qsp->s_next != qp; qsp = qsp->s_next)
			;
		qsp->s_next = qp->s_next;
	}
	(void)free((char *)qp);
}

sqflush()
{
	register struct qstream *sp, *spnext;

	for (sp = streamq; sp != QSTREAM_NULL; sp = spnext) {
		sp->s_refcnt = 0;
		spnext = sp->s_next;
		sqrm(sp, &mask);
	}
}

setproctitle(a, s)
	char *a;
	int s;
{
	int size;
	register char *cp;
	struct sockaddr_in sin;
	char buf[80];

	cp = Argv[0];
	size = sizeof(sin);
	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
	else {
		syslog(LOG_DEBUG, "getpeername: %m");
		(void) sprintf(buf, "-%s", a);
	}
	(void) strncpy(cp, buf, LastArg - cp);
	cp += strlen(cp);
	while (cp < LastArg)
		*cp++ = ' ';
}

net_mask(in)
struct in_addr in;
{
	register u_long i = ntohl(in.s_addr);

	if (IN_CLASSA(i))
		return (htonl(IN_CLASSA_NET));
	else if (IN_CLASSB(i))
		return (htonl(IN_CLASSB_NET));
	else
		return (htonl(IN_CLASSC_NET));
}

gettime(ttp)
struct timeval *ttp;
{
	if (gettimeofday(ttp, (struct timezone *)0) < 0)
		syslog(LOG_ERR, "gettimeofday failed: %m");
	return;
}
