head 1.32; access; symbols; locks; comment @ * @; 1.32 date 2001.05.10.23.15.14; author pkern; state Exp; branches; next 1.31; 1.31 date 2001.04.18.17.46.44; author pkern; state Exp; branches; next 1.30; 1.30 date 2001.04.03.18.22.35; author pkern; state Exp; branches; next 1.29; 1.29 date 2001.03.29.20.22.28; author pkern; state Exp; branches; next 1.28; 1.28 date 2001.03.29.18.40.59; author pkern; state Exp; branches; next 1.27; 1.27 date 2000.03.07.20.35.24; author pkern; state Exp; branches; next 1.26; 1.26 date 2000.02.29.17.57.11; author pkern; state Exp; branches; next 1.25; 1.25 date 99.10.28.14.28.19; author pkern; state Exp; branches; next 1.24; 1.24 date 99.10.08.22.45.43; author pkern; state Exp; branches; next 1.23; 1.23 date 99.10.08.21.23.25; author pkern; state Exp; branches; next 1.22; 1.22 date 99.09.01.14.58.09; author pkern; state Exp; branches; next 1.21; 1.21 date 99.08.18.18.00.43; author pkern; state Exp; branches; next 1.20; 1.20 date 99.08.13.20.57.56; author pkern; state Exp; branches; next 1.19; 1.19 date 99.08.13.18.05.47; author pkern; state Exp; branches; next 1.18; 1.18 date 99.08.11.22.34.10; author pkern; state Exp; branches; next 1.17; 1.17 date 99.08.09.23.09.38; author pkern; state Exp; branches; next 1.16; 1.16 date 99.08.09.22.27.41; author pkern; state Exp; branches; next 1.15; 1.15 date 98.12.18.20.33.44; author pkern; state Exp; branches; next 1.14; 1.14 date 98.12.18.19.58.55; author pkern; state Exp; branches; next 1.13; 1.13 date 97.07.07.15.26.37; author pkern; state Exp; branches; next 1.12; 1.12 date 97.07.07.15.15.17; author pkern; state Exp; branches; next 1.11; 1.11 date 96.02.09.13.49.14; author pkern; state Exp; branches; next 1.10; 1.10 date 95.05.04.11.17.23; author pkern; state Exp; branches; next 1.9; 1.9 date 95.05.04.11.09.32; author pkern; state Exp; branches; next 1.8; 1.8 date 94.11.17.09.56.03; author pkern; state Exp; branches; next 1.7; 1.7 date 93.05.28.17.51.07; author pkern; state Exp; branches; next 1.6; 1.6 date 93.05.28.17.47.04; author pkern; state Exp; branches; next 1.5; 1.5 date 92.03.12.11.44.38; author pkern; state Exp; branches; next 1.4; 1.4 date 92.03.12.11.40.51; author pkern; state Exp; branches; next 1.3; 1.3 date 92.01.08.17.29.17; author pkern; state Exp; branches; next 1.2; 1.2 date 90.08.01.15.29.20; author cks; state Exp; branches; next 1.1; 1.1 date 90.08.01.15.28.33; author cks; state Exp; branches; next ; desc @A logging, restrictable, flexible version of inetd. @ 1.32 log @oops. bug. @ text @/* * ninetd - an alternative inetd. This one implements restriction * lists, per-connect logging, usage statistics logging, * and per interface services. The latter allows UDP * servers to be implemented properly on routers. * * Copyright (c) 1990 University of Toronto. All rights reserved. * Anyone may use or copy this software, except that it may not be * sold for profit, that this copyright notice remain intact, and that * credit is given where it is due. The University of Toronto and the * author make no warranty and accept no liability for this software. * Written by Dennis Ferguson . * Bug fixes by Chris Siebenmann . * Additions by Paul Kern . */ #ifndef lint static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.31 2001/04/18 17:46:44 pkern Exp $"; #endif #ifdef _AIX #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _AIX #include #endif #ifdef _AIX43 #define setpgrp setpgid #endif #ifdef LIBWRAP #include int allow_severity; int deny_severity; #endif #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) #define STRNEQ(a, b, n) (*(a) == *(b) && strncmp((a), (b), (n)) == 0) /* * We retry things we can't get open right away. This is the * retry period. */ #define RETRYTIME (60*5) /* * We expect child and alarm interrupts, and use the hangup * interrupt to reconfigure. */ #define BLOCKMASK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) #define BLOCKSIGS(osig) (osig) = sigblock(BLOCKMASK) #define UNBLOCKSIGS(osig) (void) sigsetmask((osig)) /* * This server really only understands TCP and UDP sockets. These * are the types. */ #define T_TCP 1 #define T_UDP 2 /* * Length of an interface name. This is hardcoded in struct ifreq's. */ #define INTER_NAME_LEN 16 /* * Maximum length of an argument list for an external server. */ #define MAXARGV 8 /* * Maximum concatenated length of a line in the configuration file */ #define MAXLINE 2048 /* * Absolute maximum number of interfaces. We actually declare * space as needed. */ #define MAXINTERFACES 64 /* * Maximum number of restrictions allowed */ #define MAXRESTRICT 512 /* * Buffer size we use for things */ #define BUFFERSIZE 4096 /* * Masks to use as defaults for address-and-mask restrictions */ #define DEFMASK 0 #define HOSTMASK 0xffffffff /* * Configuration keyword table */ struct keyword { char *word; int numargs; int key; }; /* * Generic identifiers */ #define KEY_ERROR (-1) #define KEY_NONE 0 /* * Configuration keyword identifiers */ #define CF_INTERFACE 1 #define CF_STATS 2 #define CF_CONNECT 3 #define CF_LOG 4 #define CF_RESTRICT 5 #define CF_USER 6 #define CF_PROGRAM 7 #define CF_INTERNAL 8 #ifdef LIBWRAP #define CF_LIBWRAP 9 #endif #define CF_MAXACTIVE 10 #define CF_FILTER 11 #define CF_BACKLOG 12 /* * Configuration keyword tables */ struct keyword config_keywords[] = { { "interface", 1, CF_INTERFACE }, { "stats", 1, CF_STATS }, { "connect", 0, CF_CONNECT }, { "log", 0, CF_LOG }, { "restrict", 1, CF_RESTRICT }, { "user", 1, CF_USER }, { "program", 0, CF_PROGRAM }, { "internal", 1, CF_INTERNAL }, #ifdef LIBWRAP { "libwrap", 1, CF_LIBWRAP }, #endif { "max", 1, CF_MAXACTIVE }, { "filter", 1, CF_FILTER }, { "backlog", 1, CF_BACKLOG }, { "", 0, KEY_NONE } }; /* * Restriction keywords and table */ #define RS_ALLOW 1 #define RS_DISALLOW 2 #define RS_INCLUDE 3 #define FL_INSERT 4 #define DECL_FILTER 101 #ifdef LIBWRAP #define DECL_WRAPPER 102 #endif #define END_DECL 999 struct keyword restrict_keywords[] = { { "allow", 1, RS_ALLOW }, { "disallow", 1, RS_DISALLOW }, { "include", 1, RS_INCLUDE }, { "end", 0, END_DECL }, { "}", 0, END_DECL }, { "insert", 1, FL_INSERT }, { "", 0, KEY_NONE } }; struct keyword declare_keywords[] = { { "}", 0, END_DECL }, { "filter", 1, DECL_FILTER }, #ifdef LIBWRAP { "libwrap", 0, DECL_WRAPPER }, #endif { "", 0, KEY_NONE } }; #ifdef LIBWRAP #define TW_ALLOW 1 #define TW_DENY 2 #ifndef MAXPATHLEN #define MAXPATHLEN 1024 #endif char tw_allow[MAXPATHLEN], tw_deny[MAXPATHLEN]; struct keyword libwrap_keywords[] = { { "}", 0, END_DECL }, { "allow", 1, TW_ALLOW }, { "deny", 1, TW_DENY }, { "", 0, KEY_NONE } }; #endif /* * Some simple services are provided internally. These are defined * following. */ int tcp_echo(), udp_echo(), tcp_discard(), udp_discard(); int tcp_time(), udp_time(), tcp_daytime(), udp_daytime(); int tcp_chargen(), udp_chargen(), tcp_dropconn(); struct internal { char *service; /* internally provided service name */ short type; /* type of service supported */ short fork; /* 1 if should fork before call */ int (*doit_rtn)(); /* function which performs it */ } internal_servers[] = { /* Echo received data */ "tcp_echo", T_TCP, 1, tcp_echo, "udp_echo", T_UDP, 0, udp_echo, /* Internet /dev/null */ "tcp_discard", T_TCP, 1, tcp_discard, "udp_discard", T_UDP, 0, udp_discard, /* Return 32 bit time since 1970 */ "tcp_time", T_TCP, 0, tcp_time, "udp_time", T_UDP, 0, udp_time, /* Return human-readable time */ "tcp_daytime", T_TCP, 0, tcp_daytime, "udp_daytime", T_UDP, 0, udp_daytime, /* Familiar character generator */ "tcp_chargen", T_TCP, 1, tcp_chargen, "udp_chargen", T_UDP, 0, udp_chargen, /* Service to simply drop the connection, for logging */ "tcp_dropconn", T_TCP, 0, tcp_dropconn, NULL, 0, 0, 0 }; /* * An external server is known by the executable to run and * the argv list it is to be run with. */ struct external { char *executable; char *argv[MAXARGV+1]; #ifdef compat char addrb[64]; /* SunOS 3.5 compat */ #endif }; /* * We keep a list of interfaces we can see on the machine for * occasions when we have to open multiple sockets. */ struct interface { char name[INTER_NAME_LEN]; /* length is hardcoded in ifreq */ struct in_addr address; /* address of interface */ int flags; /* flags for the interface */ }; #define INT_DEFAULT 0x1 /* this is default interface */ #define INT_INCOMPLETE 0x2 /* incomplete address */ /* * Restrictions. These are done by address-and-mask matches. */ struct restrict { u_long address; u_long mask; int allow; }; #ifndef PID_TYPE #define PID_TYPE int #endif /* * Limits on number of active servers per service. * Enforcing limits means keeping track of the PIDS of active servers. */ struct active { int max; /* maximum number of active servers. */ int num; /* number of servers currently active. */ int pidc; /* size of pidv (number of PIDs). */ PID_TYPE *pidv; /* PIDs of the active servers. */ }; /* * Server description. An individual server is identified by * the triple {protocol, port#, interface}. Protocol is always * T_TCP or T_UDP, while the interface is either one of the * interfaces on the machine, or default. */ struct server { struct server *next; int type; /* TCP or UDP */ char *name; /* name of service */ struct sockaddr_in address; /* address & port number bound */ int interface_index; /* index into interface array */ int flags; /* flags for this server */ char *user; /* name of user to use when running */ struct internal *internal_handler; /* internal handler, if any */ struct external *external_handler; /* external handler otherwise */ int fd; /* file descriptor */ struct timeval laststattime; /* last time stats printed */ int statinterval; /* stat print interval */ int statcount; /* count of requests since last time */ struct restrict *reslist; /* restrict list */ int len_reslist; /* length of the restrict list */ PID_TYPE child_pid; /* single-threaded server */ #ifdef LIBWRAP char *libwrap; /* tcpwrapper identity */ #endif struct active active; int maxbacklog; /* max listen(2) queue size */ }; /* * Flags used in the server description */ #define SERVE_DELETE 0x1 /* delete after merge operation */ #define SERVE_LOG 0x2 /* connection logging set */ #define SERVE_STATS 0x4 /* print stats once in a while */ #define SERVE_CONNECT 0x8 /* datagram service does connect */ #define SERVE_HAVE_CHILD 0x10 /* we have an active child */ #define SERVE_DOWN 0x20 /* this service is down (retry) */ #define SERVE_MAXACTIVE 0x40 /* we have a maximum for active children */ #define SERVE_FILTERED 0x80 /* reslist is a filter */ #ifdef _AIX char tzenv[] = "TZ=\0xxxxNNyyyy"; #endif /* * Clean environment for running things with */ char *clean_envp[] = { "TERM=network", "HOME=/", #ifdef _AIX #define TZENV 2 tzenv, #endif 0 }; /* * The interface and server list heads. */ struct server *servlist = 0; struct interface *interlist = 0; int ninterfaces; struct filter { struct filter *next; char *name; /* filter name */ struct restrict *reslist; /* restrict list */ int len_reslist; /* length of the restrict list */ }; struct filter *filtlist = 0; /* * File descriptor mask maintenance. For select. */ int nsockets; /* number of open sockets */ int maxsockets; /* highest open fd number */ fd_set allsockets; /* mask of sockets to check */ /* * Counters so the timer routines will know we have something to do. */ int numserverdown; /* number of servers down */ int numlogging; /* number of servers requiring logging */ int isalarmed; /* set when alarm is set */ int waitingforchild; /* keep count of children being waited upon */ int checkforactive; /* keep count of servers for services with cielings */ /* * Configuration file path */ #ifndef CONFIG #define CONFIG "/etc/ninetd.conf" #endif char *config_file = CONFIG; /* * Miscellany */ char *progname; int debug = 0; int backlog = 10; /* default listen(2) queue limit */ extern int errno; char **Argv; char *LastArg; #ifndef HOST_OKAY /* * main - parse arguments and handle options */ main(argc, argv, envp) int argc; char *argv[]; char *envp[]; { register struct server *svp; char **endp; int c; int errflg = 0; int osig; fd_set readable; extern int optind; extern char *optarg; int alarmed(); void configure(); int catch_child(); void detach(); void setsignals(); void process(); void getinterfaces(); Argv = argv; if (envp == 0 || *envp == 0) envp = argv; while (*envp) envp++; LastArg = envp[-1] + strlen(envp[-1]); progname = argv[0]; while ((c = getopt(argc, argv, "b:df:")) != EOF) switch (c) { case 'b': backlog = atoi(optarg); break; case 'd': ++debug; break; case 'f': config_file = optarg; break; default: errflg++; break; } if (errflg || optind != argc) { (void) fprintf(stderr, "usage: %s [-d] [-b maxbacklog] [-f config]\n", progname); exit(2); } /* * If not debugging, detach us from the controlling terminal */ if (!debug) detach(); #ifdef TZENV /* * Pass along current timezone. */ strcat(tzenv, (char *)getenv("TZ")); #endif /* * Open the log file */ #ifdef LOG_DAEMON openlog("ninetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); #else /* OH joy, 4.2 BSD syslog. Grump. */ openlog("ninetd", LOG_PID); #endif /* * Get interface list for host */ getinterfaces(); /* * Block signals, set up signal handlers and read the * configuration file. */ BLOCKSIGS(osig); setsignals(alarmed, configure, catch_child); configure(); UNBLOCKSIGS(osig); /* * Now the loop. Select on the current mask, see who * is ready when we wake up, and go to it. */ for (;;) { /* * If no sockets are open, wait for someone to * reconfigure us. */ if (nsockets == 0) { BLOCKSIGS(osig); while (nsockets == 0) (void) sigpause(0L); UNBLOCKSIGS(osig); } /* * Wait for something to do */ readable = allsockets; c = select(maxsockets+1, &readable, (fd_set *)NULL, (fd_set *)NULL, (struct timeval *)NULL); if (c <= 0) { if (c < 0 && errno != EINTR) { syslog(LOG_WARNING, "select: %m"); sleep(1); } continue; } /* * Search the server list. Block signals while doing this. * Process anything which is ready. */ BLOCKSIGS(osig); for (svp = servlist; svp != 0; svp = svp->next) if (svp->fd > 0 && FD_ISSET(svp->fd, &readable)) process(svp); UNBLOCKSIGS(osig); } } /* * process - process a service request */ void process(serverp) struct server *serverp; { register struct server *svp; int n; PID_TYPE pid; int svc_fd; int dofork; int findid; int size; struct sockaddr_in peerid; int okayhost(); void eatpacket(); char *hosttoa(); svp = serverp; /* * Discover a couple of processing options */ if (svp->internal_handler == NULL || svp->internal_handler->fork) dofork = 1; else dofork = 0; #ifdef LIBWRAP /* * If libwrap is configured, then must fork first and leave * wrapper processing to the child in order to keep the parent * from being held hostage by any wrapper exec options. * Thanks to cks for catching that. So forking is mandatory * for any wrapped services. This includes internal services. */ if (svp->libwrap != NULL) dofork = 1; #endif if ((svp->flags & SERVE_LOG) || svp->len_reslist > 0) findid = 1; else findid = 0; /* * Do protocol specific processing */ switch (svp->type) { case T_TCP: if (debug) (void) fprintf(stderr, "process: someone wants %s/tcp\n", svp->name); svc_fd = accept(svp->fd, (struct sockaddr *)0, (int *)0); if (svc_fd < 0) { if (errno != EINTR) syslog(LOG_WARNING, "accept: %m"); return; } if (findid) { size = sizeof(struct sockaddr_in); if (getpeername(svc_fd, (struct sockaddr *)&peerid, &size) < 0) { syslog(LOG_WARNING, "getpeername: %m"); (void) close(svc_fd); return; } } break; case T_UDP: if (debug) (void) fprintf(stderr, "process: someone wants %s/udp\n", svp->name); svc_fd = svp->fd; if (findid) { size = sizeof(peerid); n = recvfrom(svc_fd, (char *)&n, 0, MSG_PEEK, (struct sockaddr *)&peerid, &size); if (n < 0) { #ifdef linux /* hmmm, is there an echo in here? */ if (errno == ECONNREFUSED) return; #endif syslog(LOG_WARNING, "%s/udp: recvfrom(MSG_PEEK): %m", svp->name); #ifndef linux eatpacket(svc_fd); #endif return; } } break; default: syslog(LOG_ERR, "hideous internal error! type = %d", svp->type); exit(1); /*NOTREACHED*/ } /* * Check for restrictions. If none, but logging is on, log * the attempt. If counting accesses, do that too. */ #define log_reason(why) \ syslog(LOG_ERR, "%s/%s: %s.%d %s on %s", \ svp->name, ((svp->type == T_TCP) ? "tcp" : "udp"), \ hosttoa(&peerid), ntohs(peerid.sin_port), \ (why), interlist[svp->interface_index].name) #define log_connect \ syslog(LOG_INFO, "%s/%s: %s.%d on %s", svp->name, \ ((svp->type == T_TCP) ? "tcp" : "udp"), \ hosttoa(&peerid), ntohs(peerid.sin_port), \ interlist[svp->interface_index].name) if (svp->len_reslist > 0 && svp->reslist != NULL) { if (!okayhost(&peerid, svp->reslist, svp->len_reslist)) { if (svp->flags & SERVE_LOG) log_reason("denied access"); if (svp->type == T_TCP) (void) close(svc_fd); else eatpacket(svc_fd); return; } } if (svp->flags & SERVE_MAXACTIVE) { if (svp->active.num >= svp->active.max) { if (svp->flags & SERVE_LOG) { if (svp->active.max == 0) log_reason("denied access"); else log_reason("exceeds limit"); } if (svp->type == T_TCP) (void) close(svc_fd); else eatpacket(svc_fd); return; } } if (svp->flags & SERVE_LOG) { #ifdef LIBWRAP /* * if libwrap-ed, delay any logging until after wrapper * processing has decided the fate of this connection. * wrapper processing is done after the fork. */ if (svp->libwrap == NULL) #endif log_connect; } if (svp->flags & SERVE_STATS) { svp->statcount++; } /* * If we have to fork, do it now. */ if (dofork) { pid = fork(); if (pid < 0) { /* boo-boo */ if (svp->type == T_TCP) (void) close(svc_fd); else eatpacket(svc_fd); sleep(1); return; } if (pid > 0) { /* parent */ if (svp->type == T_UDP && !(svp->flags & SERVE_CONNECT)) { svp->flags |= SERVE_HAVE_CHILD; svp->child_pid = pid; FD_CLR(svp->fd, &allsockets); nsockets--; waitingforchild++; } if (svp->active.max > 0) { svp->active.pidv[svp->active.num] = pid; svp->active.num++; checkforactive++; } if (svp->type == T_TCP) (void) close(svc_fd); return; } #ifdef LIBWRAP if (svp->libwrap != NULL) { struct hostent *hp = gethostbyaddr((char *)&peerid.sin_addr, sizeof(struct in_addr), AF_INET); char *peerid_str = hosttoa(&peerid); if (hosts_ctl(svp->libwrap, (hp) ? hp->h_name : STRING_UNKNOWN, peerid_str, (svp->user) ? svp->user : STRING_UNKNOWN) == 0) { if (svp->flags & SERVE_LOG) log_reason("denied access"); _exit(0); } if (svp->flags & SERVE_LOG) log_connect; } #endif /* LIBWRAP */ if (debug) { /* detach us from terminal */ if ((n = open("/dev/tty", O_RDWR)) >= 0) { (void) ioctl(n, TIOCNOTTY, 0); (void) close(n); } (void) setpgrp(0, 0); (void) signal(SIGTSTP, SIG_IGN); (void) signal(SIGTTIN, SIG_IGN); (void) signal(SIGTTOU, SIG_IGN); } /* * close everything we aren't going to need */ for (n = maxsockets; n > 2; n--) { if (n != svc_fd) (void) close(n); } } /* * If using an internal service, dispatch this now. */ if (svp->internal_handler != NULL) { svp->internal_handler->doit_rtn(svc_fd, svp); if (dofork) _exit(0); if (svp->type == T_TCP) (void) close(svc_fd); return; } /* * Now we're to the nitty-gritty. We have to exec * a server. Move the service fd onto the first * three file descriptors. If there was a user * associated with this, set our gid to that. Then * run the service. */ (void) sigsetmask(0L); (void) dup2(svc_fd, 0); (void) close(svc_fd); (void) dup2(0, 1); (void) dup2(0, 2); if (svp->user) { struct passwd *pwd; pwd = getpwnam(svp->user); if (pwd == NULL) { syslog(LOG_ERR, "getpwnam: %s: no such user", svp->user); if (svp->type != T_TCP) eatpacket(0); _exit(1); } if (pwd->pw_uid > 0) { (void) setgid((gid_t)pwd->pw_gid); initgroups(pwd->pw_name, pwd->pw_gid); (void) setuid((uid_t)pwd->pw_uid); } } #ifdef compat { /* SunOS 3.5 compat */ register int i; struct external *exp = svp->external_handler; sprintf(exp->addrb, "%X.%d", ntohl(peerid.sin_addr.s_addr), ntohs(peerid.sin_port)); for (i = 1; exp->argv[i]; i++) if (STREQ(exp->argv[i], "%A")) exp->argv[i] = exp->addrb; } #endif execve(svp->external_handler->executable, svp->external_handler->argv, clean_envp); /* * If we got here, we're in trouble. Print a message and * exit. */ syslog(LOG_ERR, "execve %s: %m", svp->external_handler->executable); if (svp->type != T_TCP) eatpacket(0); _exit(1); } /* * eatpacket - read a packet from a datagram socket and toss it */ void eatpacket(fd) int fd; { char buf[64]; (void) recv(fd, buf, sizeof buf, 0); } /* * detach - detach us from the controlling terminal */ void detach() { register int i; register int res; /* * Fork us off. Let the parent commit hara kiri. */ i = 5; for (;;) { res = fork(); if (res == -1) { if (i-- > 0) sleep(3); else { perror(progname); exit(1); } } else if (res > 0) { exit(0); } else { break; } } /* * Close all descriptors */ res = getdtablesize(); for (i = 0; i < res; i++) (void) close(i); /* * Open garbage to keep 0, 1 and 2 clear */ (void) open("/", O_RDONLY); (void) dup2(0, 1); (void) dup2(0, 2); /* * Now detach us */ i = open("/dev/tty", O_RDWR); if (i > 0) { (void) ioctl(i, TIOCNOTTY, (char *)0); (void) close(i); } /* * Restrain unnecessary signals */ (void) signal(SIGTSTP, SIG_IGN); (void) signal(SIGTTIN, SIG_IGN); (void) signal(SIGTTOU, SIG_IGN); } /* * setsignals - set up signal handlers */ void setsignals(alarm_hndlr, hup_hndlr, child_hndlr) void (*alarm_hndlr)(); void (*hup_hndlr)(); void (*child_hndlr)(); { struct sigvec sv; bzero((char *)&sv, sizeof(sv)); sv.sv_mask = BLOCKMASK; if (alarm_hndlr != 0) { sv.sv_handler = alarm_hndlr; sigvec(SIGALRM, &sv, (struct sigvec *)0); } if (hup_hndlr != 0) { sv.sv_handler = hup_hndlr; sigvec(SIGHUP, &sv, (struct sigvec *)0); } if (child_hndlr != 0) { sv.sv_handler = child_hndlr; sigvec(SIGCHLD, &sv, (struct sigvec *)0); } } /* * catch_child - catch SIGCHLD interrupts, inform waiting servers */ int catch_child() { register struct server *svp; #ifdef _AIX int status; #else union wait status; #endif PID_TYPE pid; for (;;) { pid = wait3(&status, WNOHANG, (struct rusage *)0); if (pid <= 0) break; if (debug) (void) fprintf(stderr, "catch_child: process %d terminated\n", pid); if (waitingforchild > 0) { for (svp = servlist; svp != NULL; svp = svp->next) if ((svp->flags & SERVE_HAVE_CHILD) && svp->child_pid == pid) break; if (svp != NULL) { #ifdef _AIX if (WIFEXITED(pid)) syslog(LOG_WARNING, "%s/%s: exit status 0x%x", svp->name, ((svp->type == T_TCP) ? "tcp" : "udp"), WEXITSTATUS(pid)); #else /* _AIX */ if (status.w_status != 0) syslog(LOG_WARNING, "%s/%s: exit status 0x%x", svp->name, ((svp->type == T_TCP) ? "tcp" : "udp"), status.w_status); #endif /* _AIX */ FD_SET(svp->fd, &allsockets); if (svp->fd > maxsockets) maxsockets = svp->fd; svp->flags &= ~SERVE_HAVE_CHILD; svp->child_pid = 0; nsockets++; waitingforchild--; continue; } } if (checkforactive > 0) { int foundpid = 0; register int i = 0, j = 0; for (svp = servlist; svp != NULL; svp = svp->next) { for (i = 0; i < svp->active.num; i++) { if (svp->active.pidv[i] != pid) continue; for (j = i+1; j < svp->active.num; i++, j++) svp->active.pidv[i] = svp->active.pidv[j]; svp->active.pidv[i] = 0; svp->active.num--; checkforactive--; foundpid = 1; break; } if (foundpid) break; } if (foundpid) continue; } } return 0; } /* * alarmed - handle an alarm interrupt */ int alarmed() { register struct server *svp; register long nowsec, nexttime, temp; int nsdown, nslogging, fd; struct timeval now; int opensocket(); void printstats(); nsdown = nslogging = 0; (void) gettimeofday(&now, (struct timezone *)0); nowsec = now.tv_sec; nexttime = -1; /* * Run through the list looking for servers which * need to be restarted and stats which need to be * logged. Keep a count of what you find. */ for (svp = servlist; svp != 0; svp = svp->next) { temp = -1; if (svp->flags & SERVE_DOWN) { nsdown++; temp = svp->laststattime.tv_sec + RETRYTIME - nowsec; if (temp <= 0) { svp->laststattime = now; fd = opensocket(svp->type, &svp->address, svp->maxbacklog); if (fd != 0) { /* * Got this one open. Record * this fact. */ svp->flags &= ~SERVE_DOWN; svp->fd = fd; nsdown--; } else { temp = RETRYTIME; } } } if (!(svp->flags & SERVE_DOWN) && (svp->flags & SERVE_STATS)) { nslogging++; temp = svp->laststattime.tv_sec + svp->statinterval - nowsec; if (temp < 0) { printstats(svp); svp->laststattime = now; temp = svp->statinterval; } } if (temp > 0) { if (nexttime < 0 || temp < nexttime) nexttime = temp; } } numserverdown = nsdown; numlogging = nslogging; if (nexttime > 0) { isalarmed = 1; alarm((int)nexttime); } else { isalarmed = 0; } return 0; } /* * setalarm - set an alarm interrupt for the next propitious moment */ void setalarm(now) struct timeval *now; { register struct server *svp; register long nowsec, nexttime, temp; nowsec = now->tv_sec; nexttime = -1; for (svp = servlist; svp != NULL; svp = svp->next) { if (svp->flags & SERVE_DOWN) { temp = svp->laststattime.tv_sec + 1 + RETRYTIME - nowsec; if (temp <= 0) temp = 1; if (nexttime < 0 || temp < nexttime) nexttime = temp; } else if (svp->flags & SERVE_STATS) { temp = svp->laststattime.tv_sec + 1 + svp->statinterval - nowsec; if (temp <= 0) temp = 1; if (nexttime < 0 || temp < nexttime) nexttime = temp; } } if (nexttime > 0) { isalarmed = 1; alarm((int)nexttime); } else { isalarmed = 0; } } /* * configure - read the configuration file, make our internal * configuration match */ void configure() { register char *tok; register struct server *svp; register struct filter *flp; struct filter *oldfilt, *findfilter(); int n; int key; FILE *fp; char *linebp; char *cf_resfile; char *cf_filter; char *cf_interfaces; char *proto; char *arg1, *arg2; u_long ltemp; char linebuf[MAXLINE]; struct restrict reslist[MAXRESTRICT]; struct server svr; struct external ext; struct timeval now; char okayinter[MAXINTERFACES]; char *getline(); char *gettoken(); struct external *get_external(); struct internal *find_internal(); int getrestrict(); int setfilter(); int getkeyword(); int atouint(); void addservice(); void rmservice(); void makeinterlist(); void setalarm(); void printservlist(); syslog(LOG_INFO, "config: %s", config_file); /* * Open the configuration file */ fp = fopen(config_file, "r"); if (fp == NULL) { syslog(LOG_ERR, "can't open %s!: %m", config_file); return; } /* * Mark all existing entries for deletion */ for (svp = servlist; svp != NULL; svp = svp->next) svp->flags |= SERVE_DELETE; /* * retire all existing filters. */ oldfilt = filtlist; filtlist = NULL; /* * Set number of failed opens and number of stat-printers * to zero. We'll count them again. */ numserverdown = 0; numlogging = 0; if (isalarmed) { alarm(0); isalarmed = 0; } /* * Get current time */ (void) gettimeofday(&now, (struct timezone *)0); /* * Read lines of input, processing until we hit the end * of the file. */ while ((linebp = getline(fp, linebuf, MAXLINE)) != NULL) { bzero((char *)&svr, sizeof(svr)); svr.laststattime = now; cf_filter = cf_resfile = cf_interfaces = NULL; /* * Fill in the service spec */ if ((tok = gettoken(&linebp)) == NULL) continue; if (STREQ(tok, "{")) { key = getkeyword(declare_keywords, &linebp, &arg1, &arg2); if (key == KEY_ERROR || key == KEY_NONE) continue; switch(key) { case DECL_FILTER: setfilter(fp, arg1, reslist, MAXRESTRICT); break; #ifdef LIBWRAP case DECL_WRAPPER: setwrap(fp); break; #endif } continue; } if (!getservice(tok, &svr.name, &proto, &svr.type, &svr.address.sin_port)) { syslog(LOG_ERR, "invalid service specification %s in file %s, ignored", tok, config_file); continue; } /* * Okay. Next are all keyword-argument combinations. * Run through decoding these. */ while ((key = getkeyword(config_keywords, &linebp, &arg1, &arg2)) != KEY_NONE && key != KEY_ERROR) { switch(key) { case CF_INTERFACE: cf_interfaces = arg1; break; case CF_STATS: if (!atouint(arg1, <emp)) { syslog(LOG_ERR, "stats interval %s undecodable", arg1); key = KEY_ERROR; } else { svr.statinterval = 60 * (int)ltemp; svr.flags |= SERVE_STATS; } break; case CF_CONNECT: if (svr.type == T_TCP) { syslog(LOG_ERR, "connect keyword inappropriate for a TCP service"); key = KEY_ERROR; } else { svr.flags |= SERVE_CONNECT; } break; case CF_LOG: svr.flags |= SERVE_LOG; break; case CF_RESTRICT: cf_resfile = arg1; break; case CF_USER: svr.user = arg1; break; case CF_PROGRAM: if (get_external(&ext, &linebp) == NULL) key = KEY_ERROR; else svr.external_handler = &ext; break; case CF_INTERNAL: svr.internal_handler = find_internal(arg1); if (svr.internal_handler == NULL) { syslog(LOG_ERR, "internal server %s unknown", arg1); key = KEY_ERROR; } break; #ifdef LIBWRAP case CF_LIBWRAP: svr.libwrap = arg1; break; #endif case CF_MAXACTIVE: if (!atouint(arg1, <emp)) { syslog(LOG_ERR, "max limit %s undecodable", arg1); key = KEY_ERROR; } else { svr.active.max = (int)ltemp; svr.flags |= SERVE_MAXACTIVE; } break; case CF_FILTER: cf_filter = arg1; break; case CF_BACKLOG: if (!atouint(arg1, <emp)) { syslog(LOG_ERR, "backlog %s undecodable", arg1); key = KEY_ERROR; } else { svr.maxbacklog = (int)ltemp; } break; } if (key == KEY_ERROR || key == CF_PROGRAM || key == CF_INTERNAL) break; } if (key == KEY_ERROR) { syslog(LOG_ERR, "%s/%s ignored due to error", svr.name, proto); continue; } else if (key == KEY_NONE) { syslog(LOG_ERR, "%s/%s: no program specification", svr.name, proto); continue; } /* * If we get to here, we have a valid service as far as * this has gone, anyway. If we have a restrict file, * read it now. */ if (cf_resfile != NULL) { svr.len_reslist = getrestrict(cf_resfile, reslist, MAXRESTRICT, 0); if (svr.len_reslist > 0) svr.reslist = reslist; } if (cf_filter != NULL && cf_resfile != NULL) { syslog(LOG_ERR, "%s/%s: warning: 'restrict' overrides 'filter'.", svr.name, proto); } else if (cf_filter != NULL) { svr.len_reslist = 0; flp = findfilter(cf_filter); if (flp != NULL) { svr.len_reslist = flp->len_reslist; if (svr.len_reslist > 0) { svr.flags |= SERVE_FILTERED; svr.reslist = flp->reslist; } } } /* * Now the tricky part. Run through the interface * list, determining whether we should set up this * service on the interface. For each one we must * open on, either update the existing server data * or open a new service by copying what we have. * Note that if no interface selection was given, * we use the default. Note also that the default * is always opened first, if at all, this to avoid * troubles with older multicasting kernels. */ makeinterlist(cf_interfaces, okayinter); for (n = 0; n < ninterfaces; n++) if (okayinter[n]) addservice(n, &svr); } (void) fclose(fp); /* * If we're here, the configuration has been completely read. * Go through the server list looking for guys to delete. */ while (servlist != NULL && (servlist->flags & SERVE_DELETE)) { svp = servlist; servlist = svp->next; rmservice(svp); } if (servlist != NULL) { svp = servlist; while (svp->next != NULL) { if (svp->next->flags & SERVE_DELETE) { struct server *osvp; osvp = svp->next; svp->next = osvp->next; rmservice(osvp); } else { svp = svp->next; } } } /* * should be safe to now delete all retired filters. * by now, nothing should have pointers into this list. */ while (oldfilt != NULL) { flp = oldfilt; oldfilt = flp->next; if (flp->len_reslist > 0) (void) free((char *)flp->reslist); (void) free(flp->name); (void) free((char *)flp); } /* * Finally, if there is work to do on a timeout, set it up. */ if (numlogging > 0 || numserverdown > 0) setalarm(&now); if (debug) printservlist(); #ifdef LIBWRAP syslog(LOG_INFO, "allow: %s", hosts_allow_table); syslog(LOG_INFO, "deny: %s", hosts_deny_table); #endif } /* * makeinterlist - make a list of interfaces we are permitted to use */ void makeinterlist(str, list) char *str; char *list; { register int i; register char *cp; register char *ep; int not; u_long netnum; char entry[256]; int decodenetnum(); /* * zero out the list */ cp = list; for (i = 0; i < ninterfaces; i++) cp[i] = 0; /* * If str is null, use default interface. Otherwise, * use the list as given. */ if (str == NULL || *str == '\0') { /* * Default interface always first */ list[0] = 1; return; } cp = str; for (;;) { /* * Make positive assumption */ not = 0; /* * Space past leading ',' */ while (*cp == ',') cp++; /* * If next is a !, this is instuction not to use * interface. */ if (*cp == '!') { cp++; if (*cp == ',') continue; not++; } /* * If this is the end of the list, return. Nothing more * to do. */ if (*cp == '\0') break; /* * Got one. Copy it out into temp storage */ ep = entry; while (*cp != ',' && *cp != '\0') *ep++ = *cp++; *ep = '\0'; /* * Check to see if this is "all". If so, mark * all interfaces that haven't been !'d as * golden. */ if (STREQ("all", entry)) { if (not) continue; for (i = 1; i < ninterfaces; i++) if (list[i] != 2) list[i] = 1; continue; } /* * Assume it is an interface name. Search * through the list looking for it. If found, record * this fact in the list. */ for (i = 0; i < ninterfaces; i++) if (STRNEQ(entry, interlist[i].name, INTER_NAME_LEN)) break; if (i < ninterfaces) { if (not) list[i] = 2; else if (list[i] != 2) list[i] = 1; continue; } /* * Maybe a network address. See if we have a match. */ if (decodenetnum(entry, &netnum)) { for (i = 0; i < ninterfaces; i++) if (!(interlist[i].flags & INT_INCOMPLETE) && interlist[i].address.s_addr == netnum) break; if (i < ninterfaces) { if (not) list[i] = 2; else if (list[i] != 2) list[i] = 1; continue; } } /* * Maybe do symbolic addresses here. */ } /* * Go through, turning all 2's into 0's */ for (i = 0; i < ninterfaces; i++) if (list[i] == 2) list[i] = 0; } /* * addservice - add a service to the list, deleting the old version * if appropriate. */ void addservice(inter, newserver) int inter; struct server *newserver; { register struct server *ns; register struct server *os; char *emalloc(); char *strsave(); struct external *copy_external(); void free_external(); void printstats(); int opensocket(); void closesocket(); /* * Search through the current list to see if * we can find a match for this. */ ns = newserver; for (os = servlist; os != NULL; os = os->next) if (os->type == ns->type && os->interface_index == inter && os->address.sin_port == ns->address.sin_port) break; /* * If we haven't got a structure for this, create one and * link it to the list. Otherwise, free up the details. */ if (os == NULL) { os = (struct server *)emalloc(sizeof(struct server)); bzero((char *)os, sizeof(struct server)); os->type = ns->type; os->interface_index = inter; os->address.sin_port = ns->address.sin_port; os->next = servlist; servlist = os; } else { if ((os->flags & SERVE_STATS) && os->statcount > 0) printstats(os); if (os->address.sin_addr.s_addr != interlist[inter].address.s_addr) { if (os->fd != 0) { closesocket(os->fd); os->fd = 0; } os->child_pid = 0; } (void) free(os->name); if (os->user != NULL) (void) free(os->user); #ifdef LIBWRAP if (os->libwrap != NULL) (void) free(os->libwrap); #endif os->internal_handler = NULL; if (os->external_handler != NULL) { free_external(os->external_handler); os->external_handler = NULL; } os->statcount = 0; if (os->len_reslist > 0 && os->reslist != NULL && (os->flags & SERVE_FILTERED) == 0) { (void) free((char *)os->reslist); os->len_reslist = 0; } } /* * By the time we get here we have an entry linked into * the list which has barely been initialized. Copy stuff * from the new entry into this one. */ os->name = strsave(ns->name); os->flags = ns->flags; if (ns->user != NULL) os->user = strsave(ns->user); #ifdef LIBWRAP if (ns->libwrap != NULL) os->libwrap = strsave(ns->libwrap); #endif if (ns->external_handler != NULL) { os->external_handler = copy_external(ns->external_handler); } else { os->internal_handler = ns->internal_handler; } os->laststattime = ns->laststattime; os->statinterval = ns->statinterval; if (ns->len_reslist > 0) { if (ns->flags & SERVE_FILTERED) os->reslist = ns->reslist; else { os->reslist = (struct restrict *)emalloc( ns->len_reslist * sizeof(struct restrict)); bcopy((char *)ns->reslist, (char *)os->reslist, ns->len_reslist * sizeof(struct restrict)); } os->len_reslist = ns->len_reslist; } if (ns->active.max > os->active.num && ns->active.max != os->active.pidc) { ns->active.pidv = (PID_TYPE *)emalloc(ns->active.max * sizeof(PID_TYPE)); bzero((char *)ns->active.pidv, ns->active.max * sizeof(PID_TYPE)); ns->active.pidc = ns->active.max; if (os->active.pidv != NULL) { bcopy((char *)os->active.pidv, (char *)ns->active.pidv, os->active.num * sizeof(PID_TYPE)); (void) free(os->active.pidv); } os->active.pidc = ns->active.pidc; os->active.pidv = ns->active.pidv; } os->active.max = ns->active.max; /* * If stats printing is on, count this in */ if (os->flags & SERVE_STATS) numlogging++; /* * Finally, worry about the socket for I/O. If it isn't * open already, try to do so now. If we can't get it * open, mark for retry. */ if (os->fd == 0) { os->address.sin_family = AF_INET; if (!(interlist[inter].flags & INT_INCOMPLETE)) { os->address.sin_addr = interlist[inter].address; os->fd = opensocket(os->type, &os->address, os->maxbacklog); } if (os->fd == 0) { os->flags |= SERVE_DOWN; numserverdown++; } } } /* * rmservice - free all resources associated with this service entry */ void rmservice(svr) struct server *svr; { void closesocket(); void free_external(); void printstats(); if ((svr->flags & SERVE_STATS) && svr->statcount > 0) printstats(svr); (void) free(svr->name); if (svr->user != NULL) (void) free(svr->user); #ifdef LIBWRAP if (svr->libwrap != NULL) (void) free(svr->libwrap); #endif if (svr->external_handler != NULL) free_external(svr->external_handler); if (svr->len_reslist > 0 && svr->reslist != NULL && (svr->flags & SERVE_FILTERED) == 0) /* free up non-filter reslists only. */ (void) free((char *)svr->reslist); if (svr->fd != 0) closesocket(svr->fd); (void) free((char *)svr); } /* * opensocket - open a socket and add it to the set we will * select on. */ int opensocket(type, addr, tcpbacklog) int type, tcpbacklog; struct sockaddr_in *addr; { int sock_type; int fd; int on; if (type == T_TCP) sock_type = SOCK_STREAM; else if (type == T_UDP) sock_type = SOCK_DGRAM; else { syslog(LOG_ERR, "internal error: socket type %d unknown!", type); exit(1); /*NOTREACHED*/ } fd = socket(AF_INET, sock_type, 0); if (fd < 0) { syslog(LOG_ERR, "can't open socket type %s: %m", ((type == T_TCP) ? "stream" : "datagram")); return 0; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m"); (void) close(fd); return 0; } if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) { syslog(LOG_ERR, "%d/%s: bind: %m", htons(addr->sin_port), ((type == T_TCP) ? "tcp" : "udp")); (void) close(fd); return 0; } if (type == T_TCP) (void) listen(fd, tcpbacklog ? tcpbacklog : backlog); FD_SET(fd, &allsockets); nsockets++; if (fd > maxsockets) maxsockets = fd; return fd; } /* * closesocket - close a socket and remove its bit from the select mask */ void closesocket(fd) int fd; { register int n; (void) close(fd); FD_CLR(fd, &allsockets); nsockets--; if (fd == maxsockets) { for (n = maxsockets - 1; n > 0; n--) if (FD_ISSET(n, &allsockets)) break; if (n == 0 && nsockets > 0) { syslog(LOG_ERR, "internal error: nsockets %d, no mask bits set", nsockets); exit(1); } maxsockets = n; } } #endif /* HOST_OKAY */ /* * getrestrict - read a restriction file and compile a restrict list */ int getrestrict(file, list, maxentry, numentries) char *file; struct restrict *list; int maxentry, numentries; { FILE *fp; int num, getrestrictlist(); /* * Open the file if we can. */ fp = fopen(file, "r"); if (fp == NULL) { register struct restrict *lp; syslog(LOG_ERR, "can't open restrict file %s: %m", file); if (numentries > 0) return(-1); /* * If we can't open, disallow any access -- a restrict * list was specified but it's not available. */ lp = list; lp->address = DEFMASK; lp->mask = DEFMASK; lp->allow = 0; return(1); } num = getrestrictlist(fp, list, maxentry, numentries, "restrict", file); (void) fclose(fp); return(num); } int getrestrictlist(fp, list, maxentry, numentries, idtyp, ident) FILE *fp; struct restrict *list; int maxentry, numentries; char *idtyp, *ident; { register struct restrict *lp; register struct restrict *lplast; register char *tok; register int key; char buf[1024]; char *bufp, *arg1, *arg2; u_long netnum; u_long netmask; char *getline(); char *gettoken(); int getkeyword(); int getnetnum(); int decodenetnum(); lp = list; if (numentries > 0) lplast = lp + numentries; else { /* * Add a default entry first. We assume initially that * the default is to allow hosts. */ lp->address = DEFMASK; lp->mask = DEFMASK; lp->allow = 1; lplast = lp + 1; } /* * Read the file line-by-line until we hit the end. */ while ((bufp = getline(fp, buf, sizeof buf)) != NULL) { key = getkeyword(restrict_keywords, &bufp, &arg1, &arg2); if (key == KEY_ERROR || key == KEY_NONE) continue; if (key == END_DECL) break; /* * Include restrictions from another place. If there's * a problem then try to leave a trail of (file)names. */ if (key == RS_INCLUDE || key == FL_INSERT) { int len_res; if (key == FL_INSERT) len_res = copyfilter(arg1, list, maxentry, lplast - list); else len_res = getrestrict(arg1, list, maxentry, lplast - list); if (len_res < 0) { syslog(LOG_ERR, "%s %s: problem with %s %s", idtyp, ident, (key == FL_INSERT) ? "include file" : "filter", arg1); } else lplast = list + len_res; if (lplast < list + maxentry) continue; syslog(LOG_ERR, "%s %s: too many entries (> %d)", idtyp, ident, maxentry); break; } if (!getnetnum(arg1, &netnum)) { syslog(LOG_ERR, "%s %s: can't find host %s", idtyp, ident, arg1); continue; } netnum = ntohl(netnum); netmask = HOSTMASK; tok = gettoken(&bufp); if (tok != NULL) { if (!STREQ(tok, "mask")) { syslog(LOG_ERR, "%s %s: inappropriate keyword %s", idtyp, ident, tok); continue; } tok = gettoken(&bufp); if (tok == NULL) { syslog(LOG_ERR, "%s %s: no mask following keywords", idtyp, ident); continue; } if (!decodenetnum(tok, &netmask)) { syslog(LOG_ERR, "%s %s: invalid net mask %s", idtyp, ident, tok); continue; } netmask = ntohl(netmask); } netnum &= netmask; /* * At this point we have a valid number and mask in * host byte order. Sort it into the existing array * if we have room. */ if (lplast == list + maxentry) { syslog(LOG_ERR, "%s %s: too many entries (> %d)", idtyp, ident, maxentry); break; } for (lp = list + 1; lp < lplast; lp++) if (netnum < lp->address || (netnum == lp->address && netmask < lp->mask)) break; if (lp < lplast) { register struct restrict *rp; for (rp = lplast; rp > lp; rp--) *rp = *(rp-1); } lplast++; lp->address = netnum; lp->mask = netmask; if (key == RS_ALLOW) { lp->allow = 1; /* * so we have an allow entry. which implies * that this list is meant to restrict access. * so reset the default from 'allow' to 'deny'. */ list->allow = 0; } else { lp->allow = 0; } } /* * Finally, done. Return number of entries. */ return (lplast - list); } /* * setfilter - build a filter entry. */ setfilter(fp, name, list, maxentry) FILE *fp; char *name; struct restrict *list; int maxentry; { register struct filter *flp, *ofl, *nfl; struct filter flt; int len_res; /* load restrict list from the config file. */ len_res = getrestrictlist(fp, list, maxentry, 0, "filter", name); if (len_res < 0) return; nfl = (struct filter *)emalloc(sizeof(struct filter)); nfl->next = NULL; if (len_res > 0) { nfl->reslist = (struct restrict *)emalloc(len_res * sizeof(struct restrict)); bcopy((char *)list, (char *)nfl->reslist, len_res * sizeof(struct restrict)); } else nfl->reslist = NULL; nfl->len_reslist = len_res; nfl->name = emalloc(strlen(name)+1); strcpy(nfl->name, name); if (filtlist == NULL) { filtlist = nfl; /* the first entry */ return; } /* check for an existing filter with the same name */ for (flp = filtlist, ofl = NULL; flp != NULL; flp = flp->next) { if (STREQ(name, flp->name)) break; ofl = flp; } if (ofl != NULL) ofl->next = nfl; nfl->next = flp; if (flp != NULL) { /* found an existing filter. */ nfl->next = flp->next; /* preserve link. */ /* now delete the old one. */ if (flp->len_reslist > 0) (void) free((char *)flp->reslist); (void) free(flp->name); (void) free((char *)flp); } } /* * findfilter - locate filter by name. */ struct filter * findfilter(name) char *name; { struct filter *flp; for (flp = filtlist; flp != NULL; flp = flp->next) if (STREQ(name, flp->name)) break; return(flp); } /* * copyfilter - copy filter into reslist at the current point. */ copyfilter(name, list, maxentry, numentries) char *name; struct restrict *list; int maxentry, numentries; { register struct restrict *lp, *lplast, *np, *rp; register struct filter *flp; int addentries; flp = findfilter(name); lp = list; if (flp == NULL) { syslog(LOG_ERR, "no such filter: '%s'", name); if (numentries > 0) return(-1); /* * If no filter, disallow any access -- a filter * was specified but it's not available. */ lp->address = DEFMASK; lp->mask = DEFMASK; lp->allow = 0; return(1); } /* * append the found filter onto the given list */ np = lp + numentries; addentries = flp->len_reslist; if (numentries + addentries > maxentry) { /* truncate if it's about to overflow. */ addentries = maxentry - numentries; /* * XXX there should be a warning here ... * XXX ... but getrestrictlist() already has one. */ } bcopy((char *)flp->reslist, (char *)np, addentries * sizeof(struct restrict)); lplast = np + addentries; /* * merge/sort the resulting list. * we assume the component lists were already sorted. */ while (np < lplast && lp < np) { if (np->address > lp->address) { lp++; continue; } if (np->address == lp->address) { if (np->mask > lp->mask) { lp++; continue; } if (np->mask == lp->mask) { /* a duplicate entry. pull up. */ for (rp = np; rp < lplast; rp++) *rp = *(rp+1); lplast--, addentries--, lp++; continue; } } /* save, push down and then insert. */ *lplast = *np; /* XXX */ for (rp = np; rp > lp; rp--) *rp = *(rp-1); *lp = *lplast; /* XXX */ lp++, np++; } /* Done. Return number of entries. */ return(numentries + addentries); } #ifdef LIBWRAP setwrap(fp) FILE *fp; { register int key; char buf[1024]; char *bufp, *arg1, *arg2; char *getline(); int getkeyword(); while ((bufp = getline(fp, buf, sizeof buf)) != NULL) { key = getkeyword(libwrap_keywords, &bufp, &arg1, &arg2); if (key == KEY_ERROR || key == KEY_NONE) continue; if (key == END_DECL) break; switch (key) { case TW_ALLOW: if (strlen(arg1) < sizeof(tw_allow)) { strcpy(tw_allow, arg1); hosts_allow_table = tw_allow; } break; case TW_DENY: if (strlen(arg1) < sizeof(tw_deny)) { strcpy(tw_deny, arg1); hosts_deny_table = tw_deny; } break; } } } #endif /* * okayhost - determine if a host is okay with the restriction list */ int okayhost(hostp, list, nres) struct sockaddr_in *hostp; struct restrict *list; int nres; { register struct restrict *lp; register struct restrict *lplast; register u_long netnum; register int allow; lp = list; if (lp == NULL || nres <= 0) return 1; lplast = lp + nres; netnum = ntohl(hostp->sin_addr.s_addr); allow = lp->allow; for (lp++; lp < lplast; lp++) { if ((netnum & lp->mask) == lp->address) allow = lp->allow; else if (netnum < lp->address) break; } return allow; } /* * Macros for classing characters */ #define ISWHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r') #define ISEOL(c) ((c) == '\n' || (c) == '#' || (c) == '\0') #define BSLASH ('\\') /* * getline - read a line from the configuration file */ char * getline(fp, linebuf, maxline) FILE *fp; char *linebuf; int maxline; { register char *cp, *bp, *bq; register int continued; char buf[1024]; cp = linebuf; *cp = '\0'; while (&linebuf[maxline] - cp > 0) { if (fgets(buf, sizeof(buf), fp) == NULL) { if (cp == linebuf) return NULL; return linebuf; } /* * Collect tokens */ bp = buf; for (;;) { /* * Space past white space at begining */ while (ISWHITE(*bp)) bp++; /* * If we've hit the end of a line, quit */ if (ISEOL(*bp)) { continued = 0; break; } /* * If we've found a backslash, determine whether * the line is continued. */ if (*bp == BSLASH) { bq = bp+1; while (ISWHITE(*bq)) bq++; if (ISEOL(*bq)) { continued = 1; break; } } /* * Got a token, pointed at by bp. Copy it into * the line buffer and append a space */ do { if (cp >= &linebuf[maxline]) { continued = 1; break; } *cp++ = *bp++; } while (!ISWHITE(*bp) && !ISEOL(*bp)); *cp++ = ' '; *cp = '\0'; } /* * Here we've ended the line. If it was continued, * or if we've collected nothing at all, go around * again. Otherwise, return what we have. */ if (continued == 0 && cp != linebuf) return linebuf; } /* * If we're here the line was too long. This is so * unlikely we don't even try to recover. Print an * error and pretend we hit the end of the file. */ linebuf[32] = '\0'; syslog(LOG_ERR, "processing of %s terminated, line too long: %s...", config_file, linebuf); return NULL; } /* * gettoken - return the next token on the line */ char * gettoken(line_nextc) char **line_nextc; { register char *cp, *tp; tp = *line_nextc; while (*tp == ' ') tp++; cp = tp; while (*cp != ' ' && *cp != '\0') cp++; if (cp == tp) { *line_nextc = tp; if (debug >= 3) (void) fprintf(stderr, "gettoken: returning NULL\n"); return NULL; } *cp++ = '\0'; *line_nextc = cp; if (debug >= 3) (void) fprintf(stderr, "gettoken: returning `%s'\n", tp); return tp; } /* * getkeyword - read the next token and return the keyword it * corresponds to */ int getkeyword(keylist, line_nextc, arg1, arg2) struct keyword *keylist; char **line_nextc; char **arg1; char **arg2; { register struct keyword *list; register char *cp; char *gettoken(); cp = gettoken(line_nextc); if (cp == NULL) return KEY_NONE; for (list = keylist; list->key != KEY_NONE; list++) if (STREQ(cp, list->word)) break; if (list->key == KEY_NONE) { syslog(LOG_ERR, "keyword %s unknown", cp); return KEY_ERROR; } if (list->numargs > 0) { *arg1 = gettoken(line_nextc); if (*arg1 == NULL) { syslog(LOG_ERR, "keyword %s requires an argument", cp); return KEY_ERROR; } } else { *arg1 = NULL; } if (list->numargs > 1) { *arg2 = gettoken(line_nextc); if (*arg2 == NULL) { syslog(LOG_ERR, "keyword %s requires a second argument", cp); return KEY_ERROR; } } else { *arg2 = NULL; } return list->key; } /* * getservice - return a service specification */ int getservice(str, name, proto, type, port) char *str; char **name; char **proto; int *type; u_short *port; { register char *s; register struct servent *sp; u_long p; int atouint(); /* * Services look like telnet/tcp or 123/udp. Find the * '/' and split it there. */ for (s = str; *s != '/' && *s != '\0'; s++) /* nothing */; if (*s != '/') return 0; *s++ = '\0'; /* * The second part should be either tcp or udp. Check * this. */ if (STREQ(s, "tcp")) *type = T_TCP; else if (STREQ(s, "udp")) *type = T_UDP; else goto nogood; /* * So far, so good. Next is the port number, which is * either an integer or a string to be given to * getservbyname(). Try the former first, if no * go try the latter. */ if (atouint(str, &p)) { if (p > 65535 || p == 0) goto nogood; *port = htons((u_short)p); } else { sp = getservbyname(str, s); if (sp == NULL) goto nogood; *port = (u_short)sp->s_port; } *name = str; *proto = s; return 1; nogood: *(--s) = '/'; return 0; } /* * find_internal - given a name, find an internal service */ struct internal * find_internal(name) char *name; { register struct internal *isp; for (isp = internal_servers; isp->service != NULL; isp++) { if (STREQ(name, isp->service)) return isp; } return NULL; } /* * get_external - read the tokens from the input and store the * data in an external structure. */ struct external * get_external(extern_pt, line_nextc) struct external *extern_pt; char **line_nextc; { register char *cp; register struct external *exp; register int i; char *gettoken(); cp = gettoken(line_nextc); if (cp == NULL) { syslog(LOG_ERR, "no arguments to `program' keyword"); return NULL; } exp = extern_pt; exp->executable = cp; for (i = 0; i < MAXARGV; i++) { cp = gettoken(line_nextc); if (cp == NULL) { exp->argv[i] = NULL; break; } exp->argv[i] = cp; } if (i == 0) { exp->argv[0] = exp->executable; exp->argv[1] = NULL; } else if (i == MAXARGV) { exp->argv[MAXARGV] = NULL; if (gettoken(line_nextc) != NULL) { syslog(LOG_ERR, "too many arguments for `program' keyword (> %d)", MAXARGV); return NULL; } } return exp; } /* * copy_external - copy an external specification into malloc'd space */ struct external * copy_external(extern_pt) struct external *extern_pt; { register struct external *old_exp; register struct external *new_exp; register int i; char *emalloc(); char *strsave(); old_exp = extern_pt; new_exp = (struct external *)emalloc(sizeof(struct external)); new_exp->executable = strsave(old_exp->executable); for (i = 0; i < MAXARGV && old_exp->argv[i] != NULL; i++) new_exp->argv[i] = strsave(old_exp->argv[i]); for (; i <= MAXARGV; i++) new_exp->argv[i] = NULL; return new_exp; } /* * free_external - free all memory associated with an external server */ void free_external(exp) struct external *exp; { register char **cpp; if (exp->executable != NULL) (void) free(exp->executable); cpp = exp->argv; while (*cpp != NULL) { (void) free(*cpp); cpp++; } (void) free(exp); } /* * getinterfaces - get the interface list for host */ void getinterfaces() { register struct ifreq *ifr; register int i; register int num; int vs; struct ifreq confbuf[MAXINTERFACES]; struct interface ibuf[MAXINTERFACES]; struct ifconf ifc; char *emalloc(); #if defined(__NetBSD__) || defined(__FreeBSD__) register int x; char *hosttoa(); #endif if ((vs = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "vs=socket(AF_INET, SOCK_DGRAM) %m"); exit(1); } ifc.ifc_len = sizeof(confbuf); ifc.ifc_buf = (char *)confbuf; if (ioctl(vs, SIOCGIFCONF, (char *)&ifc) < 0) { syslog(LOG_ERR, "get interface configuration: %m"); exit(1); } (void) close(vs); num = ifc.ifc_len/sizeof(struct ifreq); /* * Fill in the default interface */ bzero((char *)ibuf, sizeof(ibuf)); (void) strncpy(ibuf[0].name, "default", sizeof(ibuf[0].name)); ibuf[0].address.s_addr = INADDR_ANY; ibuf[0].flags = INT_DEFAULT; i = 0; /* * Go through the list looking for unique interface names. * Record all of them even if there isn't (yet) an IP * address associated with them. We assume there will * never be an interface named "default". We also make * illicit knowledge of the fact that the kernel groups * different addresses for the same interface together. */ #if defined(__NetBSD__) || defined(__FreeBSD__) for (x = 0; x < ifc.ifc_len; ) { ifr = (struct ifreq *)((caddr_t)ifc.ifc_req + x); x += sizeof(ifr->ifr_name) + (ifr->ifr_addr.sa_len > sizeof(struct sockaddr) ? ifr->ifr_addr.sa_len : sizeof(struct sockaddr)); #else for (ifr = ifc.ifc_req; num > 0; num--, ifr++) { #endif /* * Record the name if we haven't seen * this one before. */ if (!STRNEQ(ifr->ifr_name, ibuf[i].name, INTER_NAME_LEN)) { i++; strncpy(ibuf[i].name, ifr->ifr_name, INTER_NAME_LEN); ibuf[i].flags = INT_INCOMPLETE; } if (ifr->ifr_addr.sa_family == AF_INET) { if (!(ibuf[i].flags & INT_INCOMPLETE)) { /* * Second IP address for same interface? */ i++; strncpy(ibuf[i].name, ifr->ifr_name, INTER_NAME_LEN); } ibuf[i].address = ((struct sockaddr_in *) &(ifr->ifr_addr))->sin_addr; ibuf[i].flags &= ~INT_INCOMPLETE; if (debug) syslog(LOG_INFO, "intf %2d = {%s}{%s}", i, ibuf[i].name, hosttoa(&(ifr->ifr_addr))); } } /* * Got it. Allocate permanent space for this and copy the * list over. */ ninterfaces = i + 1; interlist = (struct interface *)emalloc( ninterfaces * sizeof(struct interface)); bcopy((char *)ibuf, (char *)interlist, ninterfaces * sizeof(struct interface)); } /* * Printable months */ char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /* * printstats - print request stats */ void printstats(svp) struct server *svp; { struct tm *tmp; char buf[128]; if (svp->statcount > 0) { tmp = localtime(&(svp->laststattime.tv_sec)); (void) sprintf(buf, "%d requests since %s %d %02d:%02d:%02d", svp->statcount, months[tmp->tm_mon], tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); syslog(LOG_INFO, "%s/%s on %s: %s", svp->name, ((svp->type == T_TCP) ? "tcp" : "udp"), interlist[svp->interface_index].name, buf); svp->statcount = 0; } } /* * emalloc - malloc with error checking */ char * emalloc(len) int len; { char *cp; extern char *malloc(); cp = malloc(len); if (cp == NULL) { syslog(LOG_ERR, "no memory! Aborting..."); exit(1); } return cp; } /* * strsave - save a string in permanent storage */ char * strsave(str) char *str; { register char *cp; register int len; if (str == NULL) return NULL; len = strlen(str) + 1; cp = emalloc(len); bcopy(str, cp, len); return cp; } /* * atouint - return an unsigned long int, with appropriate checking */ int atouint(str, uval) char *str; u_long *uval; { register u_long u; register char *cp; cp = str; if (*cp == '\0') return 0; u = 0; while (*cp != '\0') { if (!isdigit(*cp)) return 0; if (u > 429496729 || (u == 429496729 && *cp >= '6')) return 0; /* overflow */ u = (u << 3) + (u << 1); u += *cp++ - '0'; /* ascii dependent */ } *uval = u; return 1; } /* * getnetnum - given a host name, return its net number */ int getnetnum(host, num) char *host; u_long *num; { struct hostent *hp; int decodenetnum(); if (decodenetnum(host, num)) { return 1; } else if ((hp = gethostbyname(host)) != 0) { bcopy(hp->h_addr, (char *)num, sizeof(u_long)); return 1; } return 0; } /* * decodenetnum - return a net number (this is crude, but careful) */ int decodenetnum(num, netnum) char *num; u_long *netnum; { register char *cp; register char *bp; register int i; register int temp; register int eos; char buf[80]; /* will core dump on really stupid stuff */ cp = num; *netnum = 0; if (*cp == '[') { eos = ']'; cp++; } else { eos = '\0'; } for (i = 0; i < 4; i++) { bp = buf; while (isdigit(*cp)) *bp++ = *cp++; if (bp == buf) break; if (i < 3) { if (*cp++ != '.') break; } else if (*cp != eos) break; *bp = '\0'; temp = atoi(buf); if (temp > 255) break; *netnum <<= 8; *netnum += temp; } if (i < 4) return 0; *netnum = htonl(*netnum); return 1; } /* * hosttoa - turn a sockaddr_in into a printable host number */ char * hosttoa(addr) struct sockaddr_in *addr; { u_long netnum; static char numbuf[32]; netnum = (u_long)ntohl(addr->sin_addr.s_addr); (void) sprintf(numbuf, "%d.%d.%d.%d", ((netnum >> 24) & 0xff), ((netnum >> 16) & 0xff), ((netnum >> 8) & 0xff), netnum & 0xff); return numbuf; } #ifndef HOST_OKAY /* * setproctitle - rewrite the argument list so that ps can see * what we are doing. */ void setproctitle(svp, s) struct server *svp; int s; { int size; register char *cp; struct sockaddr_in sin; char buf[80]; cp = Argv[0]; size = sizeof(sin); if (getpeername(s, &sin, &size) == 0) (void) sprintf(buf, "-%s/tcp [%s]", svp->name, hosttoa(&sin)); else (void) sprintf(buf, "-%s/tcp", svp->name); strncpy(cp, buf, LastArg - cp); cp += strlen(cp); while (cp < LastArg) *cp++ = ' '; } #endif /* HOST_OKAY */ /* * Simple services provided internally */ /* * tcp_echo - echo data stream back */ tcp_echo(s, svp) int s; struct server *svp; { char buffer[BUFFERSIZE]; int i, n, done; setproctitle(svp, s); while ((i = read(s, buffer, sizeof(buffer))) > 0) { done = 0; do { n = write(s, &buffer[done], i - done); if (n <= 0) exit(0); done += n; } while (done < i); } exit(0); } /* * udp_echo - echo datagrams back */ udp_echo(s, svp) int s; struct server *svp; { char buffer[BUFFERSIZE]; int i, size; struct sockaddr sa; size = sizeof(sa); i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size); if (i < 0) return; (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); } /* * tcp_discard - read and discard incoming data */ tcp_discard(s, svp) int s; struct server *svp; { char buffer[BUFFERSIZE]; int i; setproctitle(svp, s); do { i = read(s, buffer, sizeof(buffer)); } while (i > 0); exit(0); } /* * udp_discard - read and discard an incoming datagram */ udp_discard(s, svp) int s; struct server *svp; { char buffer[64]; (void)recv(s, buffer, sizeof(buffer), 0); } /* * Unix time has a later epoch (Jan 1, 1970) than the TCP/UDP * time format (Jan 1, 1900). This is the conversion. */ #define JAN_1970 2208988800L /* 1970 - 1900 in seconds */ /* * tcp_time - return the time in machine readable format */ tcp_time(s, svp) int s; struct server *svp; { struct timeval tv; u_long toc; (void) gettimeofday(&tv, (struct timezone *)0); toc = htonl((u_long)tv.tv_sec + JAN_1970); (void) write(s, (char *) &toc, sizeof(toc)); } /* * udp_time - return the time in machine readable format */ udp_time(s, svp) int s; struct server *svp; { struct timeval tv; u_long toc; struct sockaddr sa; int size; size = sizeof(sa); if (recvfrom(s, (char *)&toc, sizeof(toc), 0, &sa, &size) < 0) return; (void) gettimeofday(&tv, (struct timezone *)0); toc = htonl((u_long)tv.tv_sec + JAN_1970); (void) sendto(s, (char *)&toc, sizeof(toc), 0, &sa, sizeof(sa)); } #ifdef no_ctime /* * Month and day-of-week strings useful for forming a date */ char *day_month[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; char *day_day[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; /* * day_makedate - make a nice looking date */ int day_makedate(tbuf) char *tbuf; { struct tm *tmp; char *tzp; struct timeval tv; struct timezone tz; extern char *timezone(); (void) gettimeofday(&tv, &tz); tmp = localtime(&tv.tv_sec); tzp = timezone(tz.tz_minuteswest, tmp->tm_isdst); (void) sprintf(tbuf, "%s, %s %d, %d %02d:%02d:%02d %s\r\n", day_day[tmp->tm_wday], day_month[tmp->tm_mon], tmp->tm_mday, tmp->tm_year + 1900, tmp->tm_hour, tmp->tm_min, tmp->tm_sec, tzp); return strlen(tbuf); } #else /* no_ctime */ int day_makedate(tbuf) char *tbuf; { time_t now; now = time(0); strcpy(tbuf, ctime(&now)); return strlen(tbuf); } #endif /* no_ctime */ /* * tcp_daytime - return a human readable time of day */ tcp_daytime(s, svp) int s; struct server *svp; { char buffer[100]; int i; i = day_makedate(buffer); (void) write(s, buffer, i); } /* * udp_daytime - return a human readable time of day */ udp_daytime(s, svp) int s; struct server *svp; { char buffer[100]; struct sockaddr sa; int size; size = sizeof(sa); if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) return; size = day_makedate(buffer); (void) sendto(s, buffer, size, 0, &sa, sizeof(sa)); } /* * Pattern data for chargen routines */ #define NPCHARS 95 #define NPERLINE 72 #define LINELEN (NPERLINE + 2) char cg_pattern[168] = { ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; /* * cg_fillbuf - fill a buffer with character data */ void cg_fillbuf(buffer, len) char *buffer; int len; { register char *bp; register int left; register int i; bp = buffer; left = len; for (i = 0; left >= LINELEN; left -= LINELEN, i = (i+1) % NPCHARS) { bcopy(&cg_pattern[i], bp, NPERLINE); bp += NPERLINE; *bp++ = '\r'; *bp++ = '\n'; } if (left > 0) { if (left == 1) { bp--; } else if (left > 2) { bcopy(&cg_pattern[i], bp, left-2); bp += left-2; } *bp++ = '\r'; *bp++ = '\n'; } } /* * tcp_chargen - print a pretty pattern */ tcp_chargen(s, svp) int s; struct server *svp; { char buffer[NPCHARS * LINELEN]; setproctitle(svp, s); cg_fillbuf(buffer, sizeof(buffer)); for (;;) { if (write(s, buffer, sizeof(buffer)/2) < sizeof(buffer)/2) break; if (write(s, &buffer[sizeof(buffer)/2], sizeof(buffer)/2) < sizeof(buffer)/2) break; } exit(0); } /* * udp_chargen - send back a random sized packet full of stuff */ udp_chargen(s, svp) int s; struct server *svp; { char buffer[512]; struct timeval tv; int len; struct sockaddr sa; int size; /* * Get origin of packet */ size = sizeof(sa); if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) return; /* * Use time to generate pseudo-random number */ (void) gettimeofday(&tv, (struct timezone *)0); len = (int)((tv.tv_sec ^ (tv.tv_usec >> 10)) % 509); if ((len % LINELEN) < 2) len += 3; cg_fillbuf(buffer, len); (void) sendto(s, buffer, len, 0, &sa, sizeof(sa)); } /* * tcp_dropconn - do absolutely nothing */ tcp_dropconn(s, svp) int s; struct server *svp; { /* nothing */ } /* * printservlist - print the server list, for debugging purposes */ void printservlist() { register struct server *svp; register int i; char *hosttoa(); if (servlist == 0) { (void) fprintf(stderr, "No servers in list\n"); return; } for (svp = servlist; svp != 0; svp = svp->next) { (void) fprintf(stderr, "%s/%s %s.%d %s ", svp->name, ((svp->type == T_TCP) ? "tcp" : "udp"), hosttoa(&svp->address), ntohs(svp->address.sin_port), interlist[svp->interface_index].name); (void) fprintf(stderr, "flags:"); if (svp->flags & SERVE_DELETE) (void) fprintf(stderr, " delete"); if (svp->flags & SERVE_LOG) (void) fprintf(stderr, " log"); if (svp->flags & SERVE_STATS) (void) fprintf(stderr, " stats"); if (svp->flags & SERVE_CONNECT) (void) fprintf(stderr, " connect"); if (svp->flags & SERVE_HAVE_CHILD) (void) fprintf(stderr, " have_child"); if (svp->flags & SERVE_DOWN) (void) fprintf(stderr, " down"); if (svp->flags & SERVE_MAXACTIVE) (void) fprintf(stderr, " maxactive"); if (svp->flags & SERVE_FILTERED) (void) fprintf(stderr, " filtered"); (void) fprintf(stderr, "\n"); if (svp->len_reslist > 0) { for (i = 0; i < svp->len_reslist; i++) { (void) fprintf(stderr, " %sallow %d.%d.%d.%d", (svp->reslist[i].allow ? "" : "dis"), ((svp->reslist[i].address >> 24) & 0xff), ((svp->reslist[i].address >> 16) & 0xff), ((svp->reslist[i].address >> 8) & 0xff), (svp->reslist[i].address & 0xff)); (void) fprintf(stderr, " mask %d.%d.%d.%d\n", ((svp->reslist[i].mask >> 24) & 0xff), ((svp->reslist[i].mask >> 16) & 0xff), ((svp->reslist[i].mask >> 8) & 0xff), (svp->reslist[i].mask & 0xff)); } } } } #ifdef HOST_OKAY static int len_res = 0; static struct restrict list_res[MAXRESTRICT]; initreslist(cf_resfile) char *cf_resfile; { len_res = getrestrict(cf_resfile, list_res, MAXRESTRICT, 0); return(len_res); } okay(peer_id) struct sockaddr_in peer_id; { return(okayhost(&peer_id, list_res, len_res)); } #endif /* HOST_OKAY */ @ 1.31 log @allow for various listen(2) backlogs. @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.30 2001/04/03 18:22:35 pkern Exp $"; d2245 3 a2247 2 /* push down and then insert. */ for (rp = lplast; rp > lp; rp--) d2249 2 a2250 1 np++; *lp = *np; lp++; @ 1.30 log @copyfilter() needs to sort/merge. @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.29 2001/03/29 20:22:28 pkern Exp $"; d153 1 d172 1 d346 1 d426 1 d467 1 a467 1 while ((c = getopt(argc, argv, "df:")) != EOF) d469 3 d483 1 a483 1 (void) fprintf(stderr, "usage: %s [-d] [-f config]\n", d1094 1 a1094 1 fd = opensocket(svp->type, &svp->address); d1377 10 d1778 1 a1778 1 os->fd = opensocket(os->type, &os->address); d1827 2 a1828 2 opensocket(type, addr) int type; d1868 1 a1868 1 (void) listen(fd, 10); @ 1.29 log @added "{ libwrap ... }" to replace the -A and -D options. @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.28 2001/03/29 18:40:59 pkern Exp $"; d2173 1 a2173 1 register struct restrict *lp, *lplast; d2195 4 a2198 1 lplast = lp + numentries; d2200 11 d2212 21 a2232 5 if (numentries + addentries > maxentry) addentries = maxentry - numentries; /* truncate */ bcopy((char *)flp->reslist, (char *)lplast, addentries * sizeof(struct restrict)); @ 1.28 log @added "filtering". filters are restriction lists which are defined in the config file (ie. instead of being listed in a separate restrict file). see conf.hints for sample usage. @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.27 2000/03/07 20:35:24 pkern Exp $"; d184 4 a187 1 #define DECL_FILTER 100 d203 3 d209 18 d463 1 a463 6 #ifdef LIBWRAP #define OPTS "df:A:D:" #else #define OPTS "df:" #endif while ((c = getopt(argc, argv, OPTS)) != EOF) a470 8 #ifdef LIBWRAP case 'A': hosts_allow_table = optarg; break; case 'D': hosts_deny_table = optarg; break; #endif d476 2 a477 7 (void) fprintf(stderr, #ifdef LIBWRAP "usage: %s [-d] [-f config] [-A allow-list] [-D deny-list]\n", #else "usage: %s [-d] [-f config]\n", #endif progname); a1214 4 #ifdef LIBWRAP syslog(LOG_INFO, "allow: %s", hosts_allow_table); syslog(LOG_INFO, "deny: %s", hosts_deny_table); #endif d1275 4 d1481 4 d2207 35 @ 1.27 log @netbsd and freebsd use same code for collecting interface names. @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.26 2000/02/29 17:57:11 pkern Exp $"; d152 1 d170 1 d182 5 d191 9 d333 1 d360 8 d846 1 a846 1 if (strcmp(exp->argv[i], "%A") == 0) d1176 2 d1183 1 d1199 1 d1229 5 d1256 1 a1256 1 cf_resfile = cf_interfaces = NULL; d1263 14 d1360 4 d1393 16 d1454 14 d1682 2 a1683 1 if (os->len_reslist > 0 && os->reslist != NULL) { d1710 8 a1717 4 os->reslist = (struct restrict *)emalloc( ns->len_reslist * sizeof(struct restrict)); bcopy((char *)ns->reslist, (char *)os->reslist, ns->len_reslist * sizeof(struct restrict)); d1785 3 a1787 1 if (svr->len_reslist > 0 && svr->reslist != NULL) a1890 6 register struct restrict *lp; register struct restrict *lplast; register char *tok; register int key; char buf[1024]; char *bufp, *arg1, *arg2; d1892 1 a1892 7 u_long netnum; u_long netmask; char *getline(); char *gettoken(); int getkeyword(); int getnetnum(); int decodenetnum(); d1899 2 d1916 29 d1967 3 d1971 2 a1972 2 * Include restrictions from another file. If there's * a problem then try to leave a trail of filenames. d1974 1 a1974 1 if (key == RS_INCLUDE) { d1977 4 a1980 1 len_res = getrestrict(arg1, list, maxentry, lplast - list); d1983 4 a1986 2 "restrict file %s: problem with include file %s", file, arg1); d1995 2 a1996 2 "restrict file %s: too many entries (> %d)", file, maxentry); d2001 2 a2002 2 syslog(LOG_ERR, "restrict file %s: can't find host %s", file, arg1); d2012 2 a2013 2 "restrict file %s: inappropriate keyword %s", file, tok); d2020 2 a2021 2 "restrict file %s: no mask following keywords", file); d2027 2 a2028 2 "restrict file %s: invalid net mask %s", file, tok); d2042 2 a2043 2 "restrict file %s: too many entries (> %d)", file, maxentry); d2064 5 a2074 2 (void) fclose(fp); d2081 117 d3289 4 @ 1.26 log @mod for aix 4.3 @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.25 1999/10/28 14:28:19 pkern Exp $"; d2370 1 a2370 1 #ifdef __NetBSD__ d2406 1 a2406 1 #ifdef __NetBSD__ @ 1.25 log @linux workaround? more logging info. @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.24 1999/10/08 22:45:43 pkern Exp $"; d46 3 @ 1.24 log @linux tweak? @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.23 1999/10/08 21:23:25 pkern Exp $"; d615 2 a616 1 syslog(LOG_WARNING, "recvfrom(MSG_PEEK): %m"); d618 1 @ 1.23 log @hmmm, a missing "if (debug)" @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.22 1999/09/01 14:58:09 pkern Exp $"; d611 4 d2435 1 a2435 1 i, ibuf[i].name, hosttoa(ifr->ifr_addr)); @ 1.22 log @if "max 0" then log "denied access" instead of "exceeds limit" @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.21 1999/08/18 18:00:43 pkern Exp $"; d602 3 a604 1 (void) fprintf(stderr, "someone wants %s/udp\n", svp->name); @ 1.21 log @mods for NetBSD. @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.20 1999/08/13 20:57:56 pkern Exp $"; d653 6 a658 2 if (svp->flags & SERVE_LOG) log_reason("exceeds limit"); @ 1.20 log @AIX 3.2.5 mods (just for the record). @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.19 1999/08/13 18:05:47 pkern Exp $"; d2355 4 d2391 8 d2400 1 d2423 4 @ 1.19 log @added "include" feature to restrict lists. usage: include filename @ text @d17 4 a20 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.18 1999/08/11 22:34:10 pkern Exp $"; d44 4 d315 3 d325 4 d453 7 d937 3 d941 1 d959 8 d972 1 @ 1.18 log @do wrapper processing after fork @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.17 1999/08/09 23:09:38 pkern Exp $"; d168 1 d173 1 d1287 1 a1287 1 MAXRESTRICT); d1748 1 a1748 1 getrestrict(file, list, maxentry) d1751 1 a1751 1 int maxentry; d1774 2 a1787 4 /* * Add a default entry first. We assume initially that the default * is to allow hosts. */ d1789 12 a1800 4 lp->address = DEFMASK; lp->mask = DEFMASK; lp->allow = 1; lplast = lp + 1; d1810 25 d3016 1 a3016 1 len_res = getrestrict(cf_resfile, list_res, MAXRESTRICT); @ 1.17 log @added SERVE_MAXACTIVE. @ text @d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/SRC/RCS/ninetd.c,v 1.16 1999/08/09 22:27:41 pkern Exp $"; d397 1 a397 1 #else /* LIBWRAP */ d399 1 a399 1 #endif /* LIBWRAP */ d535 12 d604 1 a604 1 #define log_reason(a) \ d608 7 a614 1 (a), interlist[svp->interface_index].name) a627 20 #ifdef LIBWRAP if (svp->libwrap != NULL) { struct hostent *hp = gethostbyaddr((char *)&peerid.sin_addr, sizeof(struct in_addr), AF_INET); char *peerid_str = hosttoa(&peerid); if (hosts_ctl(svp->libwrap, (hp) ? hp->h_name : STRING_UNKNOWN, peerid_str, (svp->user) ? svp->user : STRING_UNKNOWN) == 0) { if (svp->flags & SERVE_LOG) log_reason("denied access"); if (svp->type == T_TCP) (void) close(svc_fd); else eatpacket(svc_fd); return; } } #endif /* LIBWRAP */ d631 1 a631 1 log_reason("limit reached"); d641 9 a649 4 syslog(LOG_INFO, "%s/%s: %s.%d on %s", svp->name, ((svp->type == T_TCP) ? "tcp" : "udp"), hosttoa(&peerid), ntohs(peerid.sin_port), interlist[svp->interface_index].name); d692 18 d948 1 d960 1 a960 1 i = -1; d963 1 a963 1 if (i < 0) d966 1 a966 1 if (i < 0) d1250 1 a1250 1 #endif /* LIBWRAP */ @ 1.16 log @added "max" keyword -- set a ceiling for number of active servers. @ text @d17 1 a17 1 static char rcsid[] = "$Header: /c/src/local.sbin/ninetd/RCS/ninetd.c,v 1.15 98/12/18 20:33:44 pkern Exp $"; d304 1 d630 1 a630 1 if (svp->active.max > 0) { d1236 1 @ 1.15 log @get hostname. @ text @d14 1 d17 1 a17 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/RCS/ninetd.c,v 1.14 1998/12/18 19:58:55 pkern Exp $"; d141 1 d158 1 d251 13 d287 1 a287 1 int child_pid; /* single-threaded server */ d291 1 d338 1 d513 1 a513 1 int pid; d590 7 d600 1 a600 6 syslog(LOG_ERR, "%s/%s: %s.%d denied access on %s", svp->name, ((svp->type == T_TCP) ? "tcp" : "udp"), hosttoa(&peerid), ntohs(peerid.sin_port), interlist[svp->interface_index].name); d619 1 a619 6 syslog(LOG_ERR, "%s/%s: %s.%d denied access on %s", svp->name, ((svp->type == T_TCP) ? "tcp" : "udp"), peerid_str, ntohs(peerid.sin_port), interlist[svp->interface_index].name); d629 12 d678 5 d893 1 a893 1 int pid; d922 1 d925 21 d1228 9 d1558 15 @ 1.14 log @libwrap mods. @ text @d16 1 a16 1 static char rcsid[] = "$Header: /local/src/local.sbin/ninetd/RCS/ninetd.c,v 1.13 1997/07/07 15:26:37 pkern Exp $"; d591 1 d594 3 a596 1 if (hosts_ctl(svp->libwrap, STRING_UNKNOWN, peerid_str, @ 1.13 log @added mods defined by HOST_OKAY. for use is just want ninetd-style restrict lists. @ text @d16 1 a16 1 static char rcsid[] = "$Header: /nfs/@@ugw/c/src/local.sbin/ninetd/RCS/ninetd.c,v 1.12 97/07/07 15:15:17 pkern Exp $"; d41 6 d137 3 d153 3 d272 3 d376 6 a381 1 while ((c = getopt(argc, argv, "df:")) != EOF) d389 8 d403 6 a408 1 "usage: %s [-d] [-f config]\n", progname); d589 22 d1049 4 d1165 6 d1449 4 d1474 4 d1536 4 @ 1.12 log @comments. @ text @d16 1 a16 1 static char rcsid[] = "$Header: /c/src/local.sbin/ninetd/RCS/ninetd.c,v 1.11 96/02/09 13:49:14 pkern Exp $"; d327 2 d1556 1 d2367 2 d2396 1 d2791 22 @ 1.11 log @change restrict default. @ text @d16 1 a16 1 static char rcsid[] = "$Header: /nfs/@@ugw/c/src/local.sbin/ninetd/RCS/ninetd.c,v 1.10 95/05/04 11:17:23 pkern Exp $"; d1588 2 a1589 2 * If we can't open, disallow any access. * a restrict list was specified but it's not available. d1601 1 a1601 1 * is to disallow hosts. @ 1.10 log @hmmm, deal with minor complaints. @ text @d16 1 a16 1 static char rcsid[] = "$Header: /nfs/@@ugw/c/src/local.sbin/ninetd/RCS/ninetd.c,v 1.9 95/05/04 11:09:32 pkern Exp $"; d1582 1 a1582 1 * Open the file if we can d1587 10 a1596 1 return 0; d1601 1 a1601 1 * is to allow hosts. @ 1.9 log @close after reading restrict file. @ text @d16 1 a16 1 static rcsid[] = "$Header: /nfs/@@ugw/c/src/local.sbin/ninetd/RCS/ninetd.c,v 1.8 94/11/17 09:56:03 pkern Exp $"; d769 3 a771 3 int (*alarm_hndlr)(); int (*hup_hndlr)(); int (*child_hndlr)(); @ 1.8 log @added CONFIG. @ text @d16 1 a16 1 static rcsid[] = "$Header: /nfs/ugw/c/src/local.etc/ninetd/RCS/ninetd.c,v 1.7 93/05/28 17:51:07 pkern Exp $"; d1677 2 @ 1.7 log @added rcsid[] @ text @d16 1 a16 1 static rcsid[] = "$Header$"; d311 4 a314 1 char *config_file = "/etc/ninetd.conf"; @ 1.6 log @use ctime() in day_makedate() @ text @d15 3 @ 1.5 log @add SunOS 3.5 compat code (but not enabled) @ text @d2503 1 d2540 12 @ 1.4 log @log each reconfiguration. @ text @d205 3 d654 13 @ 1.3 log @added credits. @ text @d969 1 @ 1.2 log @#ifdef'd openlog() call to work on Ultrix too, fixed a bug with UDP service handling that would cause ninetd to wind up just sitting there, and fixed a minor lint complaint by making configure() a void routine. @ text @d6 8 @ 1.1 log @Initial revision @ text @d327 1 a327 1 int configure(); d369 1 d371 4 d387 1 a387 1 (void) configure(); d798 1 d928 1 a928 1 int d967 1 a967 1 return 0; @