inetd.c revision 56482
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1983, 1991, 1993, 1994
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes * Redistribution and use in source and binary forms, with or without
61553Srgrimes * modification, are permitted provided that the following conditions
71553Srgrimes * are met:
81553Srgrimes * 1. Redistributions of source code must retain the above copyright
91553Srgrimes *    notice, this list of conditions and the following disclaimer.
101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111553Srgrimes *    notice, this list of conditions and the following disclaimer in the
121553Srgrimes *    documentation and/or other materials provided with the distribution.
131553Srgrimes * 3. All advertising materials mentioning features or use of this software
141553Srgrimes *    must display the following acknowledgement:
151553Srgrimes *	This product includes software developed by the University of
161553Srgrimes *	California, Berkeley and its contributors.
171553Srgrimes * 4. Neither the name of the University nor the names of its contributors
181553Srgrimes *    may be used to endorse or promote products derived from this software
191553Srgrimes *    without specific prior written permission.
201553Srgrimes *
211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311553Srgrimes * SUCH DAMAGE.
321553Srgrimes */
331553Srgrimes
341553Srgrimes#ifndef lint
3529602Scharnierstatic const char copyright[] =
361553Srgrimes"@(#) Copyright (c) 1983, 1991, 1993, 1994\n\
371553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381553Srgrimes#endif /* not lint */
391553Srgrimes
401553Srgrimes#ifndef lint
4129602Scharnier#if 0
4229602Scharnierstatic char sccsid[] = "@(#)from: inetd.c	8.4 (Berkeley) 4/13/94";
4329602Scharnier#endif
4429602Scharnierstatic const char rcsid[] =
4550479Speter  "$FreeBSD: head/usr.sbin/inetd/inetd.c 56482 2000-01-23 20:17:41Z charnier $";
461553Srgrimes#endif /* not lint */
471553Srgrimes
481553Srgrimes/*
491553Srgrimes * Inetd - Internet super-server
501553Srgrimes *
511553Srgrimes * This program invokes all internet services as needed.  Connection-oriented
521553Srgrimes * services are invoked each time a connection is made, by creating a process.
531553Srgrimes * This process is passed the connection as file descriptor 0 and is expected
541553Srgrimes * to do a getpeername to find out the source host and port.
551553Srgrimes *
561553Srgrimes * Datagram oriented services are invoked when a datagram
571553Srgrimes * arrives; a process is created and passed a pending message
581553Srgrimes * on file descriptor 0.  Datagram servers may either connect
591553Srgrimes * to their peer, freeing up the original socket for inetd
601553Srgrimes * to receive further messages on, or ``take over the socket'',
611553Srgrimes * processing all arriving datagrams and, eventually, timing
621553Srgrimes * out.	 The first type of server is said to be ``multi-threaded'';
638857Srgrimes * the second type of server ``single-threaded''.
641553Srgrimes *
651553Srgrimes * Inetd uses a configuration file which is read at startup
661553Srgrimes * and, possibly, at some later time in response to a hangup signal.
671553Srgrimes * The configuration file is ``free format'' with fields given in the
681553Srgrimes * order shown below.  Continuation lines for an entry must being with
691553Srgrimes * a space or tab.  All fields must be present in each entry.
701553Srgrimes *
711553Srgrimes *	service name			must be in /etc/services or must
721553Srgrimes *					name a tcpmux service
731553Srgrimes *	socket type			stream/dgram/raw/rdm/seqpacket
741553Srgrimes *	protocol			must be in /etc/protocols
751553Srgrimes *	wait/nowait			single-threaded/multi-threaded
761553Srgrimes *	user				user to run daemon as
771553Srgrimes *	server program			full path name
781553Srgrimes *	server program arguments	maximum of MAXARGS (20)
791553Srgrimes *
801553Srgrimes * TCP services without official port numbers are handled with the
811553Srgrimes * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
821553Srgrimes * requests. When a connection is made from a foreign host, the service
831553Srgrimes * requested is passed to tcpmux, which looks it up in the servtab list
841553Srgrimes * and returns the proper entry for the service. Tcpmux returns a
851553Srgrimes * negative reply if the service doesn't exist, otherwise the invoked
861553Srgrimes * server is expected to return the positive reply if the service type in
871553Srgrimes * inetd.conf file has the prefix "tcpmux/". If the service type has the
881553Srgrimes * prefix "tcpmux/+", tcpmux will return the positive reply for the
891553Srgrimes * process; this is for compatibility with older server code, and also
901553Srgrimes * allows you to invoke programs that use stdin/stdout without putting any
911553Srgrimes * special server code in them. Services that use tcpmux are "nowait"
921553Srgrimes * because they do not have a well-known port and hence cannot listen
931553Srgrimes * for new requests.
941553Srgrimes *
952657Scsgr * For RPC services
962657Scsgr *	service name/version		must be in /etc/rpc
972657Scsgr *	socket type			stream/dgram/raw/rdm/seqpacket
982657Scsgr *	protocol			must be in /etc/protocols
992657Scsgr *	wait/nowait			single-threaded/multi-threaded
1002657Scsgr *	user				user to run daemon as
1012657Scsgr *	server program			full path name
1022657Scsgr *	server program arguments	maximum of MAXARGS
1032657Scsgr *
1041553Srgrimes * Comment lines are indicated by a `#' in column 1.
1051553Srgrimes */
1061553Srgrimes#include <sys/param.h>
1071553Srgrimes#include <sys/ioctl.h>
1081553Srgrimes#include <sys/wait.h>
1091553Srgrimes#include <sys/time.h>
1101553Srgrimes#include <sys/resource.h>
1111553Srgrimes
1121553Srgrimes#include <netinet/in.h>
11336042Sguido#include <netinet/tcp.h>
1141553Srgrimes#include <arpa/inet.h>
1152657Scsgr#include <rpc/rpc.h>
11619617Sjulian#include <rpc/pmap_clnt.h>
1171553Srgrimes
1181553Srgrimes#include <errno.h>
11929602Scharnier#include <err.h>
1201553Srgrimes#include <fcntl.h>
12130807Sache#include <grp.h>
1221553Srgrimes#include <netdb.h>
1231553Srgrimes#include <pwd.h>
1241553Srgrimes#include <signal.h>
1251553Srgrimes#include <stdio.h>
1261553Srgrimes#include <stdlib.h>
1271553Srgrimes#include <string.h>
1281553Srgrimes#include <syslog.h>
12948279Ssheldonh#include <tcpd.h>
1301553Srgrimes#include <unistd.h>
13113142Speter#include <libutil.h>
13219617Sjulian#include <sysexits.h>
1331553Srgrimes
13448981Ssheldonh#include "inetd.h"
13548981Ssheldonh#include "pathnames.h"
13648981Ssheldonh
13745089Smarkm#ifndef LIBWRAP_ALLOW_FACILITY
13845089Smarkm# define LIBWRAP_ALLOW_FACILITY LOG_AUTH
13945089Smarkm#endif
14045089Smarkm#ifndef LIBWRAP_ALLOW_SEVERITY
14145089Smarkm# define LIBWRAP_ALLOW_SEVERITY LOG_INFO
14245089Smarkm#endif
14345089Smarkm#ifndef LIBWRAP_DENY_FACILITY
14445089Smarkm# define LIBWRAP_DENY_FACILITY LOG_AUTH
14545089Smarkm#endif
14645089Smarkm#ifndef LIBWRAP_DENY_SEVERITY
14745089Smarkm# define LIBWRAP_DENY_SEVERITY LOG_WARNING
14845089Smarkm#endif
14945089Smarkm
15048382Ssheldonh#define ISWRAP(sep)	\
15148697Ssheldonh	   ( ((wrap_ex && !(sep)->se_bi) || (wrap_bi && (sep)->se_bi)) \
15248382Ssheldonh	&& ( ((sep)->se_accept && (sep)->se_socktype == SOCK_STREAM) \
15348382Ssheldonh	    || (sep)->se_socktype == SOCK_DGRAM))
15448382Ssheldonh
15521640Speter#ifdef LOGIN_CAP
15621640Speter#include <login_cap.h>
15730792Sache
15830792Sache/* see init.c */
15930792Sache#define RESOURCE_RC "daemon"
16030792Sache
16121640Speter#endif
16221640Speter
16333794Spst#ifndef	MAXCHILD
16433794Spst#define	MAXCHILD	-1		/* maximum number of this service
16533794Spst					   < 0 = no limit */
16633794Spst#endif
16733794Spst
16833794Spst#ifndef	MAXCPM
16933794Spst#define	MAXCPM		-1		/* rate limit invocations from a
17033794Spst					   single remote address,
17133794Spst					   < 0 = no limit */
17233794Spst#endif
17333794Spst
1742659Scsgr#define	TOOMANY		256		/* don't start more than TOOMANY */
1751553Srgrimes#define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
1761553Srgrimes#define	RETRYTIME	(60*10)		/* retry after bind or server fail */
17719618Sjulian#define MAX_MAXCHLD	32767		/* max allowable max children */
1781553Srgrimes
1791553Srgrimes#define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
1801553Srgrimes
18148279Ssheldonhint	allow_severity;
18248279Ssheldonhint	deny_severity;
18348697Ssheldonhint	wrap_ex = 0;
18448279Ssheldonhint	wrap_bi = 0;
1851553Srgrimesint	debug = 0;
1862659Scsgrint	log = 0;
18748988Ssheldonhint	maxsock;			/* highest-numbered descriptor */
1881553Srgrimesfd_set	allsock;
1891553Srgrimesint	options;
1901553Srgrimesint	timingout;
1911553Srgrimesint	toomany = TOOMANY;
19248069Ssheldonhint	maxchild = MAXCHILD;
19348069Ssheldonhint	maxcpm = MAXCPM;
1941553Srgrimesstruct	servent *sp;
1952657Scsgrstruct	rpcent *rpc;
19617482Sjulianstruct	in_addr bind_address;
19742122Sdesint	signalpipe[2];
19848991Ssheldonh#ifdef SANITY_CHECK
19948991Ssheldonhint	nsock;
20048991Ssheldonh#endif
2011553Srgrimes
20248981Ssheldonhstruct	servtab *servtab;
2031553Srgrimes
20448981Ssheldonhextern struct biltin biltins[];
2051553Srgrimes
2061553Srgrimes#define NUMINT	(sizeof(intab) / sizeof(struct inent))
2071553Srgrimeschar	*CONFIG = _PATH_INETDCONF;
20817482Sjulianchar	*pid_file = _PATH_INETDPID;
20913142Speter
21013142Speter#ifdef OLD_SETPROCTITLE
2111553Srgrimeschar	**Argv;
2121553Srgrimeschar 	*LastArg;
21313142Speter#endif
2141553Srgrimes
2151553Srgrimesint
21633794Spstgetvalue(arg, value, whine)
21733794Spst	char *arg, *whine;
21833794Spst	int  *value;
21933794Spst{
22033794Spst	int  tmp;
22133794Spst	char *p;
22233794Spst
22333794Spst	tmp = strtol(arg, &p, 0);
22433794Spst	if (tmp < 1 || *p) {
22533794Spst		syslog(LOG_ERR, whine, arg);
22633794Spst		return 1;			/* failure */
22733794Spst	}
22833794Spst	*value = tmp;
22933794Spst	return 0;				/* success */
23033794Spst}
23133794Spst
23233794Spstint
2331553Srgrimesmain(argc, argv, envp)
2341553Srgrimes	int argc;
2351553Srgrimes	char *argv[], *envp[];
2361553Srgrimes{
2371553Srgrimes	struct servtab *sep;
2381553Srgrimes	struct passwd *pwd;
23930807Sache	struct group *grp;
24048962Ssheldonh	struct sigaction sa, saalrm, sachld, sahup, sapipe;
2411553Srgrimes	int tmpint, ch, dofork;
2421553Srgrimes	pid_t pid;
2431553Srgrimes	char buf[50];
24421640Speter#ifdef LOGIN_CAP
24521640Speter	login_cap_t *lc = NULL;
24621640Speter#endif
24745089Smarkm	struct request_info req;
24845089Smarkm	int denied;
24945089Smarkm	char *service = NULL;
25048382Ssheldonh	char *pnm;
25147972Ssheldonh	struct  sockaddr_in peer;
25247972Ssheldonh	int i;
2531553Srgrimes
25413142Speter
25513142Speter#ifdef OLD_SETPROCTITLE
2561553Srgrimes	Argv = argv;
2571553Srgrimes	if (envp == 0 || *envp == 0)
2581553Srgrimes		envp = argv;
2591553Srgrimes	while (*envp)
2601553Srgrimes		envp++;
2611553Srgrimes	LastArg = envp[-1] + strlen(envp[-1]);
26213142Speter#endif
2631553Srgrimes
2641553Srgrimes	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
2651553Srgrimes
26617482Sjulian	bind_address.s_addr = htonl(INADDR_ANY);
26748697Ssheldonh	while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:")) != -1)
2681553Srgrimes		switch(ch) {
2691553Srgrimes		case 'd':
2701553Srgrimes			debug = 1;
2711553Srgrimes			options |= SO_DEBUG;
2721553Srgrimes			break;
2732659Scsgr		case 'l':
2742659Scsgr			log = 1;
2752659Scsgr			break;
27633794Spst		case 'R':
27733794Spst			getvalue(optarg, &toomany,
27833794Spst				"-R %s: bad value for service invocation rate");
2791553Srgrimes			break;
28033794Spst		case 'c':
28133794Spst			getvalue(optarg, &maxchild,
28233794Spst				"-c %s: bad value for maximum children");
28333794Spst			break;
28433794Spst		case 'C':
28533794Spst			getvalue(optarg, &maxcpm,
28633794Spst				"-C %s: bad value for maximum children/minute");
28733794Spst			break;
28817482Sjulian		case 'a':
28917482Sjulian			if (!inet_aton(optarg, &bind_address)) {
29017482Sjulian				syslog(LOG_ERR,
29117482Sjulian			         "-a %s: invalid IP address", optarg);
29219617Sjulian				exit(EX_USAGE);
29317482Sjulian			}
29417482Sjulian			break;
29517482Sjulian		case 'p':
29617482Sjulian			pid_file = optarg;
29717482Sjulian			break;
29848279Ssheldonh		case 'w':
29948697Ssheldonh			wrap_ex++;
30048279Ssheldonh			break;
30148697Ssheldonh		case 'W':
30248697Ssheldonh			wrap_bi++;
30348697Ssheldonh			break;
3041553Srgrimes		case '?':
3051553Srgrimes		default:
3061553Srgrimes			syslog(LOG_ERR,
30748697Ssheldonh				"usage: inetd [-dlwW] [-a address] [-R rate]"
30833794Spst				" [-c maximum] [-C rate]"
30917482Sjulian				" [-p pidfile] [conf-file]");
31019617Sjulian			exit(EX_USAGE);
3111553Srgrimes		}
3121553Srgrimes	argc -= optind;
3131553Srgrimes	argv += optind;
3141553Srgrimes
3151553Srgrimes	if (argc > 0)
3161553Srgrimes		CONFIG = argv[0];
3171553Srgrimes	if (debug == 0) {
31811447Swollman		FILE *fp;
31912024Speter		if (daemon(0, 0) < 0) {
32012024Speter			syslog(LOG_WARNING, "daemon(0,0) failed: %m");
32112024Speter		}
32212024Speter		/*
32312024Speter		 * In case somebody has started inetd manually, we need to
32412024Speter		 * clear the logname, so that old servers run as root do not
32512024Speter		 * get the user's logname..
32612024Speter		 */
32712024Speter		if (setlogin("") < 0) {
32812024Speter			syslog(LOG_WARNING, "cannot clear logname: %m");
32912024Speter			/* no big deal if it fails.. */
33012024Speter		}
33111447Swollman		pid = getpid();
33217482Sjulian		fp = fopen(pid_file, "w");
33311447Swollman		if (fp) {
33411447Swollman			fprintf(fp, "%ld\n", (long)pid);
33511447Swollman			fclose(fp);
33611447Swollman		} else {
33717482Sjulian			syslog(LOG_WARNING, "%s: %m", pid_file);
33811447Swollman		}
3391553Srgrimes	}
34035948Sbde	sa.sa_flags = 0;
34135948Sbde	sigemptyset(&sa.sa_mask);
34235948Sbde	sigaddset(&sa.sa_mask, SIGALRM);
34335948Sbde	sigaddset(&sa.sa_mask, SIGCHLD);
34435948Sbde	sigaddset(&sa.sa_mask, SIGHUP);
34542122Sdes	sa.sa_handler = flag_retry;
34648962Ssheldonh	sigaction(SIGALRM, &sa, &saalrm);
34742122Sdes	config();
34842122Sdes	sa.sa_handler = flag_config;
34948962Ssheldonh	sigaction(SIGHUP, &sa, &sahup);
35042122Sdes	sa.sa_handler = flag_reapchild;
35148962Ssheldonh	sigaction(SIGCHLD, &sa, &sachld);
35235848Sguido	sa.sa_handler = SIG_IGN;
35335948Sbde	sigaction(SIGPIPE, &sa, &sapipe);
3541553Srgrimes
3551553Srgrimes	{
3561553Srgrimes		/* space for daemons to overwrite environment for ps */
3571553Srgrimes#define	DUMMYSIZE	100
3581553Srgrimes		char dummy[DUMMYSIZE];
3591553Srgrimes
36019298Salex		(void)memset(dummy, 'x', DUMMYSIZE - 1);
3611553Srgrimes		dummy[DUMMYSIZE - 1] = '\0';
3621553Srgrimes		(void)setenv("inetd_dummy", dummy, 1);
3631553Srgrimes	}
3641553Srgrimes
36542250Sdes	if (pipe(signalpipe) != 0) {
36652219Scharnier		syslog(LOG_ERR, "pipe: %m");
36742250Sdes		exit(EX_OSERR);
36842122Sdes	}
36942122Sdes	FD_SET(signalpipe[0], &allsock);
37048991Ssheldonh#ifdef SANITY_CHECK
37147015Sdes	nsock++;
37248991Ssheldonh#endif
37348989Ssheldonh	if (signalpipe[0] > maxsock)
37448989Ssheldonh	    maxsock = signalpipe[0];
37548989Ssheldonh	if (signalpipe[1] > maxsock)
37648989Ssheldonh	    maxsock = signalpipe[1];
37741685Sdillon
3781553Srgrimes	for (;;) {
3791553Srgrimes	    int n, ctrl;
3801553Srgrimes	    fd_set readable;
3811553Srgrimes
38248991Ssheldonh#ifdef SANITY_CHECK
3831553Srgrimes	    if (nsock == 0) {
38447015Sdes		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
38547015Sdes		exit(EX_SOFTWARE);
3861553Srgrimes	    }
38748991Ssheldonh#endif
3881553Srgrimes	    readable = allsock;
38942122Sdes	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
39042122Sdes		(fd_set *)0, (struct timeval *)0)) <= 0) {
39142122Sdes		    if (n < 0 && errno != EINTR) {
3921553Srgrimes			syslog(LOG_WARNING, "select: %m");
39328907Simp			sleep(1);
39428907Simp		    }
3951553Srgrimes		    continue;
3961553Srgrimes	    }
39742122Sdes	    /* handle any queued signal flags */
39842250Sdes	    if (FD_ISSET(signalpipe[0], &readable)) {
39942122Sdes		int n;
40042250Sdes		if (ioctl(signalpipe[0], FIONREAD, &n) != 0) {
40142122Sdes		    syslog(LOG_ERR, "ioctl: %m");
40242122Sdes		    exit(EX_OSERR);
40342122Sdes		}
40442250Sdes		while (--n >= 0) {
40542250Sdes		    char c;
40642250Sdes		    if (read(signalpipe[0], &c, 1) != 1) {
40742250Sdes			syslog(LOG_ERR, "read: %m");
40842250Sdes			exit(EX_OSERR);
40942250Sdes		    }
41042250Sdes		    if (debug)
41156482Scharnier			warnx("handling signal flag %c", c);
41242250Sdes		    switch(c) {
41342250Sdes		    case 'A': /* sigalrm */
41442250Sdes			retry();
41542250Sdes			break;
41642250Sdes		    case 'C': /* sigchld */
41742250Sdes			reapchild();
41842250Sdes			break;
41942250Sdes		    case 'H': /* sighup */
42042250Sdes			config();
42142250Sdes			break;
42242250Sdes		    }
42342250Sdes		}
42442122Sdes	    }
4251553Srgrimes	    for (sep = servtab; n && sep; sep = sep->se_next)
4261553Srgrimes	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
4271553Srgrimes		    n--;
4281553Srgrimes		    if (debug)
42929602Scharnier			    warnx("someone wants %s", sep->se_service);
43019618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
43153256Speter			    i = 1;
43253256Speter			    if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
43353256Speter				    syslog(LOG_ERR, "ioctl (FIONBIO, 1): %m");
4341553Srgrimes			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
4351553Srgrimes				(int *)0);
4361553Srgrimes			    if (debug)
43729602Scharnier				    warnx("accept, ctrl %d", ctrl);
4381553Srgrimes			    if (ctrl < 0) {
4391553Srgrimes				    if (errno != EINTR)
4401553Srgrimes					    syslog(LOG_WARNING,
4411553Srgrimes						"accept (for %s): %m",
44237844Sphk						sep->se_service);
44337816Sphk                                      if (sep->se_accept &&
44437816Sphk                                          sep->se_socktype == SOCK_STREAM)
44537816Sphk                                              close(ctrl);
4461553Srgrimes				    continue;
4471553Srgrimes			    }
44853256Speter			    i = 0;
44953256Speter			    if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
45053256Speter				    syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m");
45153256Speter			    if (ioctl(ctrl, FIONBIO, &i) < 0)
45253256Speter				    syslog(LOG_ERR, "ioctl2(FIONBIO, 0): %m");
45330847Sdima			    if (cpmip(sep, ctrl) < 0) {
45430847Sdima				close(ctrl);
45530847Sdima				continue;
45630847Sdima			    }
4571553Srgrimes		    } else
4581553Srgrimes			    ctrl = sep->se_fd;
45948382Ssheldonh		    if (log && !ISWRAP(sep)) {
46048382Ssheldonh			    pnm = "unknown";
46148382Ssheldonh			    i = sizeof peer;
46248382Ssheldonh			    if (getpeername(ctrl, (struct sockaddr *)
46348382Ssheldonh					    &peer, &i)) {
46448382Ssheldonh				    i = sizeof peer;
46548382Ssheldonh				    if (recvfrom(ctrl, buf, sizeof(buf),
46648382Ssheldonh					MSG_PEEK,
46748382Ssheldonh					(struct sockaddr *)&peer, &i) >= 0)
46848382Ssheldonh					    pnm = inet_ntoa(peer.sin_addr);
46948382Ssheldonh			    }
47048382Ssheldonh			    else
47148382Ssheldonh				    pnm = inet_ntoa(peer.sin_addr);
47248382Ssheldonh			    syslog(LOG_INFO,"%s from %s", sep->se_service, pnm);
47348382Ssheldonh		    }
47442122Sdes		    (void) sigblock(SIGBLOCK);
4751553Srgrimes		    pid = 0;
47647972Ssheldonh		    /*
47748958Ssheldonh		     * Fork for all external services, builtins which need to
47848958Ssheldonh		     * fork and anything we're wrapping (as wrapping might
47948958Ssheldonh		     * block or use hosts_options(5) twist).
48047972Ssheldonh		     */
48148382Ssheldonh		    dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep);
4821553Srgrimes		    if (dofork) {
4831553Srgrimes			    if (sep->se_count++ == 0)
48437856Sache				(void)gettimeofday(&sep->se_time, (struct timezone *)NULL);
4851553Srgrimes			    else if (sep->se_count >= toomany) {
4861553Srgrimes				struct timeval now;
4871553Srgrimes
48837856Sache				(void)gettimeofday(&now, (struct timezone *)NULL);
4891553Srgrimes				if (now.tv_sec - sep->se_time.tv_sec >
4901553Srgrimes				    CNT_INTVL) {
4911553Srgrimes					sep->se_time = now;
4921553Srgrimes					sep->se_count = 1;
4931553Srgrimes				} else {
4941553Srgrimes					syslog(LOG_ERR,
4951553Srgrimes			"%s/%s server failing (looping), service terminated",
4961553Srgrimes					    sep->se_service, sep->se_proto);
4971553Srgrimes					close_sep(sep);
49842122Sdes					sigsetmask(0L);
4991553Srgrimes					if (!timingout) {
5001553Srgrimes						timingout = 1;
5011553Srgrimes						alarm(RETRYTIME);
5021553Srgrimes					}
5031553Srgrimes					continue;
5041553Srgrimes				}
5051553Srgrimes			    }
5061553Srgrimes			    pid = fork();
5071553Srgrimes		    }
5081553Srgrimes		    if (pid < 0) {
5091553Srgrimes			    syslog(LOG_ERR, "fork: %m");
51019618Sjulian			    if (sep->se_accept &&
5111553Srgrimes				sep->se_socktype == SOCK_STREAM)
5121553Srgrimes				    close(ctrl);
51342122Sdes			    sigsetmask(0L);
5141553Srgrimes			    sleep(1);
5151553Srgrimes			    continue;
5161553Srgrimes		    }
51719618Sjulian		    if (pid)
51819618Sjulian			addchild(sep, pid);
51942122Sdes		    sigsetmask(0L);
5201553Srgrimes		    if (pid == 0) {
5211553Srgrimes			    if (dofork) {
5221553Srgrimes				if (debug)
52329602Scharnier					warnx("+ closing from %d", maxsock);
5241553Srgrimes				for (tmpint = maxsock; tmpint > 2; tmpint--)
5251553Srgrimes					if (tmpint != ctrl)
52619617Sjulian						(void) close(tmpint);
52748962Ssheldonh				sigaction(SIGALRM, &saalrm, (struct sigaction *)0);
52848962Ssheldonh				sigaction(SIGCHLD, &sachld, (struct sigaction *)0);
52948962Ssheldonh				sigaction(SIGHUP, &sahup, (struct sigaction *)0);
53048962Ssheldonh				/* SIGPIPE reset before exec */
5311553Srgrimes			    }
53235829Sguido			    /*
53335829Sguido			     * Call tcpmux to find the real service to exec.
53435829Sguido			     */
53535829Sguido			    if (sep->se_bi &&
53635829Sguido				sep->se_bi->bi_fn == (void (*)()) tcpmux) {
53735829Sguido				    sep = tcpmux(ctrl);
53835829Sguido				    if (sep == NULL) {
53935829Sguido					    close(ctrl);
54035829Sguido					    _exit(0);
54135829Sguido				    }
54235829Sguido			    }
54348382Ssheldonh			    if (ISWRAP(sep)) {
54448698Ssheldonh				inetd_setproctitle("wrapping", ctrl);
54547972Ssheldonh				service = sep->se_server_name ?
54647972Ssheldonh				    sep->se_server_name : sep->se_service;
54747972Ssheldonh				request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, NULL);
54845089Smarkm				fromhost(&req);
54947972Ssheldonh				deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
55047972Ssheldonh				allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
55145089Smarkm				denied = !hosts_access(&req);
55245089Smarkm				if (denied) {
55345089Smarkm				    syslog(deny_severity,
55445089Smarkm				        "refused connection from %.500s, service %s (%s)",
55545089Smarkm				        eval_client(&req), service, sep->se_proto);
55648382Ssheldonh				    if (sep->se_socktype != SOCK_STREAM)
55748382Ssheldonh					recv(ctrl, buf, sizeof (buf), 0);
55848382Ssheldonh				    if (dofork)
55948382Ssheldonh					_exit(0);
56045089Smarkm				}
56145089Smarkm				if (log) {
56245089Smarkm				    syslog(allow_severity,
56345089Smarkm				        "connection from %.500s, service %s (%s)",
56445089Smarkm					eval_client(&req), service, sep->se_proto);
56545089Smarkm				}
56645089Smarkm			    }
56719617Sjulian			    if (sep->se_bi) {
5681553Srgrimes				(*sep->se_bi->bi_fn)(ctrl, sep);
56919617Sjulian			    } else {
5701553Srgrimes				if (debug)
57129602Scharnier					warnx("%d execl %s",
57229602Scharnier						getpid(), sep->se_server);
5731553Srgrimes				dup2(ctrl, 0);
5741553Srgrimes				close(ctrl);
5751553Srgrimes				dup2(0, 1);
5761553Srgrimes				dup2(0, 2);
5771553Srgrimes				if ((pwd = getpwnam(sep->se_user)) == NULL) {
5781553Srgrimes					syslog(LOG_ERR,
57956482Scharnier					    "%s/%s: %s: no such user",
5801553Srgrimes						sep->se_service, sep->se_proto,
5811553Srgrimes						sep->se_user);
5821553Srgrimes					if (sep->se_socktype != SOCK_STREAM)
5831553Srgrimes						recv(0, buf, sizeof (buf), 0);
58419617Sjulian					_exit(EX_NOUSER);
5851553Srgrimes				}
58630807Sache				grp = NULL;
58730807Sache				if (   sep->se_group != NULL
58830807Sache				    && (grp = getgrnam(sep->se_group)) == NULL
58930807Sache				   ) {
59030807Sache					syslog(LOG_ERR,
59156482Scharnier					    "%s/%s: %s: no such group",
59230807Sache						sep->se_service, sep->se_proto,
59330807Sache						sep->se_group);
59430807Sache					if (sep->se_socktype != SOCK_STREAM)
59530807Sache						recv(0, buf, sizeof (buf), 0);
59630807Sache					_exit(EX_NOUSER);
59730807Sache				}
59830807Sache				if (grp != NULL)
59930807Sache					pwd->pw_gid = grp->gr_gid;
60021640Speter#ifdef LOGIN_CAP
60130792Sache				if ((lc = login_getclass(sep->se_class)) == NULL) {
60230792Sache					/* error syslogged by getclass */
60330792Sache					syslog(LOG_ERR,
60430792Sache					    "%s/%s: %s: login class error",
60537850Sache						sep->se_service, sep->se_proto,
60637850Sache						sep->se_class);
60730792Sache					if (sep->se_socktype != SOCK_STREAM)
60830792Sache						recv(0, buf, sizeof (buf), 0);
60930792Sache					_exit(EX_NOUSER);
61030792Sache				}
61121640Speter#endif
61212024Speter				if (setsid() < 0) {
61312024Speter					syslog(LOG_ERR,
61412024Speter						"%s: can't setsid(): %m",
61512024Speter						 sep->se_service);
61619617Sjulian					/* _exit(EX_OSERR); not fatal yet */
61712024Speter				}
61821640Speter#ifdef LOGIN_CAP
61921640Speter				if (setusercontext(lc, pwd, pwd->pw_uid,
62021640Speter				    LOGIN_SETALL) != 0) {
62121640Speter					syslog(LOG_ERR,
62221640Speter					 "%s: can't setusercontext(..%s..): %m",
62321640Speter					 sep->se_service, sep->se_user);
62421640Speter					_exit(EX_OSERR);
62521640Speter				}
62621640Speter#else
6271553Srgrimes				if (pwd->pw_uid) {
62812024Speter					if (setlogin(sep->se_user) < 0) {
62912024Speter						syslog(LOG_ERR,
63012024Speter						 "%s: can't setlogin(%s): %m",
63112024Speter						 sep->se_service, sep->se_user);
63219617Sjulian						/* _exit(EX_OSERR); not yet */
63312024Speter					}
6341553Srgrimes					if (setgid(pwd->pw_gid) < 0) {
6351553Srgrimes						syslog(LOG_ERR,
6368857Srgrimes						  "%s: can't set gid %d: %m",
6371553Srgrimes						  sep->se_service, pwd->pw_gid);
63819617Sjulian						_exit(EX_OSERR);
6391553Srgrimes					}
6401553Srgrimes					(void) initgroups(pwd->pw_name,
6411553Srgrimes							pwd->pw_gid);
6421553Srgrimes					if (setuid(pwd->pw_uid) < 0) {
6431553Srgrimes						syslog(LOG_ERR,
6448857Srgrimes						  "%s: can't set uid %d: %m",
6451553Srgrimes						  sep->se_service, pwd->pw_uid);
64619617Sjulian						_exit(EX_OSERR);
6471553Srgrimes					}
6481553Srgrimes				}
64921640Speter#endif
65035948Sbde				sigaction(SIGPIPE, &sapipe,
65135948Sbde				    (struct sigaction *)0);
6521553Srgrimes				execv(sep->se_server, sep->se_argv);
65345089Smarkm				syslog(LOG_ERR,
65445089Smarkm				    "cannot execute %s: %m", sep->se_server);
6551553Srgrimes				if (sep->se_socktype != SOCK_STREAM)
6561553Srgrimes					recv(0, buf, sizeof (buf), 0);
6571553Srgrimes			    }
65847972Ssheldonh			    if (dofork)
65947972Ssheldonh				_exit(0);
6601553Srgrimes		    }
66119618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
6621553Srgrimes			    close(ctrl);
6631553Srgrimes		}
6641553Srgrimes	}
6651553Srgrimes}
6661553Srgrimes
66719618Sjulian/*
66842122Sdes * Add a signal flag to the signal flag queue for later handling
66942122Sdes */
67042122Sdes
67142122Sdesvoid flag_signal(c)
67242122Sdes    char c;
67342122Sdes{
67442250Sdes	if (write(signalpipe[1], &c, 1) != 1) {
67542250Sdes		syslog(LOG_ERR, "write: %m");
67648985Ssheldonh		_exit(EX_OSERR);
67742250Sdes	}
67842122Sdes}
67942122Sdes
68042122Sdes/*
68119618Sjulian * Record a new child pid for this service. If we've reached the
68219618Sjulian * limit on children, then stop accepting incoming requests.
68319618Sjulian */
68419618Sjulian
6851553Srgrimesvoid
68619618Sjulianaddchild(struct servtab *sep, pid_t pid)
68719618Sjulian{
68819618Sjulian#ifdef SANITY_CHECK
68919618Sjulian	if (sep->se_numchild >= sep->se_maxchild) {
69019618Sjulian		syslog(LOG_ERR, "%s: %d >= %d",
69119618Sjulian		    __FUNCTION__, sep->se_numchild, sep->se_maxchild);
69219618Sjulian		exit(EX_SOFTWARE);
69319618Sjulian	}
69419618Sjulian#endif
69519618Sjulian	if (sep->se_maxchild == 0)
69619618Sjulian		return;
69719618Sjulian	sep->se_pids[sep->se_numchild++] = pid;
69819618Sjulian	if (sep->se_numchild == sep->se_maxchild)
69919618Sjulian		disable(sep);
70019618Sjulian}
70119618Sjulian
70219618Sjulian/*
70319618Sjulian * Some child process has exited. See if it's on somebody's list.
70419618Sjulian */
70519618Sjulian
70619618Sjulianvoid
70742122Sdesflag_reapchild(signo)
7081553Srgrimes	int signo;
7091553Srgrimes{
71042250Sdes	flag_signal('C');
71142122Sdes}
71242122Sdes
71342122Sdesvoid
71442122Sdesreapchild()
71542122Sdes{
71619618Sjulian	int k, status;
7171553Srgrimes	pid_t pid;
7181553Srgrimes	struct servtab *sep;
7191553Srgrimes
7201553Srgrimes	for (;;) {
7211553Srgrimes		pid = wait3(&status, WNOHANG, (struct rusage *)0);
7221553Srgrimes		if (pid <= 0)
7231553Srgrimes			break;
7241553Srgrimes		if (debug)
72529602Scharnier			warnx("%d reaped, status %#x", pid, status);
72619618Sjulian		for (sep = servtab; sep; sep = sep->se_next) {
72719618Sjulian			for (k = 0; k < sep->se_numchild; k++)
72819618Sjulian				if (sep->se_pids[k] == pid)
72919618Sjulian					break;
73019618Sjulian			if (k == sep->se_numchild)
73119618Sjulian				continue;
73219618Sjulian			if (sep->se_numchild == sep->se_maxchild)
73319618Sjulian				enable(sep);
73419618Sjulian			sep->se_pids[k] = sep->se_pids[--sep->se_numchild];
73519618Sjulian			if (status)
73619618Sjulian				syslog(LOG_WARNING,
73719618Sjulian				    "%s[%d]: exit status 0x%x",
73819618Sjulian				    sep->se_server, pid, status);
73919618Sjulian			break;
74019618Sjulian		}
7411553Srgrimes	}
7421553Srgrimes}
7431553Srgrimes
7441553Srgrimesvoid
74542122Sdesflag_config(signo)
7461553Srgrimes	int signo;
7471553Srgrimes{
74842250Sdes	flag_signal('H');
74942122Sdes}
75042122Sdes
75142122Sdesvoid config()
75242122Sdes{
75319618Sjulian	struct servtab *sep, *new, **sepp;
75442122Sdes	long omask;
7551553Srgrimes
7561553Srgrimes	if (!setconfig()) {
7571553Srgrimes		syslog(LOG_ERR, "%s: %m", CONFIG);
7581553Srgrimes		return;
7591553Srgrimes	}
7601553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
7611553Srgrimes		sep->se_checked = 0;
76219618Sjulian	while ((new = getconfigent())) {
76330807Sache		if (getpwnam(new->se_user) == NULL) {
7641553Srgrimes			syslog(LOG_ERR,
76556482Scharnier				"%s/%s: no such user '%s', service ignored",
76619618Sjulian				new->se_service, new->se_proto, new->se_user);
7671553Srgrimes			continue;
7681553Srgrimes		}
76930807Sache		if (new->se_group && getgrnam(new->se_group) == NULL) {
77030807Sache			syslog(LOG_ERR,
77156482Scharnier				"%s/%s: no such group '%s', service ignored",
77230807Sache				new->se_service, new->se_proto, new->se_group);
77330807Sache			continue;
77430807Sache		}
77530792Sache#ifdef LOGIN_CAP
77630792Sache		if (login_getclass(new->se_class) == NULL) {
77730792Sache			/* error syslogged by getclass */
77830792Sache			syslog(LOG_ERR,
77937850Sache				"%s/%s: %s: login class error, service ignored",
78037850Sache				new->se_service, new->se_proto, new->se_class);
78130792Sache			continue;
78230792Sache		}
78330792Sache#endif
7841553Srgrimes		for (sep = servtab; sep; sep = sep->se_next)
78519618Sjulian			if (strcmp(sep->se_service, new->se_service) == 0 &&
78619618Sjulian			    strcmp(sep->se_proto, new->se_proto) == 0)
7871553Srgrimes				break;
7881553Srgrimes		if (sep != 0) {
7891553Srgrimes			int i;
7901553Srgrimes
79119618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; }
79242122Sdes			omask = sigblock(SIGBLOCK);
79319618Sjulian			/* copy over outstanding child pids */
79419618Sjulian			if (sep->se_maxchild && new->se_maxchild) {
79519618Sjulian				new->se_numchild = sep->se_numchild;
79619618Sjulian				if (new->se_numchild > new->se_maxchild)
79719618Sjulian					new->se_numchild = new->se_maxchild;
79819618Sjulian				memcpy(new->se_pids, sep->se_pids,
79919618Sjulian				    new->se_numchild * sizeof(*new->se_pids));
80019618Sjulian			}
80119618Sjulian			SWAP(sep->se_pids, new->se_pids);
80219618Sjulian			sep->se_maxchild = new->se_maxchild;
80319618Sjulian			sep->se_numchild = new->se_numchild;
80430847Sdima			sep->se_maxcpm = new->se_maxcpm;
80519618Sjulian			/* might need to turn on or off service now */
80619618Sjulian			if (sep->se_fd >= 0) {
80719618Sjulian			      if (sep->se_maxchild
80819618Sjulian				  && sep->se_numchild == sep->se_maxchild) {
80919618Sjulian				      if (FD_ISSET(sep->se_fd, &allsock))
81019618Sjulian					  disable(sep);
81119618Sjulian			      } else {
81219618Sjulian				      if (!FD_ISSET(sep->se_fd, &allsock))
81319618Sjulian					  enable(sep);
81419618Sjulian			      }
81519618Sjulian			}
81619618Sjulian			sep->se_accept = new->se_accept;
81730807Sache			SWAP(sep->se_user, new->se_user);
81830807Sache			SWAP(sep->se_group, new->se_group);
81930792Sache#ifdef LOGIN_CAP
82030807Sache			SWAP(sep->se_class, new->se_class);
82130792Sache#endif
82230807Sache			SWAP(sep->se_server, new->se_server);
82347972Ssheldonh			SWAP(sep->se_server_name, new->se_server_name);
8241553Srgrimes			for (i = 0; i < MAXARGV; i++)
82519618Sjulian				SWAP(sep->se_argv[i], new->se_argv[i]);
82642122Sdes			sigsetmask(omask);
82719618Sjulian			freeconfig(new);
8281553Srgrimes			if (debug)
8291553Srgrimes				print_service("REDO", sep);
8301553Srgrimes		} else {
83119618Sjulian			sep = enter(new);
8321553Srgrimes			if (debug)
8331553Srgrimes				print_service("ADD ", sep);
8341553Srgrimes		}
8351553Srgrimes		sep->se_checked = 1;
8361553Srgrimes		if (ISMUX(sep)) {
8371553Srgrimes			sep->se_fd = -1;
8381553Srgrimes			continue;
8391553Srgrimes		}
8402657Scsgr		if (!sep->se_rpc) {
8412657Scsgr			sp = getservbyname(sep->se_service, sep->se_proto);
8422657Scsgr			if (sp == 0) {
8432657Scsgr				syslog(LOG_ERR, "%s/%s: unknown service",
8442657Scsgr			    	sep->se_service, sep->se_proto);
8452657Scsgr				sep->se_checked = 0;
8462657Scsgr				continue;
8472657Scsgr			}
8482657Scsgr			if (sp->s_port != sep->se_ctrladdr.sin_port) {
8492657Scsgr				sep->se_ctrladdr.sin_family = AF_INET;
85022306Sjulian				sep->se_ctrladdr.sin_addr = bind_address;
8512657Scsgr				sep->se_ctrladdr.sin_port = sp->s_port;
8522657Scsgr				if (sep->se_fd >= 0)
8532657Scsgr					close_sep(sep);
8542657Scsgr			}
8552657Scsgr		} else {
8562657Scsgr			rpc = getrpcbyname(sep->se_service);
8572657Scsgr			if (rpc == 0) {
85852219Scharnier				syslog(LOG_ERR, "%s/%s unknown RPC service",
8592657Scsgr					sep->se_service, sep->se_proto);
8602657Scsgr				if (sep->se_fd != -1)
8612657Scsgr					(void) close(sep->se_fd);
8622657Scsgr				sep->se_fd = -1;
8632657Scsgr					continue;
8642657Scsgr			}
8652657Scsgr			if (rpc->r_number != sep->se_rpc_prog) {
8662657Scsgr				if (sep->se_rpc_prog)
8672657Scsgr					unregisterrpc(sep);
8682657Scsgr				sep->se_rpc_prog = rpc->r_number;
8692657Scsgr				if (sep->se_fd != -1)
8702657Scsgr					(void) close(sep->se_fd);
8712657Scsgr				sep->se_fd = -1;
8722657Scsgr			}
8731553Srgrimes		}
8741553Srgrimes		if (sep->se_fd == -1)
8751553Srgrimes			setup(sep);
8761553Srgrimes	}
8771553Srgrimes	endconfig();
8781553Srgrimes	/*
8791553Srgrimes	 * Purge anything not looked at above.
8801553Srgrimes	 */
88142122Sdes	omask = sigblock(SIGBLOCK);
8821553Srgrimes	sepp = &servtab;
88319617Sjulian	while ((sep = *sepp)) {
8841553Srgrimes		if (sep->se_checked) {
8851553Srgrimes			sepp = &sep->se_next;
8861553Srgrimes			continue;
8871553Srgrimes		}
8881553Srgrimes		*sepp = sep->se_next;
8891553Srgrimes		if (sep->se_fd >= 0)
8901553Srgrimes			close_sep(sep);
8911553Srgrimes		if (debug)
8921553Srgrimes			print_service("FREE", sep);
8932657Scsgr		if (sep->se_rpc && sep->se_rpc_prog > 0)
8942657Scsgr			unregisterrpc(sep);
8951553Srgrimes		freeconfig(sep);
8961553Srgrimes		free((char *)sep);
8971553Srgrimes	}
89842122Sdes	(void) sigsetmask(omask);
8991553Srgrimes}
9001553Srgrimes
9011553Srgrimesvoid
9022657Scsgrunregisterrpc(sep)
9032657Scsgr	struct servtab *sep;
9042657Scsgr{
9052657Scsgr        int i;
9062657Scsgr        struct servtab *sepp;
90742122Sdes	long omask;
9082657Scsgr
90942122Sdes	omask = sigblock(SIGBLOCK);
9102657Scsgr        for (sepp = servtab; sepp; sepp = sepp->se_next) {
9112657Scsgr                if (sepp == sep)
9122657Scsgr                        continue;
9132657Scsgr		if (sep->se_checked == 0 ||
9142657Scsgr                    !sepp->se_rpc ||
9152657Scsgr                    sep->se_rpc_prog != sepp->se_rpc_prog)
9162657Scsgr			continue;
9172657Scsgr                return;
9182657Scsgr        }
9192657Scsgr        if (debug)
9202657Scsgr                print_service("UNREG", sep);
9212657Scsgr        for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++)
9222657Scsgr                pmap_unset(sep->se_rpc_prog, i);
9232657Scsgr        if (sep->se_fd != -1)
9242657Scsgr                (void) close(sep->se_fd);
9252657Scsgr        sep->se_fd = -1;
92642122Sdes	(void) sigsetmask(omask);
9272657Scsgr}
9282657Scsgr
9292657Scsgrvoid
93042122Sdesflag_retry(signo)
9311553Srgrimes	int signo;
9321553Srgrimes{
93342250Sdes	flag_signal('A');
93442122Sdes}
93542122Sdes
93642122Sdesvoid
93742122Sdesretry()
93842122Sdes{
9391553Srgrimes	struct servtab *sep;
9401553Srgrimes
9411553Srgrimes	timingout = 0;
9421553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
94319617Sjulian		if (sep->se_fd == -1 && !ISMUX(sep))
9441553Srgrimes			setup(sep);
9451553Srgrimes}
9461553Srgrimes
9471553Srgrimesvoid
9481553Srgrimessetup(sep)
9491553Srgrimes	struct servtab *sep;
9501553Srgrimes{
9511553Srgrimes	int on = 1;
9521553Srgrimes
9531553Srgrimes	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
9541553Srgrimes		if (debug)
95529602Scharnier			warn("socket failed on %s/%s",
95629602Scharnier				sep->se_service, sep->se_proto);
9571553Srgrimes		syslog(LOG_ERR, "%s/%s: socket: %m",
9581553Srgrimes		    sep->se_service, sep->se_proto);
9591553Srgrimes		return;
9601553Srgrimes	}
9611553Srgrimes#define	turnon(fd, opt) \
9621553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
9631553Srgrimes	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
9641553Srgrimes	    turnon(sep->se_fd, SO_DEBUG) < 0)
9651553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
9661553Srgrimes	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
9671553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
96825253Swollman#ifdef SO_PRIVSTATE
96913956Swollman	if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
97013956Swollman		syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
97125253Swollman#endif
9721553Srgrimes#undef turnon
97336042Sguido	if (sep->se_type == TTCP_TYPE)
97436042Sguido		if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH,
97536042Sguido		    (char *)&on, sizeof (on)) < 0)
97636042Sguido			syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m");
9771553Srgrimes	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
9781553Srgrimes	    sizeof (sep->se_ctrladdr)) < 0) {
9791553Srgrimes		if (debug)
98029602Scharnier			warn("bind failed on %s/%s",
98129602Scharnier				sep->se_service, sep->se_proto);
9821553Srgrimes		syslog(LOG_ERR, "%s/%s: bind: %m",
9831553Srgrimes		    sep->se_service, sep->se_proto);
9841553Srgrimes		(void) close(sep->se_fd);
9851553Srgrimes		sep->se_fd = -1;
9861553Srgrimes		if (!timingout) {
9871553Srgrimes			timingout = 1;
9881553Srgrimes			alarm(RETRYTIME);
9891553Srgrimes		}
9901553Srgrimes		return;
9911553Srgrimes	}
9922657Scsgr        if (sep->se_rpc) {
9932657Scsgr                int i, len = sizeof(struct sockaddr);
9942657Scsgr
9958857Srgrimes                if (getsockname(sep->se_fd,
9962657Scsgr				(struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
9972657Scsgr                        syslog(LOG_ERR, "%s/%s: getsockname: %m",
9982657Scsgr                               sep->se_service, sep->se_proto);
9992657Scsgr                        (void) close(sep->se_fd);
10002657Scsgr                        sep->se_fd = -1;
10018857Srgrimes                        return;
10022657Scsgr                }
10032657Scsgr                if (debug)
10042657Scsgr                        print_service("REG ", sep);
10052657Scsgr                for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
10062657Scsgr                        pmap_unset(sep->se_rpc_prog, i);
10072657Scsgr                        pmap_set(sep->se_rpc_prog, i,
10082657Scsgr                                 (sep->se_socktype == SOCK_DGRAM)
10092657Scsgr                                 ? IPPROTO_UDP : IPPROTO_TCP,
10102657Scsgr                                 ntohs(sep->se_ctrladdr.sin_port));
10112657Scsgr                }
10128857Srgrimes
10132657Scsgr        }
10141553Srgrimes	if (sep->se_socktype == SOCK_STREAM)
101517197Sdg		listen(sep->se_fd, 64);
101619618Sjulian	enable(sep);
10171553Srgrimes	if (debug) {
101829602Scharnier		warnx("registered %s on %d",
10191553Srgrimes			sep->se_server, sep->se_fd);
10201553Srgrimes	}
10211553Srgrimes}
10221553Srgrimes
10231553Srgrimes/*
10241553Srgrimes * Finish with a service and its socket.
10251553Srgrimes */
10261553Srgrimesvoid
10271553Srgrimesclose_sep(sep)
10281553Srgrimes	struct servtab *sep;
10291553Srgrimes{
10301553Srgrimes	if (sep->se_fd >= 0) {
103119618Sjulian		if (FD_ISSET(sep->se_fd, &allsock))
103219618Sjulian			disable(sep);
10331553Srgrimes		(void) close(sep->se_fd);
10341553Srgrimes		sep->se_fd = -1;
10351553Srgrimes	}
10361553Srgrimes	sep->se_count = 0;
103719618Sjulian	sep->se_numchild = 0;	/* forget about any existing children */
10381553Srgrimes}
10391553Srgrimes
104048467Ssheldonhint
104148467Ssheldonhmatchservent(name1, name2, proto)
104248467Ssheldonh	char *name1, *name2, *proto;
104348467Ssheldonh{
104448467Ssheldonh	char **alias;
104548467Ssheldonh	struct servent *se;
104648467Ssheldonh
104749026Sdes	if (strcmp(name1, name2) == 0)
104849026Sdes		return(1);
104948467Ssheldonh	if ((se = getservbyname(name1, proto)) != NULL) {
105048467Ssheldonh		if (strcmp(name2, se->s_name) == 0)
105148467Ssheldonh			return(1);
105248467Ssheldonh		for (alias = se->s_aliases; *alias; alias++)
105348467Ssheldonh			if (strcmp(name2, *alias) == 0)
105448467Ssheldonh				return(1);
105548467Ssheldonh	}
105648467Ssheldonh	return(0);
105748467Ssheldonh}
105848467Ssheldonh
10591553Srgrimesstruct servtab *
10601553Srgrimesenter(cp)
10611553Srgrimes	struct servtab *cp;
10621553Srgrimes{
10631553Srgrimes	struct servtab *sep;
106442122Sdes	long omask;
10651553Srgrimes
10661553Srgrimes	sep = (struct servtab *)malloc(sizeof (*sep));
10671553Srgrimes	if (sep == (struct servtab *)0) {
106849102Ssheldonh		syslog(LOG_ERR, "malloc: %m");
106919617Sjulian		exit(EX_OSERR);
10701553Srgrimes	}
10711553Srgrimes	*sep = *cp;
10721553Srgrimes	sep->se_fd = -1;
107342122Sdes	omask = sigblock(SIGBLOCK);
10741553Srgrimes	sep->se_next = servtab;
10751553Srgrimes	servtab = sep;
107642122Sdes	sigsetmask(omask);
10771553Srgrimes	return (sep);
10781553Srgrimes}
10791553Srgrimes
108019618Sjulianvoid
108119618Sjulianenable(struct servtab *sep)
108219618Sjulian{
108319618Sjulian	if (debug)
108429602Scharnier		warnx(
108519618Sjulian		    "enabling %s, fd %d", sep->se_service, sep->se_fd);
108619618Sjulian#ifdef SANITY_CHECK
108719618Sjulian	if (sep->se_fd < 0) {
108819618Sjulian		syslog(LOG_ERR,
108919618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
109019618Sjulian		exit(EX_SOFTWARE);
109119618Sjulian	}
109219618Sjulian	if (ISMUX(sep)) {
109319618Sjulian		syslog(LOG_ERR,
109419618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
109519618Sjulian		exit(EX_SOFTWARE);
109619618Sjulian	}
109719618Sjulian	if (FD_ISSET(sep->se_fd, &allsock)) {
109819618Sjulian		syslog(LOG_ERR,
109919618Sjulian		    "%s: %s: not off", __FUNCTION__, sep->se_service);
110019618Sjulian		exit(EX_SOFTWARE);
110119618Sjulian	}
110248991Ssheldonh	nsock++;
110319618Sjulian#endif
110419618Sjulian	FD_SET(sep->se_fd, &allsock);
110519618Sjulian	if (sep->se_fd > maxsock)
110619618Sjulian		maxsock = sep->se_fd;
110719618Sjulian}
110819618Sjulian
110919618Sjulianvoid
111019618Sjuliandisable(struct servtab *sep)
111119618Sjulian{
111219618Sjulian	if (debug)
111329602Scharnier		warnx(
111419618Sjulian		    "disabling %s, fd %d", sep->se_service, sep->se_fd);
111519618Sjulian#ifdef SANITY_CHECK
111619618Sjulian	if (sep->se_fd < 0) {
111719618Sjulian		syslog(LOG_ERR,
111819618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
111919618Sjulian		exit(EX_SOFTWARE);
112019618Sjulian	}
112119618Sjulian	if (ISMUX(sep)) {
112219618Sjulian		syslog(LOG_ERR,
112319618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
112419618Sjulian		exit(EX_SOFTWARE);
112519618Sjulian	}
112619618Sjulian	if (!FD_ISSET(sep->se_fd, &allsock)) {
112719618Sjulian		syslog(LOG_ERR,
112819618Sjulian		    "%s: %s: not on", __FUNCTION__, sep->se_service);
112919618Sjulian		exit(EX_SOFTWARE);
113019618Sjulian	}
113119618Sjulian	if (nsock == 0) {
113219618Sjulian		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
113319618Sjulian		exit(EX_SOFTWARE);
113419618Sjulian	}
113548991Ssheldonh	nsock--;
113619618Sjulian#endif
113719618Sjulian	FD_CLR(sep->se_fd, &allsock);
113819618Sjulian	if (sep->se_fd == maxsock)
113919618Sjulian		maxsock--;
114019618Sjulian}
114119618Sjulian
11421553SrgrimesFILE	*fconfig = NULL;
11431553Srgrimesstruct	servtab serv;
11441553Srgrimeschar	line[LINE_MAX];
11451553Srgrimes
11461553Srgrimesint
11471553Srgrimessetconfig()
11481553Srgrimes{
11491553Srgrimes
11501553Srgrimes	if (fconfig != NULL) {
11511553Srgrimes		fseek(fconfig, 0L, SEEK_SET);
11521553Srgrimes		return (1);
11531553Srgrimes	}
11541553Srgrimes	fconfig = fopen(CONFIG, "r");
11551553Srgrimes	return (fconfig != NULL);
11561553Srgrimes}
11571553Srgrimes
11581553Srgrimesvoid
11591553Srgrimesendconfig()
11601553Srgrimes{
11611553Srgrimes	if (fconfig) {
11621553Srgrimes		(void) fclose(fconfig);
11631553Srgrimes		fconfig = NULL;
11641553Srgrimes	}
11651553Srgrimes}
11661553Srgrimes
11671553Srgrimesstruct servtab *
11681553Srgrimesgetconfigent()
11691553Srgrimes{
11701553Srgrimes	struct servtab *sep = &serv;
11711553Srgrimes	int argc;
117219618Sjulian	char *cp, *arg, *s;
11732657Scsgr	char *versp;
11741553Srgrimes	static char TCPMUX_TOKEN[] = "tcpmux/";
11751553Srgrimes#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
11761553Srgrimes
11771553Srgrimesmore:
11781553Srgrimes	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
11791553Srgrimes		;
11801553Srgrimes	if (cp == NULL)
11811553Srgrimes		return ((struct servtab *)0);
11821553Srgrimes	/*
11831553Srgrimes	 * clear the static buffer, since some fields (se_ctrladdr,
11841553Srgrimes	 * for example) don't get initialized here.
11851553Srgrimes	 */
11861553Srgrimes	memset((caddr_t)sep, 0, sizeof *sep);
11871553Srgrimes	arg = skip(&cp);
11881553Srgrimes	if (cp == NULL) {
11891553Srgrimes		/* got an empty line containing just blanks/tabs. */
11901553Srgrimes		goto more;
11911553Srgrimes	}
11921553Srgrimes	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
11931553Srgrimes		char *c = arg + MUX_LEN;
11941553Srgrimes		if (*c == '+') {
11951553Srgrimes			sep->se_type = MUXPLUS_TYPE;
11961553Srgrimes			c++;
11971553Srgrimes		} else
11981553Srgrimes			sep->se_type = MUX_TYPE;
11991553Srgrimes		sep->se_service = newstr(c);
12001553Srgrimes	} else {
12011553Srgrimes		sep->se_service = newstr(arg);
12021553Srgrimes		sep->se_type = NORM_TYPE;
12031553Srgrimes	}
12041553Srgrimes	arg = sskip(&cp);
12051553Srgrimes	if (strcmp(arg, "stream") == 0)
12061553Srgrimes		sep->se_socktype = SOCK_STREAM;
12071553Srgrimes	else if (strcmp(arg, "dgram") == 0)
12081553Srgrimes		sep->se_socktype = SOCK_DGRAM;
12091553Srgrimes	else if (strcmp(arg, "rdm") == 0)
12101553Srgrimes		sep->se_socktype = SOCK_RDM;
12111553Srgrimes	else if (strcmp(arg, "seqpacket") == 0)
12121553Srgrimes		sep->se_socktype = SOCK_SEQPACKET;
12131553Srgrimes	else if (strcmp(arg, "raw") == 0)
12141553Srgrimes		sep->se_socktype = SOCK_RAW;
12151553Srgrimes	else
12161553Srgrimes		sep->se_socktype = -1;
121736042Sguido
121836042Sguido	arg = sskip(&cp);
121936042Sguido	if (strcmp(arg, "tcp/ttcp") == 0) {
122036042Sguido		sep->se_type = TTCP_TYPE;
122136042Sguido		sep->se_proto = newstr("tcp");
122236042Sguido	} else {
122336042Sguido		sep->se_proto = newstr(arg);
122436042Sguido	}
12252657Scsgr        if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
122619237Sjoerg                memmove(sep->se_proto, sep->se_proto + 4,
122719237Sjoerg                    strlen(sep->se_proto) + 1 - 4);
12282657Scsgr                sep->se_rpc = 1;
12292657Scsgr                sep->se_rpc_prog = sep->se_rpc_lowvers =
12302657Scsgr			sep->se_rpc_lowvers = 0;
12312657Scsgr                sep->se_ctrladdr.sin_family = AF_INET;
12322657Scsgr                sep->se_ctrladdr.sin_port = 0;
123317482Sjulian                sep->se_ctrladdr.sin_addr = bind_address;
12342657Scsgr                if ((versp = rindex(sep->se_service, '/'))) {
12352657Scsgr                        *versp++ = '\0';
12362657Scsgr                        switch (sscanf(versp, "%d-%d",
12372657Scsgr                                       &sep->se_rpc_lowvers,
12382657Scsgr                                       &sep->se_rpc_highvers)) {
12392657Scsgr                        case 2:
12402657Scsgr                                break;
12412657Scsgr                        case 1:
12422657Scsgr                                sep->se_rpc_highvers =
12432657Scsgr                                        sep->se_rpc_lowvers;
12442657Scsgr                                break;
12452657Scsgr                        default:
12468857Srgrimes                                syslog(LOG_ERR,
124752219Scharnier					"bad RPC version specifier; %s",
12482657Scsgr					sep->se_service);
12492657Scsgr                                freeconfig(sep);
12502657Scsgr                                goto more;
12512657Scsgr                        }
12522657Scsgr                }
12532657Scsgr                else {
12542657Scsgr                        sep->se_rpc_lowvers =
12552657Scsgr                                sep->se_rpc_highvers = 1;
12562657Scsgr                }
12572657Scsgr        }
12581553Srgrimes	arg = sskip(&cp);
125919618Sjulian	if (!strncmp(arg, "wait", 4))
126019618Sjulian		sep->se_accept = 0;
126119618Sjulian	else if (!strncmp(arg, "nowait", 6))
126219618Sjulian		sep->se_accept = 1;
126319618Sjulian	else {
126419618Sjulian		syslog(LOG_ERR,
126519618Sjulian			"%s: bad wait/nowait for service %s",
126619618Sjulian			CONFIG, sep->se_service);
126719618Sjulian		goto more;
126819618Sjulian	}
126948069Ssheldonh	sep->se_maxchild = -1;
127048069Ssheldonh	sep->se_maxcpm = -1;
127119618Sjulian	if ((s = strchr(arg, '/')) != NULL) {
127219618Sjulian		char *eptr;
127319618Sjulian		u_long val;
127419618Sjulian
127519618Sjulian		val = strtoul(s + 1, &eptr, 10);
127630847Sdima		if (eptr == s + 1 || val > MAX_MAXCHLD) {
127719618Sjulian			syslog(LOG_ERR,
127819618Sjulian				"%s: bad max-child for service %s",
127919618Sjulian				CONFIG, sep->se_service);
128019618Sjulian			goto more;
128119618Sjulian		}
128248069Ssheldonh		if (debug)
128348069Ssheldonh			if (!sep->se_accept && val != 1)
128448069Ssheldonh				warnx("maxchild=%lu for wait service %s"
128548069Ssheldonh				    " not recommended", val, sep->se_service);
128619618Sjulian		sep->se_maxchild = val;
128730847Sdima		if (*eptr == '/')
128830847Sdima			sep->se_maxcpm = strtol(eptr + 1, &eptr, 10);
128930847Sdima		/*
129030847Sdima		 * explicitly do not check for \0 for future expansion /
129130847Sdima		 * backwards compatibility
129230847Sdima		 */
129319618Sjulian	}
12941553Srgrimes	if (ISMUX(sep)) {
12951553Srgrimes		/*
129619618Sjulian		 * Silently enforce "nowait" mode for TCPMUX services
129719618Sjulian		 * since they don't have an assigned port to listen on.
12981553Srgrimes		 */
129919618Sjulian		sep->se_accept = 1;
13001553Srgrimes		if (strcmp(sep->se_proto, "tcp")) {
13018857Srgrimes			syslog(LOG_ERR,
13021553Srgrimes				"%s: bad protocol for tcpmux service %s",
13031553Srgrimes				CONFIG, sep->se_service);
13041553Srgrimes			goto more;
13051553Srgrimes		}
13061553Srgrimes		if (sep->se_socktype != SOCK_STREAM) {
13078857Srgrimes			syslog(LOG_ERR,
13081553Srgrimes				"%s: bad socket type for tcpmux service %s",
13091553Srgrimes				CONFIG, sep->se_service);
13101553Srgrimes			goto more;
13111553Srgrimes		}
13121553Srgrimes	}
13131553Srgrimes	sep->se_user = newstr(sskip(&cp));
131430792Sache#ifdef LOGIN_CAP
131530792Sache	if ((s = strrchr(sep->se_user, '/')) != NULL) {
131630792Sache		*s = '\0';
131730792Sache		sep->se_class = newstr(s + 1);
131830792Sache	} else
131930792Sache		sep->se_class = newstr(RESOURCE_RC);
132030792Sache#endif
132130807Sache	if ((s = strrchr(sep->se_user, ':')) != NULL) {
132230807Sache		*s = '\0';
132330807Sache		sep->se_group = newstr(s + 1);
132430807Sache	} else
132530807Sache		sep->se_group = NULL;
13261553Srgrimes	sep->se_server = newstr(sskip(&cp));
132745588Smarkm	if ((sep->se_server_name = rindex(sep->se_server, '/')))
132845588Smarkm		sep->se_server_name++;
13291553Srgrimes	if (strcmp(sep->se_server, "internal") == 0) {
13301553Srgrimes		struct biltin *bi;
13311553Srgrimes
13321553Srgrimes		for (bi = biltins; bi->bi_service; bi++)
133349026Sdes			if (bi->bi_socktype == sep->se_socktype &&
133448467Ssheldonh			    matchservent(bi->bi_service, sep->se_service,
133548467Ssheldonh			    sep->se_proto))
13361553Srgrimes				break;
13371553Srgrimes		if (bi->bi_service == 0) {
13381553Srgrimes			syslog(LOG_ERR, "internal service %s unknown",
13391553Srgrimes				sep->se_service);
13401553Srgrimes			goto more;
13411553Srgrimes		}
134219618Sjulian		sep->se_accept = 1;	/* force accept mode for built-ins */
13431553Srgrimes		sep->se_bi = bi;
13441553Srgrimes	} else
13451553Srgrimes		sep->se_bi = NULL;
134648069Ssheldonh	if (sep->se_maxcpm < 0)
134748069Ssheldonh		sep->se_maxcpm = maxcpm;
134845588Smarkm	if (sep->se_maxchild < 0) {	/* apply default max-children */
134948069Ssheldonh		if (sep->se_bi && sep->se_bi->bi_maxchild >= 0)
135019618Sjulian			sep->se_maxchild = sep->se_bi->bi_maxchild;
135148069Ssheldonh		else if (sep->se_accept)
135248069Ssheldonh			sep->se_maxchild = maxchild > 0 ? maxchild : 0;
135319618Sjulian		else
135448069Ssheldonh			sep->se_maxchild = 1;
135545588Smarkm	}
135619618Sjulian	if (sep->se_maxchild) {
135719618Sjulian		sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
135819618Sjulian		if (sep->se_pids == NULL) {
135949102Ssheldonh			syslog(LOG_ERR, "malloc: %m");
136019618Sjulian			exit(EX_OSERR);
136119618Sjulian		}
136219618Sjulian	}
13631553Srgrimes	argc = 0;
13641553Srgrimes	for (arg = skip(&cp); cp; arg = skip(&cp))
136519618Sjulian		if (argc < MAXARGV) {
13661553Srgrimes			sep->se_argv[argc++] = newstr(arg);
136719618Sjulian		} else {
136819618Sjulian			syslog(LOG_ERR,
136919618Sjulian				"%s: too many arguments for service %s",
137019618Sjulian				CONFIG, sep->se_service);
137119618Sjulian			goto more;
137219618Sjulian		}
13731553Srgrimes	while (argc <= MAXARGV)
13741553Srgrimes		sep->se_argv[argc++] = NULL;
13751553Srgrimes	return (sep);
13761553Srgrimes}
13771553Srgrimes
13781553Srgrimesvoid
13791553Srgrimesfreeconfig(cp)
13801553Srgrimes	struct servtab *cp;
13811553Srgrimes{
13821553Srgrimes	int i;
13831553Srgrimes
13841553Srgrimes	if (cp->se_service)
13851553Srgrimes		free(cp->se_service);
13861553Srgrimes	if (cp->se_proto)
13871553Srgrimes		free(cp->se_proto);
13881553Srgrimes	if (cp->se_user)
13891553Srgrimes		free(cp->se_user);
139030807Sache	if (cp->se_group)
139130807Sache		free(cp->se_group);
139230792Sache#ifdef LOGIN_CAP
139330792Sache	if (cp->se_class)
139430792Sache		free(cp->se_class);
139530792Sache#endif
13961553Srgrimes	if (cp->se_server)
13971553Srgrimes		free(cp->se_server);
139819618Sjulian	if (cp->se_pids)
139919618Sjulian		free(cp->se_pids);
14001553Srgrimes	for (i = 0; i < MAXARGV; i++)
14011553Srgrimes		if (cp->se_argv[i])
14021553Srgrimes			free(cp->se_argv[i]);
14031553Srgrimes}
14041553Srgrimes
14051553Srgrimes
14061553Srgrimes/*
14071553Srgrimes * Safe skip - if skip returns null, log a syntax error in the
14081553Srgrimes * configuration file and exit.
14091553Srgrimes */
14101553Srgrimeschar *
14111553Srgrimessskip(cpp)
14121553Srgrimes	char **cpp;
14131553Srgrimes{
14141553Srgrimes	char *cp;
14151553Srgrimes
14161553Srgrimes	cp = skip(cpp);
14171553Srgrimes	if (cp == NULL) {
14181553Srgrimes		syslog(LOG_ERR, "%s: syntax error", CONFIG);
141919617Sjulian		exit(EX_DATAERR);
14201553Srgrimes	}
14211553Srgrimes	return (cp);
14221553Srgrimes}
14231553Srgrimes
14241553Srgrimeschar *
14251553Srgrimesskip(cpp)
14261553Srgrimes	char **cpp;
14271553Srgrimes{
14281553Srgrimes	char *cp = *cpp;
14291553Srgrimes	char *start;
143011933Sadam	char quote = '\0';
14311553Srgrimes
14321553Srgrimesagain:
14331553Srgrimes	while (*cp == ' ' || *cp == '\t')
14341553Srgrimes		cp++;
14351553Srgrimes	if (*cp == '\0') {
14361553Srgrimes		int c;
14371553Srgrimes
14381553Srgrimes		c = getc(fconfig);
14391553Srgrimes		(void) ungetc(c, fconfig);
14401553Srgrimes		if (c == ' ' || c == '\t')
144119617Sjulian			if ((cp = nextline(fconfig)))
14421553Srgrimes				goto again;
14431553Srgrimes		*cpp = (char *)0;
14441553Srgrimes		return ((char *)0);
14451553Srgrimes	}
144611933Sadam	if (*cp == '"' || *cp == '\'')
144711933Sadam		quote = *cp++;
14481553Srgrimes	start = cp;
144911933Sadam	if (quote)
145011933Sadam		while (*cp && *cp != quote)
145111933Sadam			cp++;
145211933Sadam	else
145311933Sadam		while (*cp && *cp != ' ' && *cp != '\t')
145411933Sadam			cp++;
14551553Srgrimes	if (*cp != '\0')
14561553Srgrimes		*cp++ = '\0';
14571553Srgrimes	*cpp = cp;
14581553Srgrimes	return (start);
14591553Srgrimes}
14601553Srgrimes
14611553Srgrimeschar *
14621553Srgrimesnextline(fd)
14631553Srgrimes	FILE *fd;
14641553Srgrimes{
14651553Srgrimes	char *cp;
14661553Srgrimes
14671553Srgrimes	if (fgets(line, sizeof (line), fd) == NULL)
14681553Srgrimes		return ((char *)0);
14691553Srgrimes	cp = strchr(line, '\n');
14701553Srgrimes	if (cp)
14711553Srgrimes		*cp = '\0';
14721553Srgrimes	return (line);
14731553Srgrimes}
14741553Srgrimes
14751553Srgrimeschar *
14761553Srgrimesnewstr(cp)
14771553Srgrimes	char *cp;
14781553Srgrimes{
147919617Sjulian	if ((cp = strdup(cp ? cp : "")))
14801553Srgrimes		return (cp);
14811553Srgrimes	syslog(LOG_ERR, "strdup: %m");
148219617Sjulian	exit(EX_OSERR);
14831553Srgrimes}
14841553Srgrimes
148513142Speter#ifdef OLD_SETPROCTITLE
14861553Srgrimesvoid
148713142Speterinetd_setproctitle(a, s)
14881553Srgrimes	char *a;
14891553Srgrimes	int s;
14901553Srgrimes{
14911553Srgrimes	int size;
14921553Srgrimes	char *cp;
14931553Srgrimes	struct sockaddr_in sin;
14941553Srgrimes	char buf[80];
14951553Srgrimes
14961553Srgrimes	cp = Argv[0];
14971553Srgrimes	size = sizeof(sin);
14981553Srgrimes	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
14998857Srgrimes		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
15001553Srgrimes	else
15018857Srgrimes		(void) sprintf(buf, "-%s", a);
15021553Srgrimes	strncpy(cp, buf, LastArg - cp);
15031553Srgrimes	cp += strlen(cp);
15041553Srgrimes	while (cp < LastArg)
15051553Srgrimes		*cp++ = ' ';
15061553Srgrimes}
150713142Speter#else
150813142Spetervoid
150913142Speterinetd_setproctitle(a, s)
151013142Speter	char *a;
151113142Speter	int s;
151213142Speter{
151313142Speter	int size;
151413142Speter	struct sockaddr_in sin;
151513142Speter	char buf[80];
15161553Srgrimes
151713142Speter	size = sizeof(sin);
151813142Speter	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
151913142Speter		(void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr));
152013142Speter	else
152113142Speter		(void) sprintf(buf, "%s", a);
152213142Speter	setproctitle("%s", buf);
152313142Speter}
152413142Speter#endif
152513142Speter
152613142Speter
15271553Srgrimes/*
15281553Srgrimes * Internet services provided internally by inetd:
15291553Srgrimes */
15301553Srgrimes
15315182Swollmanint check_loop(sin, sep)
15325182Swollman	struct sockaddr_in *sin;
15335182Swollman	struct servtab *sep;
15345182Swollman{
15355182Swollman	struct servtab *se2;
15365182Swollman
15375182Swollman	for (se2 = servtab; se2; se2 = se2->se_next) {
15385182Swollman		if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
15395182Swollman			continue;
15405182Swollman
15415182Swollman		if (sin->sin_port == se2->se_ctrladdr.sin_port) {
15425182Swollman			syslog(LOG_WARNING,
15435182Swollman			       "%s/%s:%s/%s loop request REFUSED from %s",
15448857Srgrimes			       sep->se_service, sep->se_proto,
15455182Swollman			       se2->se_service, se2->se_proto,
15465182Swollman			       inet_ntoa(sin->sin_addr));
15475182Swollman			return 1;
15485182Swollman		}
15495182Swollman	}
15505182Swollman	return 0;
15515182Swollman}
15525182Swollman
15531553Srgrimes/*
15541553Srgrimes * print_service:
15551553Srgrimes *	Dump relevant information to stderr
15561553Srgrimes */
15571553Srgrimesvoid
15581553Srgrimesprint_service(action, sep)
15591553Srgrimes	char *action;
15601553Srgrimes	struct servtab *sep;
15611553Srgrimes{
156219617Sjulian	fprintf(stderr,
156330792Sache#ifdef LOGIN_CAP
156438380Sjb	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%p server=%s\n",
156530792Sache#else
156638380Sjb	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%p server=%s\n",
156730792Sache#endif
156819617Sjulian	    action, sep->se_service, sep->se_proto,
156930807Sache	    sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group,
157030792Sache#ifdef LOGIN_CAP
157130792Sache	    sep->se_class,
157230792Sache#endif
157338417Sjb	    (void *) sep->se_bi, sep->se_server);
15741553Srgrimes}
15751553Srgrimes
157630847Sdima#define CPMHSIZE	256
157730847Sdima#define CPMHMASK	(CPMHSIZE-1)
157830847Sdima#define CHTGRAN		10
157930847Sdima#define CHTSIZE		6
158030847Sdima
158130847Sdimatypedef struct CTime {
158230847Sdima	unsigned long 	ct_Ticks;
158330847Sdima	int		ct_Count;
158430847Sdima} CTime;
158530847Sdima
158630847Sdimatypedef struct CHash {
158730847Sdima	struct in_addr	ch_Addr;
158830847Sdima	time_t		ch_LTime;
158930847Sdima	char		*ch_Service;
159030847Sdima	CTime		ch_Times[CHTSIZE];
159130847Sdima} CHash;
159230847Sdima
159330847SdimaCHash	CHashAry[CPMHSIZE];
159430847Sdima
159530847Sdimaint
159630847Sdimacpmip(sep, ctrl)
159730847Sdima	struct servtab *sep;
159830847Sdima	int ctrl;
159930847Sdima{
160030847Sdima	struct sockaddr_in rsin;
160130847Sdima	int rsinLen = sizeof(rsin);
160230847Sdima	int r = 0;
160330847Sdima
160430847Sdima	/*
160530847Sdima	 * If getpeername() fails, just let it through (if logging is
160630847Sdima	 * enabled the condition is caught elsewhere)
160730847Sdima	 */
160830847Sdima
160930847Sdima	if (sep->se_maxcpm > 0 &&
161030847Sdima	    getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 0 ) {
161130847Sdima		time_t t = time(NULL);
161230847Sdima		int hv = 0xABC3D20F;
161330847Sdima		int i;
161430847Sdima		int cnt = 0;
161530847Sdima		CHash *chBest = NULL;
161630847Sdima		unsigned int ticks = t / CHTGRAN;
161730847Sdima
161830847Sdima		{
161930847Sdima			char *p;
162030847Sdima			int i;
162130847Sdima
162230847Sdima			for (i = 0, p = (char *)&rsin.sin_addr;
162330847Sdima			    i < sizeof(rsin.sin_addr);
162430847Sdima			    ++i, ++p) {
162530847Sdima				hv = (hv << 5) ^ (hv >> 23) ^ *p;
162630847Sdima			}
162730847Sdima			hv = (hv ^ (hv >> 16));
162830847Sdima		}
162930847Sdima		for (i = 0; i < 5; ++i) {
163030847Sdima			CHash *ch = &CHashAry[(hv + i) & CPMHMASK];
163130847Sdima
163230847Sdima			if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr &&
163330847Sdima			    ch->ch_Service && strcmp(sep->se_service,
163430847Sdima			    ch->ch_Service) == 0) {
163530847Sdima				chBest = ch;
163630847Sdima				break;
163730847Sdima			}
163830847Sdima			if (chBest == NULL || ch->ch_LTime == 0 ||
163930847Sdima			    ch->ch_LTime < chBest->ch_LTime) {
164030847Sdima				chBest = ch;
164130847Sdima			}
164230847Sdima		}
164330847Sdima		if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr ||
164430847Sdima		    chBest->ch_Service == NULL ||
164530847Sdima		    strcmp(sep->se_service, chBest->ch_Service) != 0) {
164630847Sdima			chBest->ch_Addr = rsin.sin_addr;
164730847Sdima			if (chBest->ch_Service)
164830847Sdima				free(chBest->ch_Service);
164930847Sdima			chBest->ch_Service = strdup(sep->se_service);
165030847Sdima			bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
165130847Sdima		}
165230847Sdima		chBest->ch_LTime = t;
165330847Sdima		{
165430847Sdima			CTime *ct = &chBest->ch_Times[ticks % CHTSIZE];
165530847Sdima			if (ct->ct_Ticks != ticks) {
165630847Sdima				ct->ct_Ticks = ticks;
165730847Sdima				ct->ct_Count = 0;
165830847Sdima			}
165930847Sdima			++ct->ct_Count;
166030847Sdima		}
166130847Sdima		for (i = 0; i < CHTSIZE; ++i) {
166230847Sdima			CTime *ct = &chBest->ch_Times[i];
166330847Sdima			if (ct->ct_Ticks <= ticks &&
166430847Sdima			    ct->ct_Ticks >= ticks - CHTSIZE) {
166530847Sdima				cnt += ct->ct_Count;
166630847Sdima			}
166730847Sdima		}
166830847Sdima		if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) {
166930847Sdima			r = -1;
167030847Sdima			syslog(LOG_ERR,
167133794Spst			    "%s from %s exceeded counts/min (limit %d/min)",
167233794Spst			    sep->se_service, inet_ntoa(rsin.sin_addr),
167333794Spst			    sep->se_maxcpm);
167430847Sdima		}
167530847Sdima	}
167630847Sdima	return(r);
167730847Sdima}
1678