#if !defined(lint) && !defined(SABER) static char sccsid[] = "@(#)ns_main.c 4.55 (Berkeley) 7/1/91"; static char rcsid[] = "$Id: ns_main.c,v 1.1.1.1 1994/09/22 19:46:12 pst Exp $"; #endif /* not lint */ /* * ++Copyright++ 1986, 1989, 1990 * - * Copyright (c) 1986, 1989, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * - * --Copyright-- */ #if !defined(lint) && !defined(SABER) char copyright[] = "@(#) Copyright (c) 1986, 1989, 1990 The Regents of the University of California.\n\ portions Copyright (c) 1993 Digital Equipment Corporation\n\ portions Copyright (c) 1993 Berkeley Network Software Consortium\n\ All rights reserved.\n"; #endif /* not lint */ /* * Internet Name server (see rfc883 & others). */ #include #include #include #if !defined(SYSV) && defined(XXX) #include #endif /* !SYSV */ #include #define TIME_H_INCLUDED #include #if defined(__osf__) #define _SOCKADDR_LEN /* XXX - should be in portability.h but that * would need to be included before socket.h */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(SVR4) # include #endif #define MAIN_PROGRAM #include "named.h" #undef MAIN_PROGRAM #undef nsaddr /* UDP receive, TCP send buffer size */ static const int rbufsize = 8 * 1024; static struct sockaddr_in nsaddr; static u_int16_t local_ns_port; /* our service port */ static fd_set mask; /* open descriptors */ static char **Argv = NULL; static char *LastArg = NULL; /* end of argv */ static struct qstream *sqadd __P((void)); static void sq_query __P((struct qstream *)), opensocket __P((struct qdatagram *)), #ifdef DEBUG printnetinfo __P((struct netinfo *)), #endif setdebug __P((int)); static int sq_here __P((struct qstream *)); static SIG_FN onintr __P(()), maint_alarm __P(()), setdumpflg __P(()), onhup __P(()), #if defined(QRYLOG) && defined(SIGWINCH) setQrylogFlg __P(()), #endif setIncrDbgFlg __P(()), setNoDbgFlg __P(()), #ifdef SIGSYS sigprof __P(()), #endif /* SIGSYS */ setchkptflg __P(()), setstatsflg __P(()); static void usage() { fprintf(stderr, "Usage: named [-d #] [-q] [-r] [-p port[/localport]] [[-b] bootfile]\n"); exit(1); } /*ARGSUSED*/ void main(argc, argv, envp) int argc; char *argv[], *envp[]; { register int n, udpcnt; register char *arg; register struct qstream *sp; register struct qdatagram *dqp; struct qstream *nextsp; int nfds; const int on = 1; int len; int rfd, size; time_t lasttime, maxctime; u_char buf[BUFSIZ]; #ifndef SYSV struct sigvec vec; #endif fd_set tmpmask; struct timeval t, *tp; struct qstream *candidate = QSTREAM_NULL; char **argp; #ifdef PID_FIX char oldpid[10]; #endif #ifdef WANT_PIDFILE FILE *fp; /* file descriptor for pid file */ #endif #ifdef IP_OPTIONS u_char ip_opts[50]; /* arbitrary size */ #endif local_ns_port = ns_port = htons(NAMESERVER_PORT); /* ** Save start and extent of argv for ns_setproctitle(). */ Argv = argp = argv; while (*argp) argp++; LastArg = argp[-1] + strlen(argp[-1]); (void) umask(022); /* XXX - should use getopt here */ while (--argc > 0) { arg = *++argv; if (*arg == '-') { while (*++arg) switch (*arg) { case 'b': if (--argc <= 0) usage(); bootfile = savestr(*++argv); break; case 'd': ++argv; if (*argv != 0) { if (**argv == '-') { argv--; break; } #ifdef DEBUG debug = atoi(*argv); #endif --argc; } #ifdef DEBUG if (debug <= 0) debug = 1; setdebug(1); #endif break; case 'p': /* use nonstandard port number. * usage: -p remote/local * remote is the port number to which * we send queries. local is the port * on which we listen for queries. * local defaults to same as remote. */ if (--argc <= 0) usage(); ns_port = htons((u_int16_t) atoi(*++argv)); { char *p = strchr(*argv, '/'); if (p) { local_ns_port = htons((u_int16_t) atoi(p+1)); } else { local_ns_port = ns_port; } } break; #ifdef QRYLOG case 'q': qrylog = 1; break; #endif case 'r': NoRecurse = 1; break; default: usage(); } } else bootfile = savestr(*argv); } #ifdef DEBUG if (!debug) #endif for (n = getdtablesize() - 1; n > 2; n--) (void) close(n); /* don't use my_close() here */ #ifdef DEBUG else { fprintf(ddt, "Debug turned ON, Level %d\n",debug); fprintf(ddt, "Version = %s\n", Version); fprintf(ddt, "bootfile = %s\n", bootfile); } #endif n = 0; #if defined(DEBUG) && defined(LOG_PERROR) if (debug) n = LOG_PERROR; #endif #ifdef LOG_DAEMON openlog("named", LOG_PID|LOG_CONS|LOG_NDELAY|n, LOGFAC); #else openlog("named", LOG_PID); #endif #ifdef WANT_PIDFILE /* tuck my process id away */ #ifdef PID_FIX fp = fopen(PidFile, "r+"); if (fp != NULL) { (void) fgets(oldpid, sizeof(oldpid), fp); (void) rewind(fp); fprintf(fp, "%d\n", getpid()); (void) my_fclose(fp); } #else /*PID_FIX*/ fp = fopen(PidFile, "w"); if (fp != NULL) { fprintf(fp, "%d\n", getpid()); (void) my_fclose(fp); } #endif /*PID_FIX*/ #endif /*WANT_PIDFILE*/ syslog(LOG_NOTICE, "starting. %s", Version); _res.options &= ~(RES_DEFNAMES | RES_DNSRCH | RES_RECURSE); nsaddr.sin_family = AF_INET; nsaddr.sin_addr.s_addr = INADDR_ANY; nsaddr.sin_port = local_ns_port; /* ** Open stream port. */ for (n = 0; ; n++) { if ((vs = socket(AF_INET, SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, "socket(SOCK_STREAM): %m"); exit(1); } if (setsockopt(vs, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) { syslog(LOG_ERR, "setsockopt(vs, reuseaddr): %m"); (void) my_close(vs); continue; } if (bind(vs, (struct sockaddr *)&nsaddr, sizeof(nsaddr)) == 0) break; if (errno != EADDRINUSE || n > 4) { if (errno == EADDRINUSE) { syslog(LOG_ERR, "There may be a name server already running"); syslog(LOG_ERR, "exiting"); } else { syslog(LOG_ERR, "bind(vs, [%s].%d): %m", inet_ntoa(nsaddr.sin_addr), ntohs(nsaddr.sin_port)); } #if defined(WANT_PIDFILE) && defined(PID_FIX) /* put old pid back */ if (atoi(oldpid) && (fp = fopen(PidFile, "w"))) { fprintf(fp, "%s", oldpid); (void) my_fclose(fp); _exit(1); } #endif /*WANT_PIDFILE && PID_FIX*/ exit(1); } else { /* Retry opening the socket a few times */ my_close(vs); sleep(1); } } if (listen(vs, 5) != 0) { syslog(LOG_ERR, "listen(vs, 5): %m"); exit(1); } /* * named would be terminated if one of these is sent and no handler */ (void) signal(SIGINT, setdumpflg); (void) signal(SIGQUIT, setchkptflg); (void) signal(SIGIOT, setstatsflg); #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 */ #if defined(SIGWINCH) && defined(QRYLOG) (void) signal(SIGWINCH, setQrylogFlg); #endif /* * Get list of local addresses and set up datagram sockets. */ getnetconf(); /* ** Initialize and load database. */ gettime(&tt); buildservicelist(); buildprotolist(); ns_init(bootfile); #ifdef DEBUG if (debug) { fprintf(ddt, "Network and sort list:\n"); printnetinfo(nettab); } #endif time(&boottime); resettime = boottime; (void) signal(SIGHUP, onhup); #if defined(SIGXFSZ) (void) signal(SIGXFSZ, onhup); /* wierd DEC Hesiodism, harmless */ #endif #if defined(SYSV) (void) signal(SIGCLD, endxfer); (void) signal(SIGALRM, maint_alarm); #else bzero((char *)&vec, sizeof(vec)); vec.sv_handler = maint_alarm; vec.sv_mask = sigmask(SIGCHLD); (void) sigvec(SIGALRM, &vec, (struct sigvec *)NULL); vec.sv_handler = endxfer; vec.sv_mask = sigmask(SIGALRM); (void) sigvec(SIGCHLD, &vec, (struct sigvec *)NULL); #endif /* SYSV */ (void) signal(SIGPIPE, SIG_IGN); #ifdef SIGSYS (void) signal(SIGSYS, sigprof); #endif /* SIGSYS */ #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 dprintf(1, (ddt, "database initialized\n")); t.tv_usec = 0; /* * Fork and go into background now that * we've done any slow initialization * and are ready to answer queries. */ #ifdef USE_SETSID if ( #ifdef DEBUG !debug || #endif !isatty(0)) { if (fork() > 0) exit(0); setsid(); #ifdef DEBUG if (!debug) #endif { n = open(_PATH_DEVNULL, O_RDONLY); (void) dup2(n, 0); (void) dup2(n, 1); (void) dup2(n, 2); if (n > 2) (void) my_close(n); } } #else #ifdef DEBUG if (!debug) #endif { #ifdef HAVE_DAEMON daemon(1, 0); #else switch (fork()) { case -1: syslog(LOG_ERR, "fork: %m"); exit(1); /*FALLTHROUGH*/ case 0: /* child */ break; default: /* parent */ exit(0); } n = open(_PATH_DEVNULL, O_RDONLY); (void) dup2(n, 0); (void) dup2(n, 1); (void) dup2(n, 2); if (n > 2) (void) my_close(n); #ifdef SYSV setpgrp(); #else { struct itimerval ival; /* * The open below may hang on pseudo ttys if the person * who starts named logs out before this point. * * needmaint may get set inapropriately if the open * hangs, but all that will happen is we will see that * no maintenance is required. */ bzero((char *)&ival, sizeof(ival)); ival.it_value.tv_sec = 120; (void) setitimer(ITIMER_REAL, &ival, (struct itimerval *)NULL); n = open(_PATH_TTY, O_RDWR); ival.it_value.tv_sec = 0; (void) setitimer(ITIMER_REAL, &ival, (struct itimerval *)NULL); if (n > 0) { (void) ioctl(n, TIOCNOTTY, (char *)NULL); (void) my_close(n); } } #endif /* SYSV */ #endif /* HAVE_DAEMON */ } #endif /* USE_SETSID */ #ifdef WANT_PIDFILE /* tuck my process id away again */ fp = fopen(PidFile, "w"); if (fp != NULL) { fprintf(fp, "%d\n", getpid()); (void) my_fclose(fp); } #endif syslog(LOG_NOTICE, "Ready to answer queries.\n"); 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"); } FD_ZERO(&mask); FD_SET(vs, &mask); for (dqp = datagramq; dqp != QDATAGRAM_NULL; dqp = dqp->dq_next) FD_SET(dqp->dq_dfd, &mask); for (;;) { #ifdef DEBUG if (ddt && debug == 0) { fprintf(ddt,"Debug turned OFF\n"); (void) my_fclose(ddt); ddt = 0; } #endif #ifdef ALLOW_UPDATES if (needToExit) { struct zoneinfo *zp; sigblock(~0); /* * Block all blockable signals * to ensure a consistant * state during final dump */ dprintf(1, (ddt, "Received shutdown signal\n")); for (zp = zones; zp < &zones[nzones]; zp++) { if (zp->z_flags & Z_CHANGED) zonedump(zp); } exit(0); } #endif /* ALLOW_UPDATES */ if (needreload) { needreload = 0; db_reload(); } if (needStatsDump) { needStatsDump = 0; ns_stats(); } if (needzoneload) { needzoneload = 0; loadxfer(); } 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); /* ** The tv_sec field might be unsigned ** and thus cannot be negative. */ if ((int32_t) retryqp->q_time <= tt.tv_sec) { retry(retryqp); continue; } t.tv_sec = (int32_t) retryqp->q_time - tt.tv_sec; tp = &t; } else tp = NULL; tmpmask = mask; n = select(nfds, &tmpmask, (fd_set *)NULL, (fd_set *)NULL, tp); if (n < 0) { if (errno != EINTR) { syslog(LOG_ERR, "select: %m"); sleep(60); } } 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++) { /*XXX*/ from_len = sizeof(from_addr); if ((n = recvfrom(dqp->dq_dfd, buf, sizeof(buf), 0, (struct sockaddr *)&from_addr, &from_len)) < 0) { if ((n < 0) && (errno == PORT_WOULDBLK)) break; syslog(LOG_WARNING, "recvfrom: %m"); break; } if (n == 0) break; gettime(&tt); dprintf(1, (ddt, "\ndatagram from [%s].%d, fd %d, len %d; now %s", inet_ntoa(from_addr.sin_addr), ntohs(from_addr.sin_port), dqp->dq_dfd, n, ctime(&tt.tv_sec))); #ifdef DEBUG 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. ** ** Note that a "continue" in here takes us back to the select() ** which, if our accept() failed, will bring us back here. */ if (FD_ISSET(vs, &tmpmask)) { from_len = sizeof(from_addr); rfd = accept(vs, (struct sockaddr *)&from_addr, &from_len); if (rfd < 0 && errno == EINTR) continue; if (rfd < 0 && errno == EMFILE && streamq) { maxctime = 0; candidate = NULL; for (sp = streamq; sp; sp = nextsp) { nextsp = sp->s_next; if (sp->s_refcnt) continue; gettime(&tt); lasttime = tt.tv_sec - sp->s_time; if (lasttime >= VQEXPIRY) sqrm(sp); else if (lasttime > maxctime) { candidate = sp; maxctime = lasttime; } } if (candidate) sqrm(candidate); continue; } if (rfd < 0) { syslog(LOG_WARNING, "accept: %m"); continue; } if ((n = fcntl(rfd, F_GETFL, 0)) < 0) { syslog(LOG_ERR, "fcntl(rfd, F_GETFL): %m"); (void) my_close(rfd); continue; } if (fcntl(rfd, F_SETFL, n|PORT_NONBLOCK) != 0) { syslog(LOG_ERR, "fcntl(rfd, NONBLOCK): %m"); (void) my_close(rfd); continue; } #if defined(IP_OPTIONS) len = sizeof ip_opts; if (getsockopt(rfd, IPPROTO_IP, IP_OPTIONS, (char *)ip_opts, &len) < 0) { syslog(LOG_ERR, "getsockopt(rfd, IP_OPTIONS): %m"); (void) my_close(rfd); continue; } if (len != 0) { nameserIncr(from_addr.sin_addr, nssRcvdOpts); if (!haveComplained((char*) from_addr.sin_addr.s_addr, "rcvd ip options")) { syslog(LOG_NOTICE, "rcvd IP_OPTIONS from [%s].%d (ignored)", inet_ntoa(from_addr.sin_addr), ntohs(from_addr.sin_port)); } if (setsockopt(rfd, IPPROTO_IP, IP_OPTIONS, NULL, 0) < 0) { syslog(LOG_ERR, "setsockopt(!IP_OPTIONS): %m"); (void) my_close(rfd); continue; } } #endif if (setsockopt(rfd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) { syslog(LOG_ERR, "setsockopt(rfd, KEEPALIVE): %m"); (void) my_close(rfd); continue; } if ((sp = sqadd()) == QSTREAM_NULL) { (void) my_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_bufp = (u_char *)&sp->s_tempsize; FD_SET(rfd, &mask); FD_SET(rfd, &tmpmask); dprintf(1, (ddt, "\nTCP connection from [%s].%d (fd %d)\n", inet_ntoa(sp->s_from.sin_addr), ntohs(sp->s_from.sin_port), rfd)); } if (streamq) { dprintf(3, (ddt, "streamq = 0x%x\n",streamq)); } for (sp = streamq; sp != QSTREAM_NULL; sp = nextsp) { nextsp = sp->s_next; if (!FD_ISSET(sp->s_rfd, &tmpmask)) continue; dprintf(5, (ddt, "sp x%x rfd %d size %d time %d next x%x\n", sp, sp->s_rfd, sp->s_size, sp->s_time, sp->s_next)); dprintf(5, (ddt, "\tbufsize %d buf x%x bufp x%x\n", sp->s_bufsize, sp->s_buf, sp->s_bufp)); if (sp->s_size < 0) { size = INT16SZ - (sp->s_bufp - (u_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 < 0) && (errno == PORT_WOULDBLK)) continue; if (n <= 0) { sqrm(sp); continue; } if ((sp->s_bufp - (u_char *)&sp->s_tempsize) == INT16SZ) { sp->s_size = htons(sp->s_tempsize); if (sp->s_bufsize == 0) { if ( (sp->s_buf = (u_char *) malloc(rbufsize)) == NULL) { sp->s_buf = buf; sp->s_size = sizeof(buf); } else { sp->s_bufsize = rbufsize; } } if (sp->s_size > sp->s_bufsize && sp->s_bufsize != 0 ) { sp->s_buf = (u_char *) realloc((char *)sp->s_buf, (unsigned)sp->s_size); if (sp->s_buf == 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 > INT16SZ)) { 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, HFIXEDSZ); continue; } if ((n == -1) && (errno == PORT_WOULDBLK)) continue; if (n <= 0) { sqrm(sp); continue; } /* * Consult database to get the answer. */ if (sp->s_size == 0) { nameserIncr(sp->s_from.sin_addr, nssRcvdTCP); sq_query(sp); ns_req(sp->s_buf, sp->s_bufp - sp->s_buf, sp->s_bufsize, sp, &sp->s_from, -1); /* ns_req() can call sqrm() - check for it */ if (sq_here(sp)) { sp->s_bufp = (u_char *)&sp->s_tempsize; sp->s_size = -1; } continue; } } } /* NOTREACHED */ } void getnetconf() { register struct netinfo *ntp; struct netinfo *ontp; struct ifconf ifc; struct ifreq ifreq, *ifr; struct qdatagram *dqp; static int first = 1; char buf[BUFSIZ], *cp, *cplim; u_int32_t nm; time_t my_generation = time(NULL); ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl(vs, SIOCGIFCONF, (char *)&ifc) < 0) { syslog(LOG_ERR, "get interface configuration: %m - exiting"); exit(1); } ntp = NULL; #if defined(AF_LINK) && !defined(RISCOS_BSD) #define my_max(a, b) (a > b ? a : b) #define my_size(p) my_max((p).sa_len, sizeof(p)) #else #define my_size(p) (sizeof (p)) #endif cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */ for (cp = buf; cp < cplim; cp += sizeof (ifr->ifr_name) + my_size(ifr->ifr_addr)) { #undef my_size ifr = (struct ifreq *)cp; if (ifr->ifr_addr.sa_family != AF_INET || ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr == 0) { continue; } ifreq = *ifr; /* * Don't test IFF_UP, packets may still be received at this * address if any other interface is up. */ #if !defined(BSD) || (BSD < 199103) if (ioctl(vs, SIOCGIFADDR, (char *)&ifreq) < 0) { syslog(LOG_ERR, "get interface addr: %m"); continue; } #endif dprintf(1, (ddt, "considering [%s]\n", inet_ntoa(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr))); /* build datagram queue */ /* * look for an already existing source interface address. * This happens mostly when reinitializing. Also, if * the machine has multiple point to point interfaces, then * the local address may appear more than once. */ if (dqp = aIsUs(((struct sockaddr_in *)&ifreq.ifr_addr) ->sin_addr)) { dprintf(1, (ddt, "dup interface address %s on %s\n", inet_ntoa(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr), ifreq.ifr_name)); dqp->dq_gen = my_generation; continue; } /* * Skip over address 0.0.0.0 since this will conflict * with binding to wildcard address later. Interfaces * which are not completely configured can have this addr. */ if (((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr == 0x00000000) { /* XXX */ dprintf(1, (ddt, "skipping address 0.0.0.0 on %s\n", ifreq.ifr_name)); continue; } if ((dqp = (struct qdatagram *) calloc(1, sizeof(struct qdatagram)) ) == NULL) { syslog(LOG_ERR, "getnetconf: malloc: %m"); exit(12); } dqp->dq_next = datagramq; datagramq = dqp; dqp->dq_addr = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr; dqp->dq_gen = my_generation; opensocket(dqp); dprintf(1, (ddt, "listening [%s]\n", inet_ntoa(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr))); /* * Add interface to list of directly-attached (sub)nets * for use in sorting addresses. */ 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"); ntp->mask = net_mask(ntp->my_addr); } else 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 == inet_addr("127.0.0.1")) /* XXX */ #endif { if (netloop.my_addr.s_addr == 0) { netloop.my_addr = ntp->my_addr; netloop.mask = 0xffffffff; netloop.addr = ntp->my_addr.s_addr; dprintf(1, (ddt, "loopback address: x%lx\n", netloop.my_addr.s_addr)); } 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->addr = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr; } else { ntp->addr = ntp->mask & ntp->my_addr.s_addr; } /* * Place on end of list of locally-attached (sub)nets, * but before logical nets for subnetted nets. */ ntp->next = *elocal; *elocal = ntp; if (elocal == enettab) enettab = &ntp->next; elocal = &ntp->next; ntp = NULL; } if (ntp) free((char *)ntp); /* * now go through the datagramq and delete anything that * does not have the current generation number. this is * how we catch interfaces that go away or change their * addresses. note that 0.0.0.0 is the wildcard element * and should never be deleted by this code. * * XXX - need to update enettab/elocal as well. */ dqflush(my_generation); /* * Create separate qdatagram structure for socket * wildcard address. */ if (first) { if (!(dqp = (struct qdatagram *)calloc(1, sizeof(*dqp)))) { syslog(LOG_ERR, "getnetconf: malloc: %m"); exit(12); } dqp->dq_next = datagramq; datagramq = dqp; dqp->dq_addr.s_addr = INADDR_ANY; opensocket(dqp); ds = dqp->dq_dfd; } /* * Compute logical networks to which we're connected * based on attached subnets; * used for sorting based on network configuration. */ for (ntp = nettab; ntp != NULL; ntp = ntp->next) { nm = net_mask(ntp->my_addr); if (nm != ntp->mask) { if (findnetinfo(ntp->my_addr)) continue; ontp = (struct netinfo *) malloc(sizeof(struct netinfo)); if (ontp == NULL) { syslog(LOG_ERR, "getnetconf: malloc: %m"); exit(12); } ontp->my_addr = ntp->my_addr; ontp->mask = nm; ontp->addr = ontp->my_addr.s_addr & nm; ontp->next = *enettab; *enettab = ontp; enettab = &ontp->next; } } first = 0; } /* * Find netinfo structure for logical network implied by address "addr", * if it's on list of local/favored networks. */ struct netinfo * findnetinfo(addr) struct in_addr addr; { register struct netinfo *ntp; u_int32_t net, mask; mask = net_mask(addr); net = addr.s_addr & mask; for (ntp = nettab; ntp != NULL; ntp = ntp->next) if (ntp->addr == net && ntp->mask == mask) return (ntp); return ((struct netinfo *) NULL); } #ifdef DEBUG static void printnetinfo(ntp) register struct netinfo *ntp; { for ( ; ntp != NULL; ntp = ntp->next) { fprintf(ddt,"addr x%lx mask x%lx", ntp->addr, ntp->mask); fprintf(ddt," my_addr x%lx", ntp->my_addr.s_addr); fprintf(ddt," %s\n", inet_ntoa(ntp->my_addr)); } } #endif static void opensocket(dqp) register struct qdatagram *dqp; { int n, m; int on = 1; /* * Open datagram sockets bound to interface address. */ if ((dqp->dq_dfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "socket(SOCK_DGRAM): %m - exiting"); exit(1); } dprintf(1, (ddt, "dqp->dq_addr %s d_dfd %d\n", inet_ntoa(dqp->dq_addr), dqp->dq_dfd)); if (setsockopt(dqp->dq_dfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) { syslog(LOG_ERR, "setsockopt(dqp->dq_dfd, reuseaddr): %m"); /* XXX press on regardless, this is not too serious. */ } #ifdef SO_RCVBUF m = sizeof(n); if ((getsockopt(dqp->dq_dfd, SOL_SOCKET, SO_RCVBUF, &n, &m) >= 0) && (m == sizeof(n)) && (n < rbufsize)) { (void) setsockopt(dqp->dq_dfd, SOL_SOCKET, SO_RCVBUF, (char *)&rbufsize, sizeof(rbufsize)); } #endif /* SO_RCVBUF */ if ((n = fcntl(dqp->dq_dfd, F_GETFL, 0)) < 0) { syslog(LOG_ERR, "fcntl(dfd, F_GETFL): %m"); /* XXX press on regardless, but this really is a problem. */ } else if (fcntl(dqp->dq_dfd, F_SETFL, n|PORT_NONBLOCK) != 0) { syslog(LOG_ERR, "fcntl(dqp->dq_dfd, non-blocking): %m"); /* XXX press on regardless, but this really is a problem. */ } /* * 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. */ nsaddr.sin_addr = dqp->dq_addr; if (bind(dqp->dq_dfd, (struct sockaddr *)&nsaddr, sizeof(nsaddr))) { syslog(LOG_ERR, "bind(dfd=%d, [%s].%d): %m - exiting", dqp->dq_dfd, inet_ntoa(nsaddr.sin_addr), ntohs(nsaddr.sin_port)); #if !defined(sun) exit(1); #endif } } /* ** Set flag saying to reload database upon receiving SIGHUP. ** Must make sure that someone isn't walking through a data ** structure at the time. */ static SIG_FN onhup() { int save_errno = errno; #if defined(SYSV) (void)signal(SIGHUP, onhup); #endif /* SYSV */ needreload = 1; errno = save_errno; } /* ** Set flag saying to call ns_maint() ** Must make sure that someone isn't walking through a data ** structure at the time. */ static SIG_FN maint_alarm() { int save_errno = errno; #if defined(SYSV) (void)signal(SIGALRM, maint_alarm); #endif /* SYSV */ needmaint = 1; errno = save_errno; } #ifdef ALLOW_UPDATES /* * Signal handler to schedule shutdown. Just set flag, to ensure a consistent * state during dump. */ static SIG_FN 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 */ static SIG_FN setdumpflg() { int save_errno = errno; #if defined(SYSV) (void)signal(SIGINT, setdumpflg); #endif /* SYSV */ needToDoadump = 1; errno = save_errno; } /* ** Turn on or off debuging by open or closeing the debug file */ static void setdebug(code) int code; { #if defined(lint) && !defined(DEBUG) code = code; #endif #ifdef DEBUG if (code) { int n; ddt = freopen(debugfile, "w+", stderr); if ( ddt == NULL) { syslog(LOG_WARNING, "can't open debug file %s: %m", debugfile); debug = 0; } else { #if defined(SYSV) setvbuf(ddt, NULL, _IOLBF, BUFSIZ); #else setlinebuf(ddt); #endif if ((n = fcntl(fileno(ddt), F_GETFL, 0)) < 0) { syslog(LOG_WARNING, "fcntl(ddt, F_GETFL): %m"); } else { (void) fcntl(fileno(ddt), F_SETFL, n|O_APPEND); } } } else debug = 0; /* delay closing ddt, we might interrupt someone */ #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. */ static SIG_FN setIncrDbgFlg() { int save_errno = errno; #if defined(SYSV) (void)signal(SIGUSR1, setIncrDbgFlg); #endif /* SYSV */ #ifdef DEBUG if (debug == 0) { debug++; setdebug(1); } else { debug++; } if (debug) fprintf(ddt, "Debug turned ON, Level %d\n", debug); #endif errno = save_errno; } /* ** Catch a special signal to turn off debugging */ static SIG_FN setNoDbgFlg() { int save_errno = errno; #if defined(SYSV) (void)signal(SIGUSR2, setNoDbgFlg); #endif /* SYSV */ setdebug(0); errno = save_errno; } #if defined(QRYLOG) && defined(SIGWINCH) /* ** Set flag for query logging */ static SIG_FN setQrylogFlg() { int save_errno = errno; #if defined(SYSV) (void)signal(SIGWINCH, setQrylogFlg); #endif /* SYSV */ qrylog = !qrylog; syslog(LOG_NOTICE, "query log %s\n", qrylog ?"on" :"off"); errno = save_errno; } #endif /*QRYLOG && SIGWINCH*/ /* ** Set flag for statistics dump */ static SIG_FN setstatsflg() { int save_errno = errno; #if defined(SYSV) (void)signal(SIGIOT, setstatsflg); #endif /* SYSV */ needStatsDump = 1; errno = save_errno; } static SIG_FN setchkptflg() { int save_errno = errno; #if defined(SYSV) (void)signal(SIGQUIT, setchkptflg); #endif /* SYSV */ needToChkpt = 1; errno = save_errno; } /* ** Catch a special signal SIGSYS ** ** this is setup to fork and exit to drop to /usr/tmp/gmon.out ** and keep the server running */ #ifdef SIGSYS static SIG_FN sigprof() { int save_errno = errno; #if defined(SYSV) (void)signal(SIGSYS, sigprof); #endif /* SYSV */ dprintf(1, (ddt, "sigprof()\n")); if (fork() == 0) { (void) chdir(_PATH_TMPDIR); exit(1); } errno = save_errno; } #endif /* SIGSYS */ /* ** Routines for managing stream queue */ static struct qstream * sqadd() { register struct qstream *sqp; if (!(sqp = (struct qstream *)calloc(1, sizeof(struct qstream)))) { syslog(LOG_ERR, "sqadd: calloc: %m"); return (QSTREAM_NULL); } dprintf(3, (ddt, "sqadd(x%x)\n", sqp)); sqp->s_next = streamq; streamq = sqp; return (sqp); } /* sqrm(qp) * remove stream queue structure `qp'. * no current queries may refer to this stream when it is removed. * side effects: * memory is deallocated. sockets are closed. lists are relinked. */ void sqrm(qp) register struct qstream *qp; { register struct qstream *qsp; dprintf(2, (ddt, "sqrm(%#x, %d ) rfcnt=%d\n", qp, qp->s_rfd, qp->s_refcnt)); if (qp->s_bufsize != 0) free(qp->s_buf); FD_CLR(qp->s_rfd, &mask); (void) my_close(qp->s_rfd); if (qp == streamq) { streamq = qp->s_next; } else { for (qsp = streamq; qsp && (qsp->s_next != qp); qsp = qsp->s_next) ; if (qsp) { qsp->s_next = qp->s_next; } } free((char *)qp); } /* void * sqflush(allbut) * call sqrm() on all open streams except `allbut' * side effects: * global list `streamq' modified * idiocy: * is N^2 due to the scan inside of sqrm() */ void sqflush(allbut) register struct qstream *allbut; { register struct qstream *sp, *spnext; for (sp = streamq; sp != NULL; sp = spnext) { spnext = sp->s_next; if (sp != allbut) sqrm(sp); } } /* void * dqflush(gen) * close/deallocate all the udp sockets, unless `gen' != (time_t)0 * in which case all those not matching this generation will * be deleted except the 0.0.0.0 element, and syslog() will * be called whenever something is deleted. * side effects: * global list `datagramq' is modified */ void dqflush(gen) register time_t gen; { register struct qdatagram *dqp, *pqp, *nqp; for (pqp = NULL, dqp = datagramq; dqp != NULL; pqp = dqp, dqp = nqp) { nqp = dqp->dq_next; if (gen != (time_t)0) { if (dqp->dq_addr.s_addr == INADDR_ANY || dqp->dq_gen == gen) continue; syslog(LOG_CRIT, "interface [%s] missing; deleting", inet_ntoa(dqp->dq_addr)); } if (pqp != NULL) pqp->dq_next = dqp->dq_next; else datagramq = dqp->dq_next; free(dqp); } } /* int * sq_here(sp) * determine whether stream 'sp' is still on the streamq * return: * boolean: is it here? */ static int sq_here(sp) register struct qstream *sp; { register struct qstream *t; for (t = streamq; t != NULL; t = t->s_next) if (t == sp) return (1); return (0); } /* * Initiate query on stream; * mark as referenced and stop selecting for input. */ static void sq_query(sp) register struct qstream *sp; { sp->s_refcnt++; FD_CLR(sp->s_rfd, &mask); } /* * Note that the current request on a stream has completed, * and that we should continue looking for requests on the stream. */ void sq_done(sp) register struct qstream *sp; { sp->s_refcnt = 0; sp->s_time = tt.tv_sec; FD_SET(sp->s_rfd, &mask); } void ns_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++ = ' '; } u_int32_t net_mask(in) struct in_addr in; { register u_int32_t 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)); } #if defined(BSD43_BSD43_NFS) /* junk needed for old Sun NFS licensees */ #undef dn_skipname extern char *dn_skipname(); char *(*hack_skipname)() = dn_skipname; #endif