inetd.c revision 33794
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[] =
4533794Spst	"$Id: inetd.c,v 1.29 1997/10/29 21:49:04 dima Exp $";
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/stat.h>
1081553Srgrimes#include <sys/ioctl.h>
1091553Srgrimes#include <sys/socket.h>
1101553Srgrimes#include <sys/wait.h>
1111553Srgrimes#include <sys/time.h>
1121553Srgrimes#include <sys/resource.h>
1131553Srgrimes
1141553Srgrimes#include <netinet/in.h>
1151553Srgrimes#include <arpa/inet.h>
1162657Scsgr#include <rpc/rpc.h>
11719617Sjulian#include <rpc/pmap_clnt.h>
1181553Srgrimes
1191553Srgrimes#include <errno.h>
12029602Scharnier#include <err.h>
1211553Srgrimes#include <fcntl.h>
12230807Sache#include <grp.h>
1231553Srgrimes#include <netdb.h>
1241553Srgrimes#include <pwd.h>
1251553Srgrimes#include <signal.h>
1261553Srgrimes#include <stdio.h>
1271553Srgrimes#include <stdlib.h>
1281553Srgrimes#include <string.h>
1291553Srgrimes#include <syslog.h>
1301553Srgrimes#include <unistd.h>
13113142Speter#include <libutil.h>
13219617Sjulian#include <sysexits.h>
1331553Srgrimes
13421640Speter#ifdef LOGIN_CAP
13521640Speter#include <login_cap.h>
13630792Sache
13730792Sache/* see init.c */
13830792Sache#define RESOURCE_RC "daemon"
13930792Sache
14021640Speter#endif
14121640Speter
1421553Srgrimes#include "pathnames.h"
1431553Srgrimes
14433794Spst#ifndef	MAXCHILD
14533794Spst#define	MAXCHILD	-1		/* maximum number of this service
14633794Spst					   < 0 = no limit */
14733794Spst#endif
14833794Spst
14933794Spst#ifndef	MAXCPM
15033794Spst#define	MAXCPM		-1		/* rate limit invocations from a
15133794Spst					   single remote address,
15233794Spst					   < 0 = no limit */
15333794Spst#endif
15433794Spst
1552659Scsgr#define	TOOMANY		256		/* don't start more than TOOMANY */
1561553Srgrimes#define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
1571553Srgrimes#define	RETRYTIME	(60*10)		/* retry after bind or server fail */
15819618Sjulian#define MAX_MAXCHLD	32767		/* max allowable max children */
1591553Srgrimes
1601553Srgrimes#define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
1611553Srgrimes
1621553Srgrimesint	debug = 0;
1632659Scsgrint	log = 0;
1641553Srgrimesint	nsock, maxsock;
1651553Srgrimesfd_set	allsock;
1661553Srgrimesint	options;
1671553Srgrimesint	timingout;
1681553Srgrimesint	toomany = TOOMANY;
16933794Spstint	maxchild = MAXCPM;
17033794Spstint	maxcpm = MAXCHILD;
1711553Srgrimesstruct	servent *sp;
1722657Scsgrstruct	rpcent *rpc;
17317482Sjulianstruct	in_addr bind_address;
1741553Srgrimes
1751553Srgrimesstruct	servtab {
1761553Srgrimes	char	*se_service;		/* name of service */
1771553Srgrimes	int	se_socktype;		/* type of socket to use */
1781553Srgrimes	char	*se_proto;		/* protocol used */
17930847Sdima	int	se_maxchild;		/* max number of children */
18030847Sdima	int	se_maxcpm;		/* max connects per IP per minute */
18130847Sdima	int	se_numchild;		/* current number of children */
18219618Sjulian	pid_t	*se_pids;		/* array of child pids */
1831553Srgrimes	char	*se_user;		/* user name to run as */
18430807Sache	char    *se_group;              /* group name to run as */
18530792Sache#ifdef  LOGIN_CAP
18630792Sache	char    *se_class;              /* login class name to run with */
18730792Sache#endif
1881553Srgrimes	struct	biltin *se_bi;		/* if built-in, description */
1891553Srgrimes	char	*se_server;		/* server program */
1901553Srgrimes#define	MAXARGV 20
1911553Srgrimes	char	*se_argv[MAXARGV+1];	/* program arguments */
1921553Srgrimes	int	se_fd;			/* open descriptor */
1931553Srgrimes	struct	sockaddr_in se_ctrladdr;/* bound address */
19419618Sjulian	u_char	se_type;		/* type: normal, mux, or mux+ */
19519618Sjulian	u_char	se_checked;		/* looked at during merge */
19619618Sjulian	u_char	se_accept;		/* i.e., wait/nowait mode */
19719618Sjulian	u_char	se_rpc;			/* ==1 if RPC service */
1982657Scsgr	int	se_rpc_prog;		/* RPC program number */
1992657Scsgr	u_int	se_rpc_lowvers;		/* RPC low version */
2002657Scsgr	u_int	se_rpc_highvers;	/* RPC high version */
2011553Srgrimes	int	se_count;		/* number started since se_time */
2021553Srgrimes	struct	timeval se_time;	/* start of se_count */
2031553Srgrimes	struct	servtab *se_next;
2041553Srgrimes} *servtab;
2051553Srgrimes
2061553Srgrimes#define NORM_TYPE	0
2071553Srgrimes#define MUX_TYPE	1
2081553Srgrimes#define MUXPLUS_TYPE	2
2091553Srgrimes#define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
2101553Srgrimes			 ((sep)->se_type == MUXPLUS_TYPE))
2111553Srgrimes#define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
2121553Srgrimes
2131553Srgrimes
2141553Srgrimesvoid		chargen_dg __P((int, struct servtab *));
2151553Srgrimesvoid		chargen_stream __P((int, struct servtab *));
2161553Srgrimesvoid		close_sep __P((struct servtab *));
2171553Srgrimesvoid		config __P((int));
2181553Srgrimesvoid		daytime_dg __P((int, struct servtab *));
2191553Srgrimesvoid		daytime_stream __P((int, struct servtab *));
2201553Srgrimesvoid		discard_dg __P((int, struct servtab *));
2211553Srgrimesvoid		discard_stream __P((int, struct servtab *));
2221553Srgrimesvoid		echo_dg __P((int, struct servtab *));
2231553Srgrimesvoid		echo_stream __P((int, struct servtab *));
2241553Srgrimesvoid		endconfig __P((void));
2251553Srgrimesstruct servtab *enter __P((struct servtab *));
2261553Srgrimesvoid		freeconfig __P((struct servtab *));
2271553Srgrimesstruct servtab *getconfigent __P((void));
2281553Srgrimesvoid		machtime_dg __P((int, struct servtab *));
2291553Srgrimesvoid		machtime_stream __P((int, struct servtab *));
2301553Srgrimeschar	       *newstr __P((char *));
2311553Srgrimeschar	       *nextline __P((FILE *));
2321553Srgrimesvoid		print_service __P((char *, struct servtab *));
23319618Sjulianvoid		addchild __P((struct servtab *, int));
2341553Srgrimesvoid		reapchild __P((int));
23519618Sjulianvoid		enable __P((struct servtab *));
23619618Sjulianvoid		disable __P((struct servtab *));
2371553Srgrimesvoid		retry __P((int));
2381553Srgrimesint		setconfig __P((void));
2391553Srgrimesvoid		setup __P((struct servtab *));
2401553Srgrimeschar	       *sskip __P((char **));
2411553Srgrimeschar	       *skip __P((char **));
2421553Srgrimesstruct servtab *tcpmux __P((int));
24330847Sdimaint		cpmip __P((struct servtab *, int));
2441553Srgrimes
2452657Scsgrvoid		unregisterrpc __P((register struct servtab *sep));
2462657Scsgr
2471553Srgrimesstruct biltin {
2481553Srgrimes	char	*bi_service;		/* internally provided service name */
2491553Srgrimes	int	bi_socktype;		/* type of socket supported */
2501553Srgrimes	short	bi_fork;		/* 1 if should fork before call */
25130847Sdima	int	bi_maxchild;		/* max number of children (default) */
2521553Srgrimes	void	(*bi_fn)();		/* function which performs it */
2531553Srgrimes} biltins[] = {
2541553Srgrimes	/* Echo received data */
2551553Srgrimes	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream },
2561553Srgrimes	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg },
2571553Srgrimes
2581553Srgrimes	/* Internet /dev/null */
2591553Srgrimes	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream },
2601553Srgrimes	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg },
2611553Srgrimes
2621553Srgrimes	/* Return 32 bit time since 1970 */
2631553Srgrimes	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream },
2641553Srgrimes	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg },
2651553Srgrimes
2661553Srgrimes	/* Return human-readable time */
2671553Srgrimes	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream },
2681553Srgrimes	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg },
2691553Srgrimes
2701553Srgrimes	/* Familiar character generator */
2711553Srgrimes	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream },
2721553Srgrimes	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg },
2731553Srgrimes
2741553Srgrimes	{ "tcpmux",	SOCK_STREAM,	1, 0,	(void (*)())tcpmux },
2751553Srgrimes
2761553Srgrimes	{ NULL }
2771553Srgrimes};
2781553Srgrimes
2791553Srgrimes#define NUMINT	(sizeof(intab) / sizeof(struct inent))
2801553Srgrimeschar	*CONFIG = _PATH_INETDCONF;
28117482Sjulianchar	*pid_file = _PATH_INETDPID;
28213142Speter
28313142Speter#ifdef OLD_SETPROCTITLE
2841553Srgrimeschar	**Argv;
2851553Srgrimeschar 	*LastArg;
28613142Speter#endif
2871553Srgrimes
2881553Srgrimesint
28933794Spstgetvalue(arg, value, whine)
29033794Spst	char *arg, *whine;
29133794Spst	int  *value;
29233794Spst{
29333794Spst	int  tmp;
29433794Spst	char *p;
29533794Spst
29633794Spst	tmp = strtol(arg, &p, 0);
29733794Spst	if (tmp < 1 || *p) {
29833794Spst		syslog(LOG_ERR, whine, arg);
29933794Spst		return 1;			/* failure */
30033794Spst	}
30133794Spst	*value = tmp;
30233794Spst	return 0;				/* success */
30333794Spst}
30433794Spst
30533794Spstint
3061553Srgrimesmain(argc, argv, envp)
3071553Srgrimes	int argc;
3081553Srgrimes	char *argv[], *envp[];
3091553Srgrimes{
3101553Srgrimes	struct servtab *sep;
3111553Srgrimes	struct passwd *pwd;
31230807Sache	struct group *grp;
3131553Srgrimes	struct sigvec sv;
3141553Srgrimes	int tmpint, ch, dofork;
3151553Srgrimes	pid_t pid;
3161553Srgrimes	char buf[50];
3172659Scsgr	struct  sockaddr_in peer;
3182659Scsgr	int i;
31921640Speter#ifdef LOGIN_CAP
32021640Speter	login_cap_t *lc = NULL;
32121640Speter#endif
3221553Srgrimes
32313142Speter
32413142Speter#ifdef OLD_SETPROCTITLE
3251553Srgrimes	Argv = argv;
3261553Srgrimes	if (envp == 0 || *envp == 0)
3271553Srgrimes		envp = argv;
3281553Srgrimes	while (*envp)
3291553Srgrimes		envp++;
3301553Srgrimes	LastArg = envp[-1] + strlen(envp[-1]);
33113142Speter#endif
3321553Srgrimes
3331553Srgrimes	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
3341553Srgrimes
33517482Sjulian	bind_address.s_addr = htonl(INADDR_ANY);
33633794Spst	while ((ch = getopt(argc, argv, "dlR:a:c:C:p:")) != -1)
3371553Srgrimes		switch(ch) {
3381553Srgrimes		case 'd':
3391553Srgrimes			debug = 1;
3401553Srgrimes			options |= SO_DEBUG;
3411553Srgrimes			break;
3422659Scsgr		case 'l':
3432659Scsgr			log = 1;
3442659Scsgr			break;
34533794Spst		case 'R':
34633794Spst			getvalue(optarg, &toomany,
34733794Spst				"-R %s: bad value for service invocation rate");
3481553Srgrimes			break;
34933794Spst		case 'c':
35033794Spst			getvalue(optarg, &maxchild,
35133794Spst				"-c %s: bad value for maximum children");
35233794Spst			break;
35333794Spst		case 'C':
35433794Spst			getvalue(optarg, &maxcpm,
35533794Spst				"-C %s: bad value for maximum children/minute");
35633794Spst			break;
35717482Sjulian		case 'a':
35817482Sjulian			if (!inet_aton(optarg, &bind_address)) {
35917482Sjulian				syslog(LOG_ERR,
36017482Sjulian			         "-a %s: invalid IP address", optarg);
36119617Sjulian				exit(EX_USAGE);
36217482Sjulian			}
36317482Sjulian			break;
36417482Sjulian		case 'p':
36517482Sjulian			pid_file = optarg;
36617482Sjulian			break;
3671553Srgrimes		case '?':
3681553Srgrimes		default:
3691553Srgrimes			syslog(LOG_ERR,
37017482Sjulian				"usage: inetd [-dl] [-a address] [-R rate]"
37133794Spst				" [-c maximum] [-C rate]"
37217482Sjulian				" [-p pidfile] [conf-file]");
37319617Sjulian			exit(EX_USAGE);
3741553Srgrimes		}
3751553Srgrimes	argc -= optind;
3761553Srgrimes	argv += optind;
3771553Srgrimes
3781553Srgrimes	if (argc > 0)
3791553Srgrimes		CONFIG = argv[0];
3801553Srgrimes	if (debug == 0) {
38111447Swollman		FILE *fp;
38212024Speter		if (daemon(0, 0) < 0) {
38312024Speter			syslog(LOG_WARNING, "daemon(0,0) failed: %m");
38412024Speter		}
38512024Speter		/*
38612024Speter		 * In case somebody has started inetd manually, we need to
38712024Speter		 * clear the logname, so that old servers run as root do not
38812024Speter		 * get the user's logname..
38912024Speter		 */
39012024Speter		if (setlogin("") < 0) {
39112024Speter			syslog(LOG_WARNING, "cannot clear logname: %m");
39212024Speter			/* no big deal if it fails.. */
39312024Speter		}
39411447Swollman		pid = getpid();
39517482Sjulian		fp = fopen(pid_file, "w");
39611447Swollman		if (fp) {
39711447Swollman			fprintf(fp, "%ld\n", (long)pid);
39811447Swollman			fclose(fp);
39911447Swollman		} else {
40017482Sjulian			syslog(LOG_WARNING, "%s: %m", pid_file);
40111447Swollman		}
4021553Srgrimes	}
4031553Srgrimes	memset(&sv, 0, sizeof(sv));
4041553Srgrimes	sv.sv_mask = SIGBLOCK;
4051553Srgrimes	sv.sv_handler = retry;
4061553Srgrimes	sigvec(SIGALRM, &sv, (struct sigvec *)0);
4071553Srgrimes	config(SIGHUP);
4081553Srgrimes	sv.sv_handler = config;
4091553Srgrimes	sigvec(SIGHUP, &sv, (struct sigvec *)0);
4101553Srgrimes	sv.sv_handler = reapchild;
4111553Srgrimes	sigvec(SIGCHLD, &sv, (struct sigvec *)0);
4121553Srgrimes
4131553Srgrimes	{
4141553Srgrimes		/* space for daemons to overwrite environment for ps */
4151553Srgrimes#define	DUMMYSIZE	100
4161553Srgrimes		char dummy[DUMMYSIZE];
4171553Srgrimes
41819298Salex		(void)memset(dummy, 'x', DUMMYSIZE - 1);
4191553Srgrimes		dummy[DUMMYSIZE - 1] = '\0';
4201553Srgrimes		(void)setenv("inetd_dummy", dummy, 1);
4211553Srgrimes	}
4221553Srgrimes
4231553Srgrimes	for (;;) {
4241553Srgrimes	    int n, ctrl;
4251553Srgrimes	    fd_set readable;
4261553Srgrimes
4271553Srgrimes	    if (nsock == 0) {
4281553Srgrimes		(void) sigblock(SIGBLOCK);
4291553Srgrimes		while (nsock == 0)
4301553Srgrimes		    sigpause(0L);
4311553Srgrimes		(void) sigsetmask(0L);
4321553Srgrimes	    }
4331553Srgrimes	    readable = allsock;
4341553Srgrimes	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
4351553Srgrimes		(fd_set *)0, (struct timeval *)0)) <= 0) {
43628907Simp		    if (n < 0 && errno != EINTR) {
4371553Srgrimes			syslog(LOG_WARNING, "select: %m");
43828907Simp			sleep(1);
43928907Simp		    }
4401553Srgrimes		    continue;
4411553Srgrimes	    }
4421553Srgrimes	    for (sep = servtab; n && sep; sep = sep->se_next)
4431553Srgrimes	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
4441553Srgrimes		    n--;
4451553Srgrimes		    if (debug)
44629602Scharnier			    warnx("someone wants %s", sep->se_service);
44719618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
4481553Srgrimes			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
4491553Srgrimes				(int *)0);
4501553Srgrimes			    if (debug)
45129602Scharnier				    warnx("accept, ctrl %d", ctrl);
4521553Srgrimes			    if (ctrl < 0) {
4531553Srgrimes				    if (errno != EINTR)
4541553Srgrimes					    syslog(LOG_WARNING,
4551553Srgrimes						"accept (for %s): %m",
4561553Srgrimes						sep->se_service);
4571553Srgrimes				    continue;
4581553Srgrimes			    }
45930847Sdima			    if (cpmip(sep, ctrl) < 0) {
46030847Sdima				close(ctrl);
46130847Sdima				continue;
46230847Sdima			    }
46319617Sjulian			    if (log) {
4642659Scsgr				i = sizeof peer;
46530847Sdima				if (getpeername(ctrl, (struct sockaddr *)
4662659Scsgr						&peer, &i)) {
4672659Scsgr					syslog(LOG_WARNING,
4682659Scsgr						"getpeername(for %s): %m",
4692659Scsgr						sep->se_service);
47030847Sdima					close(ctrl);
4712659Scsgr					continue;
4722659Scsgr				}
4732659Scsgr				syslog(LOG_INFO,"%s from %s",
4742659Scsgr					sep->se_service,
4752659Scsgr					inet_ntoa(peer.sin_addr));
4762659Scsgr			    }
4771553Srgrimes			    /*
4781553Srgrimes			     * Call tcpmux to find the real service to exec.
4791553Srgrimes			     */
4801553Srgrimes			    if (sep->se_bi &&
4811553Srgrimes				sep->se_bi->bi_fn == (void (*)()) tcpmux) {
4829202Srgrimes				    struct servtab *tsep;
4839202Srgrimes
4849202Srgrimes				    tsep = tcpmux(ctrl);
4859202Srgrimes				    if (tsep == NULL) {
4861553Srgrimes					    close(ctrl);
4871553Srgrimes					    continue;
4881553Srgrimes				    }
4899202Srgrimes				    sep = tsep;
4901553Srgrimes			    }
4911553Srgrimes		    } else
4921553Srgrimes			    ctrl = sep->se_fd;
4931553Srgrimes		    (void) sigblock(SIGBLOCK);
4941553Srgrimes		    pid = 0;
4951553Srgrimes		    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
4961553Srgrimes		    if (dofork) {
4971553Srgrimes			    if (sep->se_count++ == 0)
4981553Srgrimes				(void)gettimeofday(&sep->se_time,
4991553Srgrimes				    (struct timezone *)0);
5001553Srgrimes			    else if (sep->se_count >= toomany) {
5011553Srgrimes				struct timeval now;
5021553Srgrimes
5031553Srgrimes				(void)gettimeofday(&now, (struct timezone *)0);
5041553Srgrimes				if (now.tv_sec - sep->se_time.tv_sec >
5051553Srgrimes				    CNT_INTVL) {
5061553Srgrimes					sep->se_time = now;
5071553Srgrimes					sep->se_count = 1;
5081553Srgrimes				} else {
5091553Srgrimes					syslog(LOG_ERR,
5101553Srgrimes			"%s/%s server failing (looping), service terminated",
5111553Srgrimes					    sep->se_service, sep->se_proto);
5121553Srgrimes					close_sep(sep);
5131553Srgrimes					sigsetmask(0L);
5141553Srgrimes					if (!timingout) {
5151553Srgrimes						timingout = 1;
5161553Srgrimes						alarm(RETRYTIME);
5171553Srgrimes					}
5181553Srgrimes					continue;
5191553Srgrimes				}
5201553Srgrimes			    }
5211553Srgrimes			    pid = fork();
5221553Srgrimes		    }
5231553Srgrimes		    if (pid < 0) {
5241553Srgrimes			    syslog(LOG_ERR, "fork: %m");
52519618Sjulian			    if (sep->se_accept &&
5261553Srgrimes				sep->se_socktype == SOCK_STREAM)
5271553Srgrimes				    close(ctrl);
5281553Srgrimes			    sigsetmask(0L);
5291553Srgrimes			    sleep(1);
5301553Srgrimes			    continue;
5311553Srgrimes		    }
53219618Sjulian		    if (pid)
53319618Sjulian			addchild(sep, pid);
5341553Srgrimes		    sigsetmask(0L);
5351553Srgrimes		    if (pid == 0) {
5361553Srgrimes			    if (dofork) {
5371553Srgrimes				if (debug)
53829602Scharnier					warnx("+ closing from %d", maxsock);
5391553Srgrimes				for (tmpint = maxsock; tmpint > 2; tmpint--)
5401553Srgrimes					if (tmpint != ctrl)
54119617Sjulian						(void) close(tmpint);
5421553Srgrimes			    }
54319617Sjulian			    if (sep->se_bi) {
5441553Srgrimes				(*sep->se_bi->bi_fn)(ctrl, sep);
54519617Sjulian				/* NOTREACHED */
54619617Sjulian			    } else {
5471553Srgrimes				if (debug)
54829602Scharnier					warnx("%d execl %s",
54929602Scharnier						getpid(), sep->se_server);
5501553Srgrimes				dup2(ctrl, 0);
5511553Srgrimes				close(ctrl);
5521553Srgrimes				dup2(0, 1);
5531553Srgrimes				dup2(0, 2);
5541553Srgrimes				if ((pwd = getpwnam(sep->se_user)) == NULL) {
5551553Srgrimes					syslog(LOG_ERR,
5561553Srgrimes					    "%s/%s: %s: No such user",
5571553Srgrimes						sep->se_service, sep->se_proto,
5581553Srgrimes						sep->se_user);
5591553Srgrimes					if (sep->se_socktype != SOCK_STREAM)
5601553Srgrimes						recv(0, buf, sizeof (buf), 0);
56119617Sjulian					_exit(EX_NOUSER);
5621553Srgrimes				}
56330807Sache				grp = NULL;
56430807Sache				if (   sep->se_group != NULL
56530807Sache				    && (grp = getgrnam(sep->se_group)) == NULL
56630807Sache				   ) {
56730807Sache					syslog(LOG_ERR,
56830807Sache					    "%s/%s: %s: No such group",
56930807Sache						sep->se_service, sep->se_proto,
57030807Sache						sep->se_group);
57130807Sache					if (sep->se_socktype != SOCK_STREAM)
57230807Sache						recv(0, buf, sizeof (buf), 0);
57330807Sache					_exit(EX_NOUSER);
57430807Sache				}
57530807Sache				if (grp != NULL)
57630807Sache					pwd->pw_gid = grp->gr_gid;
57721640Speter#ifdef LOGIN_CAP
57830792Sache				if ((lc = login_getclass(sep->se_class)) == NULL) {
57930792Sache					/* error syslogged by getclass */
58030792Sache					syslog(LOG_ERR,
58130792Sache					    "%s/%s: %s: login class error",
58230792Sache						sep->se_service, sep->se_proto);
58330792Sache					if (sep->se_socktype != SOCK_STREAM)
58430792Sache						recv(0, buf, sizeof (buf), 0);
58530792Sache					_exit(EX_NOUSER);
58630792Sache				}
58721640Speter#endif
58812024Speter				if (setsid() < 0) {
58912024Speter					syslog(LOG_ERR,
59012024Speter						"%s: can't setsid(): %m",
59112024Speter						 sep->se_service);
59219617Sjulian					/* _exit(EX_OSERR); not fatal yet */
59312024Speter				}
59421640Speter#ifdef LOGIN_CAP
59521640Speter				if (setusercontext(lc, pwd, pwd->pw_uid,
59621640Speter				    LOGIN_SETALL) != 0) {
59721640Speter					syslog(LOG_ERR,
59821640Speter					 "%s: can't setusercontext(..%s..): %m",
59921640Speter					 sep->se_service, sep->se_user);
60021640Speter					_exit(EX_OSERR);
60121640Speter				}
60221640Speter#else
6031553Srgrimes				if (pwd->pw_uid) {
60412024Speter					if (setlogin(sep->se_user) < 0) {
60512024Speter						syslog(LOG_ERR,
60612024Speter						 "%s: can't setlogin(%s): %m",
60712024Speter						 sep->se_service, sep->se_user);
60819617Sjulian						/* _exit(EX_OSERR); not yet */
60912024Speter					}
6101553Srgrimes					if (setgid(pwd->pw_gid) < 0) {
6111553Srgrimes						syslog(LOG_ERR,
6128857Srgrimes						  "%s: can't set gid %d: %m",
6131553Srgrimes						  sep->se_service, pwd->pw_gid);
61419617Sjulian						_exit(EX_OSERR);
6151553Srgrimes					}
6161553Srgrimes					(void) initgroups(pwd->pw_name,
6171553Srgrimes							pwd->pw_gid);
6181553Srgrimes					if (setuid(pwd->pw_uid) < 0) {
6191553Srgrimes						syslog(LOG_ERR,
6208857Srgrimes						  "%s: can't set uid %d: %m",
6211553Srgrimes						  sep->se_service, pwd->pw_uid);
62219617Sjulian						_exit(EX_OSERR);
6231553Srgrimes					}
6241553Srgrimes				}
62521640Speter#endif
6261553Srgrimes				execv(sep->se_server, sep->se_argv);
6271553Srgrimes				if (sep->se_socktype != SOCK_STREAM)
6281553Srgrimes					recv(0, buf, sizeof (buf), 0);
6291553Srgrimes				syslog(LOG_ERR,
6301553Srgrimes				    "cannot execute %s: %m", sep->se_server);
63119617Sjulian				_exit(EX_OSERR);
6321553Srgrimes			    }
6331553Srgrimes		    }
63419618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
6351553Srgrimes			    close(ctrl);
6361553Srgrimes		}
6371553Srgrimes	}
6381553Srgrimes}
6391553Srgrimes
64019618Sjulian/*
64119618Sjulian * Record a new child pid for this service. If we've reached the
64219618Sjulian * limit on children, then stop accepting incoming requests.
64319618Sjulian */
64419618Sjulian
6451553Srgrimesvoid
64619618Sjulianaddchild(struct servtab *sep, pid_t pid)
64719618Sjulian{
64819618Sjulian#ifdef SANITY_CHECK
64919618Sjulian	if (sep->se_numchild >= sep->se_maxchild) {
65019618Sjulian		syslog(LOG_ERR, "%s: %d >= %d",
65119618Sjulian		    __FUNCTION__, sep->se_numchild, sep->se_maxchild);
65219618Sjulian		exit(EX_SOFTWARE);
65319618Sjulian	}
65419618Sjulian#endif
65519618Sjulian	if (sep->se_maxchild == 0)
65619618Sjulian		return;
65719618Sjulian	sep->se_pids[sep->se_numchild++] = pid;
65819618Sjulian	if (sep->se_numchild == sep->se_maxchild)
65919618Sjulian		disable(sep);
66019618Sjulian}
66119618Sjulian
66219618Sjulian/*
66319618Sjulian * Some child process has exited. See if it's on somebody's list.
66419618Sjulian */
66519618Sjulian
66619618Sjulianvoid
6671553Srgrimesreapchild(signo)
6681553Srgrimes	int signo;
6691553Srgrimes{
67019618Sjulian	int k, status;
6711553Srgrimes	pid_t pid;
6721553Srgrimes	struct servtab *sep;
6731553Srgrimes
6741553Srgrimes	for (;;) {
6751553Srgrimes		pid = wait3(&status, WNOHANG, (struct rusage *)0);
6761553Srgrimes		if (pid <= 0)
6771553Srgrimes			break;
6781553Srgrimes		if (debug)
67929602Scharnier			warnx("%d reaped, status %#x", pid, status);
68019618Sjulian		for (sep = servtab; sep; sep = sep->se_next) {
68119618Sjulian			for (k = 0; k < sep->se_numchild; k++)
68219618Sjulian				if (sep->se_pids[k] == pid)
68319618Sjulian					break;
68419618Sjulian			if (k == sep->se_numchild)
68519618Sjulian				continue;
68619618Sjulian			if (sep->se_numchild == sep->se_maxchild)
68719618Sjulian				enable(sep);
68819618Sjulian			sep->se_pids[k] = sep->se_pids[--sep->se_numchild];
68919618Sjulian			if (status)
69019618Sjulian				syslog(LOG_WARNING,
69119618Sjulian				    "%s[%d]: exit status 0x%x",
69219618Sjulian				    sep->se_server, pid, status);
69319618Sjulian			break;
69419618Sjulian		}
6951553Srgrimes	}
6961553Srgrimes}
6971553Srgrimes
6981553Srgrimesvoid
6991553Srgrimesconfig(signo)
7001553Srgrimes	int signo;
7011553Srgrimes{
70219618Sjulian	struct servtab *sep, *new, **sepp;
7031553Srgrimes	long omask;
7041553Srgrimes
7051553Srgrimes	if (!setconfig()) {
7061553Srgrimes		syslog(LOG_ERR, "%s: %m", CONFIG);
7071553Srgrimes		return;
7081553Srgrimes	}
7091553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
7101553Srgrimes		sep->se_checked = 0;
71119618Sjulian	while ((new = getconfigent())) {
71230807Sache		if (getpwnam(new->se_user) == NULL) {
7131553Srgrimes			syslog(LOG_ERR,
7141553Srgrimes				"%s/%s: No such user '%s', service ignored",
71519618Sjulian				new->se_service, new->se_proto, new->se_user);
7161553Srgrimes			continue;
7171553Srgrimes		}
71830807Sache		if (new->se_group && getgrnam(new->se_group) == NULL) {
71930807Sache			syslog(LOG_ERR,
72030807Sache				"%s/%s: No such group '%s', service ignored",
72130807Sache				new->se_service, new->se_proto, new->se_group);
72230807Sache			continue;
72330807Sache		}
72430792Sache#ifdef LOGIN_CAP
72530792Sache		if (login_getclass(new->se_class) == NULL) {
72630792Sache			/* error syslogged by getclass */
72730792Sache			syslog(LOG_ERR,
72830792Sache				"%s/%s: login class error, service ignored",
72930792Sache				new->se_service, new->se_proto);
73030792Sache			continue;
73130792Sache		}
73230792Sache#endif
7331553Srgrimes		for (sep = servtab; sep; sep = sep->se_next)
73419618Sjulian			if (strcmp(sep->se_service, new->se_service) == 0 &&
73519618Sjulian			    strcmp(sep->se_proto, new->se_proto) == 0)
7361553Srgrimes				break;
7371553Srgrimes		if (sep != 0) {
7381553Srgrimes			int i;
7391553Srgrimes
74019618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; }
7411553Srgrimes			omask = sigblock(SIGBLOCK);
74219618Sjulian			/* copy over outstanding child pids */
74319618Sjulian			if (sep->se_maxchild && new->se_maxchild) {
74419618Sjulian				new->se_numchild = sep->se_numchild;
74519618Sjulian				if (new->se_numchild > new->se_maxchild)
74619618Sjulian					new->se_numchild = new->se_maxchild;
74719618Sjulian				memcpy(new->se_pids, sep->se_pids,
74819618Sjulian				    new->se_numchild * sizeof(*new->se_pids));
74919618Sjulian			}
75019618Sjulian			SWAP(sep->se_pids, new->se_pids);
75119618Sjulian			sep->se_maxchild = new->se_maxchild;
75219618Sjulian			sep->se_numchild = new->se_numchild;
75330847Sdima			sep->se_maxcpm = new->se_maxcpm;
75419618Sjulian			/* might need to turn on or off service now */
75519618Sjulian			if (sep->se_fd >= 0) {
75619618Sjulian			      if (sep->se_maxchild
75719618Sjulian				  && sep->se_numchild == sep->se_maxchild) {
75819618Sjulian				      if (FD_ISSET(sep->se_fd, &allsock))
75919618Sjulian					  disable(sep);
76019618Sjulian			      } else {
76119618Sjulian				      if (!FD_ISSET(sep->se_fd, &allsock))
76219618Sjulian					  enable(sep);
76319618Sjulian			      }
76419618Sjulian			}
76519618Sjulian			sep->se_accept = new->se_accept;
76630807Sache			SWAP(sep->se_user, new->se_user);
76730807Sache			SWAP(sep->se_group, new->se_group);
76830792Sache#ifdef LOGIN_CAP
76930807Sache			SWAP(sep->se_class, new->se_class);
77030792Sache#endif
77130807Sache			SWAP(sep->se_server, new->se_server);
7721553Srgrimes			for (i = 0; i < MAXARGV; i++)
77319618Sjulian				SWAP(sep->se_argv[i], new->se_argv[i]);
7741553Srgrimes			sigsetmask(omask);
77519618Sjulian			freeconfig(new);
7761553Srgrimes			if (debug)
7771553Srgrimes				print_service("REDO", sep);
7781553Srgrimes		} else {
77919618Sjulian			sep = enter(new);
7801553Srgrimes			if (debug)
7811553Srgrimes				print_service("ADD ", sep);
7821553Srgrimes		}
7831553Srgrimes		sep->se_checked = 1;
7841553Srgrimes		if (ISMUX(sep)) {
7851553Srgrimes			sep->se_fd = -1;
7861553Srgrimes			continue;
7871553Srgrimes		}
7882657Scsgr		if (!sep->se_rpc) {
7892657Scsgr			sp = getservbyname(sep->se_service, sep->se_proto);
7902657Scsgr			if (sp == 0) {
7912657Scsgr				syslog(LOG_ERR, "%s/%s: unknown service",
7922657Scsgr			    	sep->se_service, sep->se_proto);
7932657Scsgr				sep->se_checked = 0;
7942657Scsgr				continue;
7952657Scsgr			}
7962657Scsgr			if (sp->s_port != sep->se_ctrladdr.sin_port) {
7972657Scsgr				sep->se_ctrladdr.sin_family = AF_INET;
79822306Sjulian				sep->se_ctrladdr.sin_addr = bind_address;
7992657Scsgr				sep->se_ctrladdr.sin_port = sp->s_port;
8002657Scsgr				if (sep->se_fd >= 0)
8012657Scsgr					close_sep(sep);
8022657Scsgr			}
8032657Scsgr		} else {
8042657Scsgr			rpc = getrpcbyname(sep->se_service);
8052657Scsgr			if (rpc == 0) {
8062657Scsgr				syslog(LOG_ERR, "%s/%s unknown RPC service.",
8072657Scsgr					sep->se_service, sep->se_proto);
8082657Scsgr				if (sep->se_fd != -1)
8092657Scsgr					(void) close(sep->se_fd);
8102657Scsgr				sep->se_fd = -1;
8112657Scsgr					continue;
8122657Scsgr			}
8132657Scsgr			if (rpc->r_number != sep->se_rpc_prog) {
8142657Scsgr				if (sep->se_rpc_prog)
8152657Scsgr					unregisterrpc(sep);
8162657Scsgr				sep->se_rpc_prog = rpc->r_number;
8172657Scsgr				if (sep->se_fd != -1)
8182657Scsgr					(void) close(sep->se_fd);
8192657Scsgr				sep->se_fd = -1;
8202657Scsgr			}
8211553Srgrimes		}
8221553Srgrimes		if (sep->se_fd == -1)
8231553Srgrimes			setup(sep);
8241553Srgrimes	}
8251553Srgrimes	endconfig();
8261553Srgrimes	/*
8271553Srgrimes	 * Purge anything not looked at above.
8281553Srgrimes	 */
8291553Srgrimes	omask = sigblock(SIGBLOCK);
8301553Srgrimes	sepp = &servtab;
83119617Sjulian	while ((sep = *sepp)) {
8321553Srgrimes		if (sep->se_checked) {
8331553Srgrimes			sepp = &sep->se_next;
8341553Srgrimes			continue;
8351553Srgrimes		}
8361553Srgrimes		*sepp = sep->se_next;
8371553Srgrimes		if (sep->se_fd >= 0)
8381553Srgrimes			close_sep(sep);
8391553Srgrimes		if (debug)
8401553Srgrimes			print_service("FREE", sep);
8412657Scsgr		if (sep->se_rpc && sep->se_rpc_prog > 0)
8422657Scsgr			unregisterrpc(sep);
8431553Srgrimes		freeconfig(sep);
8441553Srgrimes		free((char *)sep);
8451553Srgrimes	}
8461553Srgrimes	(void) sigsetmask(omask);
8471553Srgrimes}
8481553Srgrimes
8491553Srgrimesvoid
8502657Scsgrunregisterrpc(sep)
8512657Scsgr	struct servtab *sep;
8522657Scsgr{
8532657Scsgr        int i;
8542657Scsgr        struct servtab *sepp;
8552657Scsgr	long omask;
8562657Scsgr
8572657Scsgr	omask = sigblock(SIGBLOCK);
8582657Scsgr        for (sepp = servtab; sepp; sepp = sepp->se_next) {
8592657Scsgr                if (sepp == sep)
8602657Scsgr                        continue;
8612657Scsgr		if (sep->se_checked == 0 ||
8622657Scsgr                    !sepp->se_rpc ||
8632657Scsgr                    sep->se_rpc_prog != sepp->se_rpc_prog)
8642657Scsgr			continue;
8652657Scsgr                return;
8662657Scsgr        }
8672657Scsgr        if (debug)
8682657Scsgr                print_service("UNREG", sep);
8692657Scsgr        for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++)
8702657Scsgr                pmap_unset(sep->se_rpc_prog, i);
8712657Scsgr        if (sep->se_fd != -1)
8722657Scsgr                (void) close(sep->se_fd);
8732657Scsgr        sep->se_fd = -1;
8742657Scsgr	(void) sigsetmask(omask);
8752657Scsgr}
8762657Scsgr
8772657Scsgrvoid
8781553Srgrimesretry(signo)
8791553Srgrimes	int signo;
8801553Srgrimes{
8811553Srgrimes	struct servtab *sep;
8821553Srgrimes
8831553Srgrimes	timingout = 0;
8841553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
88519617Sjulian		if (sep->se_fd == -1 && !ISMUX(sep))
8861553Srgrimes			setup(sep);
8871553Srgrimes}
8881553Srgrimes
8891553Srgrimesvoid
8901553Srgrimessetup(sep)
8911553Srgrimes	struct servtab *sep;
8921553Srgrimes{
8931553Srgrimes	int on = 1;
8941553Srgrimes
8951553Srgrimes	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
8961553Srgrimes		if (debug)
89729602Scharnier			warn("socket failed on %s/%s",
89829602Scharnier				sep->se_service, sep->se_proto);
8991553Srgrimes		syslog(LOG_ERR, "%s/%s: socket: %m",
9001553Srgrimes		    sep->se_service, sep->se_proto);
9011553Srgrimes		return;
9021553Srgrimes	}
9031553Srgrimes#define	turnon(fd, opt) \
9041553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
9051553Srgrimes	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
9061553Srgrimes	    turnon(sep->se_fd, SO_DEBUG) < 0)
9071553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
9081553Srgrimes	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
9091553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
91025253Swollman#ifdef SO_PRIVSTATE
91113956Swollman	if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
91213956Swollman		syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
91325253Swollman#endif
9141553Srgrimes#undef turnon
9151553Srgrimes	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
9161553Srgrimes	    sizeof (sep->se_ctrladdr)) < 0) {
9171553Srgrimes		if (debug)
91829602Scharnier			warn("bind failed on %s/%s",
91929602Scharnier				sep->se_service, sep->se_proto);
9201553Srgrimes		syslog(LOG_ERR, "%s/%s: bind: %m",
9211553Srgrimes		    sep->se_service, sep->se_proto);
9221553Srgrimes		(void) close(sep->se_fd);
9231553Srgrimes		sep->se_fd = -1;
9241553Srgrimes		if (!timingout) {
9251553Srgrimes			timingout = 1;
9261553Srgrimes			alarm(RETRYTIME);
9271553Srgrimes		}
9281553Srgrimes		return;
9291553Srgrimes	}
9302657Scsgr        if (sep->se_rpc) {
9312657Scsgr                int i, len = sizeof(struct sockaddr);
9322657Scsgr
9338857Srgrimes                if (getsockname(sep->se_fd,
9342657Scsgr				(struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
9352657Scsgr                        syslog(LOG_ERR, "%s/%s: getsockname: %m",
9362657Scsgr                               sep->se_service, sep->se_proto);
9372657Scsgr                        (void) close(sep->se_fd);
9382657Scsgr                        sep->se_fd = -1;
9398857Srgrimes                        return;
9402657Scsgr                }
9412657Scsgr                if (debug)
9422657Scsgr                        print_service("REG ", sep);
9432657Scsgr                for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
9442657Scsgr                        pmap_unset(sep->se_rpc_prog, i);
9452657Scsgr                        pmap_set(sep->se_rpc_prog, i,
9462657Scsgr                                 (sep->se_socktype == SOCK_DGRAM)
9472657Scsgr                                 ? IPPROTO_UDP : IPPROTO_TCP,
9482657Scsgr                                 ntohs(sep->se_ctrladdr.sin_port));
9492657Scsgr                }
9508857Srgrimes
9512657Scsgr        }
9521553Srgrimes	if (sep->se_socktype == SOCK_STREAM)
95317197Sdg		listen(sep->se_fd, 64);
95419618Sjulian	enable(sep);
9551553Srgrimes	if (debug) {
95629602Scharnier		warnx("registered %s on %d",
9571553Srgrimes			sep->se_server, sep->se_fd);
9581553Srgrimes	}
9591553Srgrimes}
9601553Srgrimes
9611553Srgrimes/*
9621553Srgrimes * Finish with a service and its socket.
9631553Srgrimes */
9641553Srgrimesvoid
9651553Srgrimesclose_sep(sep)
9661553Srgrimes	struct servtab *sep;
9671553Srgrimes{
9681553Srgrimes	if (sep->se_fd >= 0) {
96919618Sjulian		if (FD_ISSET(sep->se_fd, &allsock))
97019618Sjulian			disable(sep);
9711553Srgrimes		(void) close(sep->se_fd);
9721553Srgrimes		sep->se_fd = -1;
9731553Srgrimes	}
9741553Srgrimes	sep->se_count = 0;
97519618Sjulian	sep->se_numchild = 0;	/* forget about any existing children */
9761553Srgrimes}
9771553Srgrimes
9781553Srgrimesstruct servtab *
9791553Srgrimesenter(cp)
9801553Srgrimes	struct servtab *cp;
9811553Srgrimes{
9821553Srgrimes	struct servtab *sep;
9831553Srgrimes	long omask;
9841553Srgrimes
9851553Srgrimes	sep = (struct servtab *)malloc(sizeof (*sep));
9861553Srgrimes	if (sep == (struct servtab *)0) {
9871553Srgrimes		syslog(LOG_ERR, "Out of memory.");
98819617Sjulian		exit(EX_OSERR);
9891553Srgrimes	}
9901553Srgrimes	*sep = *cp;
9911553Srgrimes	sep->se_fd = -1;
9921553Srgrimes	omask = sigblock(SIGBLOCK);
9931553Srgrimes	sep->se_next = servtab;
9941553Srgrimes	servtab = sep;
9951553Srgrimes	sigsetmask(omask);
9961553Srgrimes	return (sep);
9971553Srgrimes}
9981553Srgrimes
99919618Sjulianvoid
100019618Sjulianenable(struct servtab *sep)
100119618Sjulian{
100219618Sjulian	if (debug)
100329602Scharnier		warnx(
100419618Sjulian		    "enabling %s, fd %d", sep->se_service, sep->se_fd);
100519618Sjulian#ifdef SANITY_CHECK
100619618Sjulian	if (sep->se_fd < 0) {
100719618Sjulian		syslog(LOG_ERR,
100819618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
100919618Sjulian		exit(EX_SOFTWARE);
101019618Sjulian	}
101119618Sjulian	if (ISMUX(sep)) {
101219618Sjulian		syslog(LOG_ERR,
101319618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
101419618Sjulian		exit(EX_SOFTWARE);
101519618Sjulian	}
101619618Sjulian	if (FD_ISSET(sep->se_fd, &allsock)) {
101719618Sjulian		syslog(LOG_ERR,
101819618Sjulian		    "%s: %s: not off", __FUNCTION__, sep->se_service);
101919618Sjulian		exit(EX_SOFTWARE);
102019618Sjulian	}
102119618Sjulian#endif
102219618Sjulian	FD_SET(sep->se_fd, &allsock);
102319618Sjulian	nsock++;
102419618Sjulian	if (sep->se_fd > maxsock)
102519618Sjulian		maxsock = sep->se_fd;
102619618Sjulian}
102719618Sjulian
102819618Sjulianvoid
102919618Sjuliandisable(struct servtab *sep)
103019618Sjulian{
103119618Sjulian	if (debug)
103229602Scharnier		warnx(
103319618Sjulian		    "disabling %s, fd %d", sep->se_service, sep->se_fd);
103419618Sjulian#ifdef SANITY_CHECK
103519618Sjulian	if (sep->se_fd < 0) {
103619618Sjulian		syslog(LOG_ERR,
103719618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
103819618Sjulian		exit(EX_SOFTWARE);
103919618Sjulian	}
104019618Sjulian	if (ISMUX(sep)) {
104119618Sjulian		syslog(LOG_ERR,
104219618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
104319618Sjulian		exit(EX_SOFTWARE);
104419618Sjulian	}
104519618Sjulian	if (!FD_ISSET(sep->se_fd, &allsock)) {
104619618Sjulian		syslog(LOG_ERR,
104719618Sjulian		    "%s: %s: not on", __FUNCTION__, sep->se_service);
104819618Sjulian		exit(EX_SOFTWARE);
104919618Sjulian	}
105019618Sjulian	if (nsock == 0) {
105119618Sjulian		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
105219618Sjulian		exit(EX_SOFTWARE);
105319618Sjulian	}
105419618Sjulian#endif
105519618Sjulian	FD_CLR(sep->se_fd, &allsock);
105619618Sjulian	nsock--;
105719618Sjulian	if (sep->se_fd == maxsock)
105819618Sjulian		maxsock--;
105919618Sjulian}
106019618Sjulian
10611553SrgrimesFILE	*fconfig = NULL;
10621553Srgrimesstruct	servtab serv;
10631553Srgrimeschar	line[LINE_MAX];
10641553Srgrimes
10651553Srgrimesint
10661553Srgrimessetconfig()
10671553Srgrimes{
10681553Srgrimes
10691553Srgrimes	if (fconfig != NULL) {
10701553Srgrimes		fseek(fconfig, 0L, SEEK_SET);
10711553Srgrimes		return (1);
10721553Srgrimes	}
10731553Srgrimes	fconfig = fopen(CONFIG, "r");
10741553Srgrimes	return (fconfig != NULL);
10751553Srgrimes}
10761553Srgrimes
10771553Srgrimesvoid
10781553Srgrimesendconfig()
10791553Srgrimes{
10801553Srgrimes	if (fconfig) {
10811553Srgrimes		(void) fclose(fconfig);
10821553Srgrimes		fconfig = NULL;
10831553Srgrimes	}
10841553Srgrimes}
10851553Srgrimes
10861553Srgrimesstruct servtab *
10871553Srgrimesgetconfigent()
10881553Srgrimes{
10891553Srgrimes	struct servtab *sep = &serv;
10901553Srgrimes	int argc;
109119618Sjulian	char *cp, *arg, *s;
10922657Scsgr	char *versp;
10931553Srgrimes	static char TCPMUX_TOKEN[] = "tcpmux/";
10941553Srgrimes#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
10951553Srgrimes
10961553Srgrimesmore:
10971553Srgrimes	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
10981553Srgrimes		;
10991553Srgrimes	if (cp == NULL)
11001553Srgrimes		return ((struct servtab *)0);
11011553Srgrimes	/*
11021553Srgrimes	 * clear the static buffer, since some fields (se_ctrladdr,
11031553Srgrimes	 * for example) don't get initialized here.
11041553Srgrimes	 */
11051553Srgrimes	memset((caddr_t)sep, 0, sizeof *sep);
11061553Srgrimes	arg = skip(&cp);
11071553Srgrimes	if (cp == NULL) {
11081553Srgrimes		/* got an empty line containing just blanks/tabs. */
11091553Srgrimes		goto more;
11101553Srgrimes	}
11111553Srgrimes	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
11121553Srgrimes		char *c = arg + MUX_LEN;
11131553Srgrimes		if (*c == '+') {
11141553Srgrimes			sep->se_type = MUXPLUS_TYPE;
11151553Srgrimes			c++;
11161553Srgrimes		} else
11171553Srgrimes			sep->se_type = MUX_TYPE;
11181553Srgrimes		sep->se_service = newstr(c);
11191553Srgrimes	} else {
11201553Srgrimes		sep->se_service = newstr(arg);
11211553Srgrimes		sep->se_type = NORM_TYPE;
11221553Srgrimes	}
11231553Srgrimes	arg = sskip(&cp);
11241553Srgrimes	if (strcmp(arg, "stream") == 0)
11251553Srgrimes		sep->se_socktype = SOCK_STREAM;
11261553Srgrimes	else if (strcmp(arg, "dgram") == 0)
11271553Srgrimes		sep->se_socktype = SOCK_DGRAM;
11281553Srgrimes	else if (strcmp(arg, "rdm") == 0)
11291553Srgrimes		sep->se_socktype = SOCK_RDM;
11301553Srgrimes	else if (strcmp(arg, "seqpacket") == 0)
11311553Srgrimes		sep->se_socktype = SOCK_SEQPACKET;
11321553Srgrimes	else if (strcmp(arg, "raw") == 0)
11331553Srgrimes		sep->se_socktype = SOCK_RAW;
11341553Srgrimes	else
11351553Srgrimes		sep->se_socktype = -1;
11361553Srgrimes	sep->se_proto = newstr(sskip(&cp));
11372657Scsgr        if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
113819237Sjoerg                memmove(sep->se_proto, sep->se_proto + 4,
113919237Sjoerg                    strlen(sep->se_proto) + 1 - 4);
11402657Scsgr                sep->se_rpc = 1;
11412657Scsgr                sep->se_rpc_prog = sep->se_rpc_lowvers =
11422657Scsgr			sep->se_rpc_lowvers = 0;
11432657Scsgr                sep->se_ctrladdr.sin_family = AF_INET;
11442657Scsgr                sep->se_ctrladdr.sin_port = 0;
114517482Sjulian                sep->se_ctrladdr.sin_addr = bind_address;
11462657Scsgr                if ((versp = rindex(sep->se_service, '/'))) {
11472657Scsgr                        *versp++ = '\0';
11482657Scsgr                        switch (sscanf(versp, "%d-%d",
11492657Scsgr                                       &sep->se_rpc_lowvers,
11502657Scsgr                                       &sep->se_rpc_highvers)) {
11512657Scsgr                        case 2:
11522657Scsgr                                break;
11532657Scsgr                        case 1:
11542657Scsgr                                sep->se_rpc_highvers =
11552657Scsgr                                        sep->se_rpc_lowvers;
11562657Scsgr                                break;
11572657Scsgr                        default:
11588857Srgrimes                                syslog(LOG_ERR,
11598857Srgrimes					"bad RPC version specifier; %s\n",
11602657Scsgr					sep->se_service);
11612657Scsgr                                freeconfig(sep);
11622657Scsgr                                goto more;
11632657Scsgr                        }
11642657Scsgr                }
11652657Scsgr                else {
11662657Scsgr                        sep->se_rpc_lowvers =
11672657Scsgr                                sep->se_rpc_highvers = 1;
11682657Scsgr                }
11692657Scsgr        }
11701553Srgrimes	arg = sskip(&cp);
117119618Sjulian	if (!strncmp(arg, "wait", 4))
117219618Sjulian		sep->se_accept = 0;
117319618Sjulian	else if (!strncmp(arg, "nowait", 6))
117419618Sjulian		sep->se_accept = 1;
117519618Sjulian	else {
117619618Sjulian		syslog(LOG_ERR,
117719618Sjulian			"%s: bad wait/nowait for service %s",
117819618Sjulian			CONFIG, sep->se_service);
117919618Sjulian		goto more;
118019618Sjulian	}
118133794Spst	sep->se_maxchild = maxchild;
118233794Spst	sep->se_maxcpm = maxcpm;
118319618Sjulian	if ((s = strchr(arg, '/')) != NULL) {
118419618Sjulian		char *eptr;
118519618Sjulian		u_long val;
118619618Sjulian
118719618Sjulian		val = strtoul(s + 1, &eptr, 10);
118830847Sdima		if (eptr == s + 1 || val > MAX_MAXCHLD) {
118919618Sjulian			syslog(LOG_ERR,
119019618Sjulian				"%s: bad max-child for service %s",
119119618Sjulian				CONFIG, sep->se_service);
119219618Sjulian			goto more;
119319618Sjulian		}
119419618Sjulian		sep->se_maxchild = val;
119530847Sdima		if (*eptr == '/')
119630847Sdima			sep->se_maxcpm = strtol(eptr + 1, &eptr, 10);
119730847Sdima		/*
119830847Sdima		 * explicitly do not check for \0 for future expansion /
119930847Sdima		 * backwards compatibility
120030847Sdima		 */
120119618Sjulian	}
12021553Srgrimes	if (ISMUX(sep)) {
12031553Srgrimes		/*
120419618Sjulian		 * Silently enforce "nowait" mode for TCPMUX services
120519618Sjulian		 * since they don't have an assigned port to listen on.
12061553Srgrimes		 */
120719618Sjulian		sep->se_accept = 1;
12081553Srgrimes		if (strcmp(sep->se_proto, "tcp")) {
12098857Srgrimes			syslog(LOG_ERR,
12101553Srgrimes				"%s: bad protocol for tcpmux service %s",
12111553Srgrimes				CONFIG, sep->se_service);
12121553Srgrimes			goto more;
12131553Srgrimes		}
12141553Srgrimes		if (sep->se_socktype != SOCK_STREAM) {
12158857Srgrimes			syslog(LOG_ERR,
12161553Srgrimes				"%s: bad socket type for tcpmux service %s",
12171553Srgrimes				CONFIG, sep->se_service);
12181553Srgrimes			goto more;
12191553Srgrimes		}
12201553Srgrimes	}
12211553Srgrimes	sep->se_user = newstr(sskip(&cp));
122230792Sache#ifdef LOGIN_CAP
122330792Sache	if ((s = strrchr(sep->se_user, '/')) != NULL) {
122430792Sache		*s = '\0';
122530792Sache		sep->se_class = newstr(s + 1);
122630792Sache	} else
122730792Sache		sep->se_class = newstr(RESOURCE_RC);
122830792Sache#endif
122930807Sache	if ((s = strrchr(sep->se_user, ':')) != NULL) {
123030807Sache		*s = '\0';
123130807Sache		sep->se_group = newstr(s + 1);
123230807Sache	} else
123330807Sache		sep->se_group = NULL;
12341553Srgrimes	sep->se_server = newstr(sskip(&cp));
12351553Srgrimes	if (strcmp(sep->se_server, "internal") == 0) {
12361553Srgrimes		struct biltin *bi;
12371553Srgrimes
12381553Srgrimes		for (bi = biltins; bi->bi_service; bi++)
12391553Srgrimes			if (bi->bi_socktype == sep->se_socktype &&
12401553Srgrimes			    strcmp(bi->bi_service, sep->se_service) == 0)
12411553Srgrimes				break;
12421553Srgrimes		if (bi->bi_service == 0) {
12431553Srgrimes			syslog(LOG_ERR, "internal service %s unknown",
12441553Srgrimes				sep->se_service);
12451553Srgrimes			goto more;
12461553Srgrimes		}
124719618Sjulian		sep->se_accept = 1;	/* force accept mode for built-ins */
12481553Srgrimes		sep->se_bi = bi;
12491553Srgrimes	} else
12501553Srgrimes		sep->se_bi = NULL;
125119618Sjulian	if (sep->se_maxchild < 0)	/* apply default max-children */
125219618Sjulian		if (sep->se_bi)
125319618Sjulian			sep->se_maxchild = sep->se_bi->bi_maxchild;
125419618Sjulian		else
125519618Sjulian			sep->se_maxchild = sep->se_accept ? 0 : 1;
125619618Sjulian	if (sep->se_maxchild) {
125719618Sjulian		sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
125819618Sjulian		if (sep->se_pids == NULL) {
125919618Sjulian			syslog(LOG_ERR, "Out of memory.");
126019618Sjulian			exit(EX_OSERR);
126119618Sjulian		}
126219618Sjulian	}
12631553Srgrimes	argc = 0;
12641553Srgrimes	for (arg = skip(&cp); cp; arg = skip(&cp))
126519618Sjulian		if (argc < MAXARGV) {
12661553Srgrimes			sep->se_argv[argc++] = newstr(arg);
126719618Sjulian		} else {
126819618Sjulian			syslog(LOG_ERR,
126919618Sjulian				"%s: too many arguments for service %s",
127019618Sjulian				CONFIG, sep->se_service);
127119618Sjulian			goto more;
127219618Sjulian		}
12731553Srgrimes	while (argc <= MAXARGV)
12741553Srgrimes		sep->se_argv[argc++] = NULL;
12751553Srgrimes	return (sep);
12761553Srgrimes}
12771553Srgrimes
12781553Srgrimesvoid
12791553Srgrimesfreeconfig(cp)
12801553Srgrimes	struct servtab *cp;
12811553Srgrimes{
12821553Srgrimes	int i;
12831553Srgrimes
12841553Srgrimes	if (cp->se_service)
12851553Srgrimes		free(cp->se_service);
12861553Srgrimes	if (cp->se_proto)
12871553Srgrimes		free(cp->se_proto);
12881553Srgrimes	if (cp->se_user)
12891553Srgrimes		free(cp->se_user);
129030807Sache	if (cp->se_group)
129130807Sache		free(cp->se_group);
129230792Sache#ifdef LOGIN_CAP
129330792Sache	if (cp->se_class)
129430792Sache		free(cp->se_class);
129530792Sache#endif
12961553Srgrimes	if (cp->se_server)
12971553Srgrimes		free(cp->se_server);
129819618Sjulian	if (cp->se_pids)
129919618Sjulian		free(cp->se_pids);
13001553Srgrimes	for (i = 0; i < MAXARGV; i++)
13011553Srgrimes		if (cp->se_argv[i])
13021553Srgrimes			free(cp->se_argv[i]);
13031553Srgrimes}
13041553Srgrimes
13051553Srgrimes
13061553Srgrimes/*
13071553Srgrimes * Safe skip - if skip returns null, log a syntax error in the
13081553Srgrimes * configuration file and exit.
13091553Srgrimes */
13101553Srgrimeschar *
13111553Srgrimessskip(cpp)
13121553Srgrimes	char **cpp;
13131553Srgrimes{
13141553Srgrimes	char *cp;
13151553Srgrimes
13161553Srgrimes	cp = skip(cpp);
13171553Srgrimes	if (cp == NULL) {
13181553Srgrimes		syslog(LOG_ERR, "%s: syntax error", CONFIG);
131919617Sjulian		exit(EX_DATAERR);
13201553Srgrimes	}
13211553Srgrimes	return (cp);
13221553Srgrimes}
13231553Srgrimes
13241553Srgrimeschar *
13251553Srgrimesskip(cpp)
13261553Srgrimes	char **cpp;
13271553Srgrimes{
13281553Srgrimes	char *cp = *cpp;
13291553Srgrimes	char *start;
133011933Sadam	char quote = '\0';
13311553Srgrimes
13321553Srgrimesagain:
13331553Srgrimes	while (*cp == ' ' || *cp == '\t')
13341553Srgrimes		cp++;
13351553Srgrimes	if (*cp == '\0') {
13361553Srgrimes		int c;
13371553Srgrimes
13381553Srgrimes		c = getc(fconfig);
13391553Srgrimes		(void) ungetc(c, fconfig);
13401553Srgrimes		if (c == ' ' || c == '\t')
134119617Sjulian			if ((cp = nextline(fconfig)))
13421553Srgrimes				goto again;
13431553Srgrimes		*cpp = (char *)0;
13441553Srgrimes		return ((char *)0);
13451553Srgrimes	}
134611933Sadam	if (*cp == '"' || *cp == '\'')
134711933Sadam		quote = *cp++;
13481553Srgrimes	start = cp;
134911933Sadam	if (quote)
135011933Sadam		while (*cp && *cp != quote)
135111933Sadam			cp++;
135211933Sadam	else
135311933Sadam		while (*cp && *cp != ' ' && *cp != '\t')
135411933Sadam			cp++;
13551553Srgrimes	if (*cp != '\0')
13561553Srgrimes		*cp++ = '\0';
13571553Srgrimes	*cpp = cp;
13581553Srgrimes	return (start);
13591553Srgrimes}
13601553Srgrimes
13611553Srgrimeschar *
13621553Srgrimesnextline(fd)
13631553Srgrimes	FILE *fd;
13641553Srgrimes{
13651553Srgrimes	char *cp;
13661553Srgrimes
13671553Srgrimes	if (fgets(line, sizeof (line), fd) == NULL)
13681553Srgrimes		return ((char *)0);
13691553Srgrimes	cp = strchr(line, '\n');
13701553Srgrimes	if (cp)
13711553Srgrimes		*cp = '\0';
13721553Srgrimes	return (line);
13731553Srgrimes}
13741553Srgrimes
13751553Srgrimeschar *
13761553Srgrimesnewstr(cp)
13771553Srgrimes	char *cp;
13781553Srgrimes{
137919617Sjulian	if ((cp = strdup(cp ? cp : "")))
13801553Srgrimes		return (cp);
13811553Srgrimes	syslog(LOG_ERR, "strdup: %m");
138219617Sjulian	exit(EX_OSERR);
13831553Srgrimes}
13841553Srgrimes
138513142Speter#ifdef OLD_SETPROCTITLE
13861553Srgrimesvoid
138713142Speterinetd_setproctitle(a, s)
13881553Srgrimes	char *a;
13891553Srgrimes	int s;
13901553Srgrimes{
13911553Srgrimes	int size;
13921553Srgrimes	char *cp;
13931553Srgrimes	struct sockaddr_in sin;
13941553Srgrimes	char buf[80];
13951553Srgrimes
13961553Srgrimes	cp = Argv[0];
13971553Srgrimes	size = sizeof(sin);
13981553Srgrimes	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
13998857Srgrimes		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
14001553Srgrimes	else
14018857Srgrimes		(void) sprintf(buf, "-%s", a);
14021553Srgrimes	strncpy(cp, buf, LastArg - cp);
14031553Srgrimes	cp += strlen(cp);
14041553Srgrimes	while (cp < LastArg)
14051553Srgrimes		*cp++ = ' ';
14061553Srgrimes}
140713142Speter#else
140813142Spetervoid
140913142Speterinetd_setproctitle(a, s)
141013142Speter	char *a;
141113142Speter	int s;
141213142Speter{
141313142Speter	int size;
141413142Speter	struct sockaddr_in sin;
141513142Speter	char buf[80];
14161553Srgrimes
141713142Speter	size = sizeof(sin);
141813142Speter	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
141913142Speter		(void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr));
142013142Speter	else
142113142Speter		(void) sprintf(buf, "%s", a);
142213142Speter	setproctitle("%s", buf);
142313142Speter}
142413142Speter#endif
142513142Speter
142613142Speter
14271553Srgrimes/*
14281553Srgrimes * Internet services provided internally by inetd:
14291553Srgrimes */
14301553Srgrimes#define	BUFSIZE	8192
14311553Srgrimes
14321553Srgrimes/* ARGSUSED */
14331553Srgrimesvoid
14341553Srgrimesecho_stream(s, sep)		/* Echo service -- echo data back */
14351553Srgrimes	int s;
14361553Srgrimes	struct servtab *sep;
14371553Srgrimes{
14381553Srgrimes	char buffer[BUFSIZE];
14391553Srgrimes	int i;
14401553Srgrimes
144113142Speter	inetd_setproctitle(sep->se_service, s);
14421553Srgrimes	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
14431553Srgrimes	    write(s, buffer, i) > 0)
14441553Srgrimes		;
14451553Srgrimes	exit(0);
14461553Srgrimes}
14471553Srgrimes
14485182Swollmanint check_loop(sin, sep)
14495182Swollman	struct sockaddr_in *sin;
14505182Swollman	struct servtab *sep;
14515182Swollman{
14525182Swollman	struct servtab *se2;
14535182Swollman
14545182Swollman	for (se2 = servtab; se2; se2 = se2->se_next) {
14555182Swollman		if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
14565182Swollman			continue;
14575182Swollman
14585182Swollman		if (sin->sin_port == se2->se_ctrladdr.sin_port) {
14595182Swollman			syslog(LOG_WARNING,
14605182Swollman			       "%s/%s:%s/%s loop request REFUSED from %s",
14618857Srgrimes			       sep->se_service, sep->se_proto,
14625182Swollman			       se2->se_service, se2->se_proto,
14635182Swollman			       inet_ntoa(sin->sin_addr));
14645182Swollman			return 1;
14655182Swollman		}
14665182Swollman	}
14675182Swollman	return 0;
14685182Swollman}
14695182Swollman
14701553Srgrimes/* ARGSUSED */
14711553Srgrimesvoid
14721553Srgrimesecho_dg(s, sep)			/* Echo service -- echo data back */
14731553Srgrimes	int s;
14741553Srgrimes	struct servtab *sep;
14751553Srgrimes{
14761553Srgrimes	char buffer[BUFSIZE];
14771553Srgrimes	int i, size;
14785182Swollman	struct sockaddr_in sin;
14791553Srgrimes
14805182Swollman	size = sizeof(sin);
14818857Srgrimes	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
14825182Swollman			  (struct sockaddr *)&sin, &size)) < 0)
14831553Srgrimes		return;
14845182Swollman
14855182Swollman	if (check_loop(&sin, sep))
14865182Swollman		return;
14875182Swollman
14885182Swollman	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin,
14895182Swollman		      sizeof(sin));
14901553Srgrimes}
14911553Srgrimes
14921553Srgrimes/* ARGSUSED */
14931553Srgrimesvoid
14941553Srgrimesdiscard_stream(s, sep)		/* Discard service -- ignore data */
14951553Srgrimes	int s;
14961553Srgrimes	struct servtab *sep;
14971553Srgrimes{
14981553Srgrimes	int ret;
14991553Srgrimes	char buffer[BUFSIZE];
15001553Srgrimes
150113142Speter	inetd_setproctitle(sep->se_service, s);
15021553Srgrimes	while (1) {
15031553Srgrimes		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
15041553Srgrimes			;
15051553Srgrimes		if (ret == 0 || errno != EINTR)
15061553Srgrimes			break;
15071553Srgrimes	}
15081553Srgrimes	exit(0);
15091553Srgrimes}
15101553Srgrimes
15111553Srgrimes/* ARGSUSED */
15121553Srgrimesvoid
15131553Srgrimesdiscard_dg(s, sep)		/* Discard service -- ignore data */
15141553Srgrimes	int s;
15151553Srgrimes	struct servtab *sep;
15161553Srgrimes{
15171553Srgrimes	char buffer[BUFSIZE];
15181553Srgrimes
15191553Srgrimes	(void) read(s, buffer, sizeof(buffer));
15201553Srgrimes}
15211553Srgrimes
15221553Srgrimes#include <ctype.h>
15231553Srgrimes#define LINESIZ 72
15241553Srgrimeschar ring[128];
15251553Srgrimeschar *endring;
15261553Srgrimes
15271553Srgrimesvoid
15281553Srgrimesinitring()
15291553Srgrimes{
15301553Srgrimes	int i;
15311553Srgrimes
15321553Srgrimes	endring = ring;
15331553Srgrimes
15341553Srgrimes	for (i = 0; i <= 128; ++i)
15351553Srgrimes		if (isprint(i))
15361553Srgrimes			*endring++ = i;
15371553Srgrimes}
15381553Srgrimes
15391553Srgrimes/* ARGSUSED */
15401553Srgrimesvoid
15411553Srgrimeschargen_stream(s, sep)		/* Character generator */
15421553Srgrimes	int s;
15431553Srgrimes	struct servtab *sep;
15441553Srgrimes{
15451553Srgrimes	int len;
15461553Srgrimes	char *rs, text[LINESIZ+2];
15471553Srgrimes
154813142Speter	inetd_setproctitle(sep->se_service, s);
15491553Srgrimes
15501553Srgrimes	if (!endring) {
15511553Srgrimes		initring();
15521553Srgrimes		rs = ring;
15531553Srgrimes	}
15541553Srgrimes
15551553Srgrimes	text[LINESIZ] = '\r';
15561553Srgrimes	text[LINESIZ + 1] = '\n';
15571553Srgrimes	for (rs = ring;;) {
15581553Srgrimes		if ((len = endring - rs) >= LINESIZ)
15591553Srgrimes			memmove(text, rs, LINESIZ);
15601553Srgrimes		else {
15611553Srgrimes			memmove(text, rs, len);
15621553Srgrimes			memmove(text + len, ring, LINESIZ - len);
15631553Srgrimes		}
15641553Srgrimes		if (++rs == endring)
15651553Srgrimes			rs = ring;
15661553Srgrimes		if (write(s, text, sizeof(text)) != sizeof(text))
15671553Srgrimes			break;
15681553Srgrimes	}
15691553Srgrimes	exit(0);
15701553Srgrimes}
15711553Srgrimes
15721553Srgrimes/* ARGSUSED */
15731553Srgrimesvoid
15741553Srgrimeschargen_dg(s, sep)		/* Character generator */
15751553Srgrimes	int s;
15761553Srgrimes	struct servtab *sep;
15771553Srgrimes{
15785182Swollman	struct sockaddr_in sin;
15791553Srgrimes	static char *rs;
15801553Srgrimes	int len, size;
15811553Srgrimes	char text[LINESIZ+2];
15821553Srgrimes
15831553Srgrimes	if (endring == 0) {
15841553Srgrimes		initring();
15851553Srgrimes		rs = ring;
15861553Srgrimes	}
15871553Srgrimes
15885182Swollman	size = sizeof(sin);
15898857Srgrimes	if (recvfrom(s, text, sizeof(text), 0,
15905182Swollman		     (struct sockaddr *)&sin, &size) < 0)
15911553Srgrimes		return;
15921553Srgrimes
15935182Swollman	if (check_loop(&sin, sep))
15945182Swollman		return;
15955182Swollman
15961553Srgrimes	if ((len = endring - rs) >= LINESIZ)
15971553Srgrimes		memmove(text, rs, LINESIZ);
15981553Srgrimes	else {
15991553Srgrimes		memmove(text, rs, len);
16001553Srgrimes		memmove(text + len, ring, LINESIZ - len);
16011553Srgrimes	}
16021553Srgrimes	if (++rs == endring)
16031553Srgrimes		rs = ring;
16041553Srgrimes	text[LINESIZ] = '\r';
16051553Srgrimes	text[LINESIZ + 1] = '\n';
16068857Srgrimes	(void) sendto(s, text, sizeof(text), 0,
16075182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
16081553Srgrimes}
16091553Srgrimes
16101553Srgrimes/*
16111553Srgrimes * Return a machine readable date and time, in the form of the
16121553Srgrimes * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
16131553Srgrimes * returns the number of seconds since midnight, Jan 1, 1970,
16141553Srgrimes * we must add 2208988800 seconds to this figure to make up for
16151553Srgrimes * some seventy years Bell Labs was asleep.
16161553Srgrimes */
16171553Srgrimes
16181553Srgrimeslong
16191553Srgrimesmachtime()
16201553Srgrimes{
16211553Srgrimes	struct timeval tv;
16221553Srgrimes
16231553Srgrimes	if (gettimeofday(&tv, (struct timezone *)0) < 0) {
16241553Srgrimes		if (debug)
162529602Scharnier			warnx("unable to get time of day");
16261553Srgrimes		return (0L);
16271553Srgrimes	}
16281553Srgrimes#define	OFFSET ((u_long)25567 * 24*60*60)
16291553Srgrimes	return (htonl((long)(tv.tv_sec + OFFSET)));
16301553Srgrimes#undef OFFSET
16311553Srgrimes}
16321553Srgrimes
16331553Srgrimes/* ARGSUSED */
16341553Srgrimesvoid
16351553Srgrimesmachtime_stream(s, sep)
16361553Srgrimes	int s;
16371553Srgrimes	struct servtab *sep;
16381553Srgrimes{
16391553Srgrimes	long result;
16401553Srgrimes
16411553Srgrimes	result = machtime();
16421553Srgrimes	(void) write(s, (char *) &result, sizeof(result));
16431553Srgrimes}
16441553Srgrimes
16451553Srgrimes/* ARGSUSED */
16461553Srgrimesvoid
16471553Srgrimesmachtime_dg(s, sep)
16481553Srgrimes	int s;
16491553Srgrimes	struct servtab *sep;
16501553Srgrimes{
16511553Srgrimes	long result;
16525182Swollman	struct sockaddr_in sin;
16531553Srgrimes	int size;
16541553Srgrimes
16555182Swollman	size = sizeof(sin);
16568857Srgrimes	if (recvfrom(s, (char *)&result, sizeof(result), 0,
16575182Swollman		     (struct sockaddr *)&sin, &size) < 0)
16581553Srgrimes		return;
16595182Swollman
16605182Swollman	if (check_loop(&sin, sep))
16615182Swollman		return;
16625182Swollman
16631553Srgrimes	result = machtime();
16648857Srgrimes	(void) sendto(s, (char *) &result, sizeof(result), 0,
16655182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
16661553Srgrimes}
16671553Srgrimes
16681553Srgrimes/* ARGSUSED */
16691553Srgrimesvoid
16701553Srgrimesdaytime_stream(s, sep)		/* Return human-readable time of day */
16711553Srgrimes	int s;
16721553Srgrimes	struct servtab *sep;
16731553Srgrimes{
16741553Srgrimes	char buffer[256];
16751553Srgrimes	time_t clock;
16761553Srgrimes
16771553Srgrimes	clock = time((time_t *) 0);
16781553Srgrimes
16791553Srgrimes	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
16801553Srgrimes	(void) write(s, buffer, strlen(buffer));
16811553Srgrimes}
16821553Srgrimes
16831553Srgrimes/* ARGSUSED */
16841553Srgrimesvoid
16851553Srgrimesdaytime_dg(s, sep)		/* Return human-readable time of day */
16861553Srgrimes	int s;
16871553Srgrimes	struct servtab *sep;
16881553Srgrimes{
16891553Srgrimes	char buffer[256];
16901553Srgrimes	time_t clock;
16915182Swollman	struct sockaddr_in sin;
16921553Srgrimes	int size;
16931553Srgrimes
16941553Srgrimes	clock = time((time_t *) 0);
16951553Srgrimes
16965182Swollman	size = sizeof(sin);
16978857Srgrimes	if (recvfrom(s, buffer, sizeof(buffer), 0,
16985182Swollman		     (struct sockaddr *)&sin, &size) < 0)
16991553Srgrimes		return;
17005182Swollman
17015182Swollman	if (check_loop(&sin, sep))
17025182Swollman		return;
17035182Swollman
17041553Srgrimes	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
17058857Srgrimes	(void) sendto(s, buffer, strlen(buffer), 0,
17065182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
17071553Srgrimes}
17081553Srgrimes
17091553Srgrimes/*
17101553Srgrimes * print_service:
17111553Srgrimes *	Dump relevant information to stderr
17121553Srgrimes */
17131553Srgrimesvoid
17141553Srgrimesprint_service(action, sep)
17151553Srgrimes	char *action;
17161553Srgrimes	struct servtab *sep;
17171553Srgrimes{
171819617Sjulian	fprintf(stderr,
171930792Sache#ifdef LOGIN_CAP
172030807Sache	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%x server=%s\n",
172130792Sache#else
172230807Sache	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%x server=%s\n",
172330792Sache#endif
172419617Sjulian	    action, sep->se_service, sep->se_proto,
172530807Sache	    sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group,
172630792Sache#ifdef LOGIN_CAP
172730792Sache	    sep->se_class,
172830792Sache#endif
172919617Sjulian	    (int)sep->se_bi, sep->se_server);
17301553Srgrimes}
17311553Srgrimes
17321553Srgrimes/*
17331553Srgrimes *  Based on TCPMUX.C by Mark K. Lottor November 1988
17341553Srgrimes *  sri-nic::ps:<mkl>tcpmux.c
17351553Srgrimes */
17361553Srgrimes
17371553Srgrimes
17381553Srgrimesstatic int		/* # of characters upto \r,\n or \0 */
17391553Srgrimesgetline(fd, buf, len)
17401553Srgrimes	int fd;
17411553Srgrimes	char *buf;
17421553Srgrimes	int len;
17431553Srgrimes{
17441553Srgrimes	int count = 0, n;
17451553Srgrimes
17461553Srgrimes	do {
17471553Srgrimes		n = read(fd, buf, len-count);
17481553Srgrimes		if (n == 0)
17491553Srgrimes			return (count);
17501553Srgrimes		if (n < 0)
17511553Srgrimes			return (-1);
17521553Srgrimes		while (--n >= 0) {
17531553Srgrimes			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
17541553Srgrimes				return (count);
17551553Srgrimes			count++;
17561553Srgrimes			buf++;
17571553Srgrimes		}
17581553Srgrimes	} while (count < len);
17591553Srgrimes	return (count);
17601553Srgrimes}
17611553Srgrimes
17621553Srgrimes#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
17631553Srgrimes
17641553Srgrimes#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
17651553Srgrimes
17661553Srgrimesstruct servtab *
17671553Srgrimestcpmux(s)
17681553Srgrimes	int s;
17691553Srgrimes{
17701553Srgrimes	struct servtab *sep;
17711553Srgrimes	char service[MAX_SERV_LEN+1];
17721553Srgrimes	int len;
17731553Srgrimes
17741553Srgrimes	/* Get requested service name */
17751553Srgrimes	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
17761553Srgrimes		strwrite(s, "-Error reading service name\r\n");
17771553Srgrimes		return (NULL);
17781553Srgrimes	}
17791553Srgrimes	service[len] = '\0';
17801553Srgrimes
17811553Srgrimes	if (debug)
178229602Scharnier		warnx("tcpmux: someone wants %s", service);
17831553Srgrimes
17841553Srgrimes	/*
17851553Srgrimes	 * Help is a required command, and lists available services,
17861553Srgrimes	 * one per line.
17871553Srgrimes	 */
17881553Srgrimes	if (!strcasecmp(service, "help")) {
17891553Srgrimes		for (sep = servtab; sep; sep = sep->se_next) {
17901553Srgrimes			if (!ISMUX(sep))
17911553Srgrimes				continue;
17921553Srgrimes			(void)write(s,sep->se_service,strlen(sep->se_service));
17931553Srgrimes			strwrite(s, "\r\n");
17941553Srgrimes		}
17951553Srgrimes		return (NULL);
17961553Srgrimes	}
17971553Srgrimes
17981553Srgrimes	/* Try matching a service in inetd.conf with the request */
17991553Srgrimes	for (sep = servtab; sep; sep = sep->se_next) {
18001553Srgrimes		if (!ISMUX(sep))
18011553Srgrimes			continue;
18021553Srgrimes		if (!strcasecmp(service, sep->se_service)) {
18031553Srgrimes			if (ISMUXPLUS(sep)) {
18041553Srgrimes				strwrite(s, "+Go\r\n");
18051553Srgrimes			}
18061553Srgrimes			return (sep);
18071553Srgrimes		}
18081553Srgrimes	}
18091553Srgrimes	strwrite(s, "-Service not available\r\n");
18101553Srgrimes	return (NULL);
18111553Srgrimes}
181230847Sdima
181330847Sdima#define CPMHSIZE	256
181430847Sdima#define CPMHMASK	(CPMHSIZE-1)
181530847Sdima#define CHTGRAN		10
181630847Sdima#define CHTSIZE		6
181730847Sdima
181830847Sdimatypedef struct CTime {
181930847Sdima	unsigned long 	ct_Ticks;
182030847Sdima	int		ct_Count;
182130847Sdima} CTime;
182230847Sdima
182330847Sdimatypedef struct CHash {
182430847Sdima	struct in_addr	ch_Addr;
182530847Sdima	time_t		ch_LTime;
182630847Sdima	char		*ch_Service;
182730847Sdima	CTime		ch_Times[CHTSIZE];
182830847Sdima} CHash;
182930847Sdima
183030847SdimaCHash	CHashAry[CPMHSIZE];
183130847Sdima
183230847Sdimaint
183330847Sdimacpmip(sep, ctrl)
183430847Sdima	struct servtab *sep;
183530847Sdima	int ctrl;
183630847Sdima{
183730847Sdima	struct sockaddr_in rsin;
183830847Sdima	int rsinLen = sizeof(rsin);
183930847Sdima	int r = 0;
184030847Sdima
184130847Sdima	/*
184230847Sdima	 * If getpeername() fails, just let it through (if logging is
184330847Sdima	 * enabled the condition is caught elsewhere)
184430847Sdima	 */
184530847Sdima
184630847Sdima	if (sep->se_maxcpm > 0 &&
184730847Sdima	    getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 0 ) {
184830847Sdima		time_t t = time(NULL);
184930847Sdima		int hv = 0xABC3D20F;
185030847Sdima		int i;
185130847Sdima		int cnt = 0;
185230847Sdima		CHash *chBest = NULL;
185330847Sdima		unsigned int ticks = t / CHTGRAN;
185430847Sdima
185530847Sdima		{
185630847Sdima			char *p;
185730847Sdima			int i;
185830847Sdima
185930847Sdima			for (i = 0, p = (char *)&rsin.sin_addr;
186030847Sdima			    i < sizeof(rsin.sin_addr);
186130847Sdima			    ++i, ++p) {
186230847Sdima				hv = (hv << 5) ^ (hv >> 23) ^ *p;
186330847Sdima			}
186430847Sdima			hv = (hv ^ (hv >> 16));
186530847Sdima		}
186630847Sdima		for (i = 0; i < 5; ++i) {
186730847Sdima			CHash *ch = &CHashAry[(hv + i) & CPMHMASK];
186830847Sdima
186930847Sdima			if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr &&
187030847Sdima			    ch->ch_Service && strcmp(sep->se_service,
187130847Sdima			    ch->ch_Service) == 0) {
187230847Sdima				chBest = ch;
187330847Sdima				break;
187430847Sdima			}
187530847Sdima			if (chBest == NULL || ch->ch_LTime == 0 ||
187630847Sdima			    ch->ch_LTime < chBest->ch_LTime) {
187730847Sdima				chBest = ch;
187830847Sdima			}
187930847Sdima		}
188030847Sdima		if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr ||
188130847Sdima		    chBest->ch_Service == NULL ||
188230847Sdima		    strcmp(sep->se_service, chBest->ch_Service) != 0) {
188330847Sdima			chBest->ch_Addr = rsin.sin_addr;
188430847Sdima			if (chBest->ch_Service)
188530847Sdima				free(chBest->ch_Service);
188630847Sdima			chBest->ch_Service = strdup(sep->se_service);
188730847Sdima			bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
188830847Sdima		}
188930847Sdima		chBest->ch_LTime = t;
189030847Sdima		{
189130847Sdima			CTime *ct = &chBest->ch_Times[ticks % CHTSIZE];
189230847Sdima			if (ct->ct_Ticks != ticks) {
189330847Sdima				ct->ct_Ticks = ticks;
189430847Sdima				ct->ct_Count = 0;
189530847Sdima			}
189630847Sdima			++ct->ct_Count;
189730847Sdima		}
189830847Sdima		for (i = 0; i < CHTSIZE; ++i) {
189930847Sdima			CTime *ct = &chBest->ch_Times[i];
190030847Sdima			if (ct->ct_Ticks <= ticks &&
190130847Sdima			    ct->ct_Ticks >= ticks - CHTSIZE) {
190230847Sdima				cnt += ct->ct_Count;
190330847Sdima			}
190430847Sdima		}
190530847Sdima		if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) {
190630847Sdima			r = -1;
190730847Sdima			syslog(LOG_ERR,
190833794Spst			    "%s from %s exceeded counts/min (limit %d/min)",
190933794Spst			    sep->se_service, inet_ntoa(rsin.sin_addr),
191033794Spst			    sep->se_maxcpm);
191130847Sdima		}
191230847Sdima	}
191330847Sdima	return(r);
191430847Sdima}
1915