inetd.c revision 45089
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[] =
4545089Smarkm	"$Id: inetd.c,v 1.46 1999/01/05 11:56:35 danny 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>
11536042Sguido#include <netinet/tcp.h>
1161553Srgrimes#include <arpa/inet.h>
1172657Scsgr#include <rpc/rpc.h>
11819617Sjulian#include <rpc/pmap_clnt.h>
1191553Srgrimes
1201553Srgrimes#include <errno.h>
12129602Scharnier#include <err.h>
1221553Srgrimes#include <fcntl.h>
12330807Sache#include <grp.h>
1241553Srgrimes#include <netdb.h>
1251553Srgrimes#include <pwd.h>
1261553Srgrimes#include <signal.h>
1271553Srgrimes#include <stdio.h>
1281553Srgrimes#include <stdlib.h>
1291553Srgrimes#include <string.h>
1301553Srgrimes#include <syslog.h>
1311553Srgrimes#include <unistd.h>
13213142Speter#include <libutil.h>
13319617Sjulian#include <sysexits.h>
1341553Srgrimes
13545089Smarkm#ifdef LIBWRAP
13645089Smarkm# include <tcpd.h>
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
14945089Smarkmint allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
15045089Smarkmint deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
15145089Smarkm#endif
15245089Smarkm
15321640Speter#ifdef LOGIN_CAP
15421640Speter#include <login_cap.h>
15530792Sache
15630792Sache/* see init.c */
15730792Sache#define RESOURCE_RC "daemon"
15830792Sache
15921640Speter#endif
16021640Speter
1611553Srgrimes#include "pathnames.h"
1621553Srgrimes
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
1811553Srgrimesint	debug = 0;
1822659Scsgrint	log = 0;
1831553Srgrimesint	nsock, maxsock;
1841553Srgrimesfd_set	allsock;
1851553Srgrimesint	options;
1861553Srgrimesint	timingout;
1871553Srgrimesint	toomany = TOOMANY;
18833794Spstint	maxchild = MAXCPM;
18933794Spstint	maxcpm = MAXCHILD;
1901553Srgrimesstruct	servent *sp;
1912657Scsgrstruct	rpcent *rpc;
19217482Sjulianstruct	in_addr bind_address;
19342122Sdesint	signalpipe[2];
1941553Srgrimes
1951553Srgrimesstruct	servtab {
1961553Srgrimes	char	*se_service;		/* name of service */
1971553Srgrimes	int	se_socktype;		/* type of socket to use */
1981553Srgrimes	char	*se_proto;		/* protocol used */
19930847Sdima	int	se_maxchild;		/* max number of children */
20030847Sdima	int	se_maxcpm;		/* max connects per IP per minute */
20130847Sdima	int	se_numchild;		/* current number of children */
20219618Sjulian	pid_t	*se_pids;		/* array of child pids */
2031553Srgrimes	char	*se_user;		/* user name to run as */
20430807Sache	char    *se_group;              /* group name to run as */
20530792Sache#ifdef  LOGIN_CAP
20630792Sache	char    *se_class;              /* login class name to run with */
20730792Sache#endif
2081553Srgrimes	struct	biltin *se_bi;		/* if built-in, description */
2091553Srgrimes	char	*se_server;		/* server program */
2101553Srgrimes#define	MAXARGV 20
2111553Srgrimes	char	*se_argv[MAXARGV+1];	/* program arguments */
2121553Srgrimes	int	se_fd;			/* open descriptor */
2131553Srgrimes	struct	sockaddr_in se_ctrladdr;/* bound address */
21419618Sjulian	u_char	se_type;		/* type: normal, mux, or mux+ */
21519618Sjulian	u_char	se_checked;		/* looked at during merge */
21619618Sjulian	u_char	se_accept;		/* i.e., wait/nowait mode */
21719618Sjulian	u_char	se_rpc;			/* ==1 if RPC service */
2182657Scsgr	int	se_rpc_prog;		/* RPC program number */
2192657Scsgr	u_int	se_rpc_lowvers;		/* RPC low version */
2202657Scsgr	u_int	se_rpc_highvers;	/* RPC high version */
2211553Srgrimes	int	se_count;		/* number started since se_time */
2221553Srgrimes	struct	timeval se_time;	/* start of se_count */
2231553Srgrimes	struct	servtab *se_next;
2241553Srgrimes} *servtab;
2251553Srgrimes
2261553Srgrimes#define NORM_TYPE	0
2271553Srgrimes#define MUX_TYPE	1
2281553Srgrimes#define MUXPLUS_TYPE	2
22936042Sguido#define TTCP_TYPE	3
2301553Srgrimes#define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
2311553Srgrimes			 ((sep)->se_type == MUXPLUS_TYPE))
2321553Srgrimes#define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
23336042Sguido#define ISTTCP(sep)	((sep)->se_type == TTCP_TYPE)
2341553Srgrimes
2351553Srgrimes
2361553Srgrimesvoid		chargen_dg __P((int, struct servtab *));
2371553Srgrimesvoid		chargen_stream __P((int, struct servtab *));
2381553Srgrimesvoid		close_sep __P((struct servtab *));
23942122Sdesvoid		flag_signal __P((char));
24042122Sdesvoid		flag_config __P((int));
24142122Sdesvoid		config __P((void));
2421553Srgrimesvoid		daytime_dg __P((int, struct servtab *));
2431553Srgrimesvoid		daytime_stream __P((int, struct servtab *));
2441553Srgrimesvoid		discard_dg __P((int, struct servtab *));
2451553Srgrimesvoid		discard_stream __P((int, struct servtab *));
2461553Srgrimesvoid		echo_dg __P((int, struct servtab *));
2471553Srgrimesvoid		echo_stream __P((int, struct servtab *));
2481553Srgrimesvoid		endconfig __P((void));
2491553Srgrimesstruct servtab *enter __P((struct servtab *));
2501553Srgrimesvoid		freeconfig __P((struct servtab *));
2511553Srgrimesstruct servtab *getconfigent __P((void));
25240910Sphkvoid		ident_stream __P((int, struct servtab *));
2531553Srgrimesvoid		machtime_dg __P((int, struct servtab *));
2541553Srgrimesvoid		machtime_stream __P((int, struct servtab *));
2551553Srgrimeschar	       *newstr __P((char *));
2561553Srgrimeschar	       *nextline __P((FILE *));
2571553Srgrimesvoid		print_service __P((char *, struct servtab *));
25819618Sjulianvoid		addchild __P((struct servtab *, int));
25942122Sdesvoid		flag_reapchild __P((int));
26042122Sdesvoid		reapchild __P((void));
26119618Sjulianvoid		enable __P((struct servtab *));
26219618Sjulianvoid		disable __P((struct servtab *));
26342122Sdesvoid		flag_retry __P((int));
26442122Sdesvoid		retry __P((void));
2651553Srgrimesint		setconfig __P((void));
2661553Srgrimesvoid		setup __P((struct servtab *));
2671553Srgrimeschar	       *sskip __P((char **));
2681553Srgrimeschar	       *skip __P((char **));
2691553Srgrimesstruct servtab *tcpmux __P((int));
27030847Sdimaint		cpmip __P((struct servtab *, int));
2711553Srgrimes
2722657Scsgrvoid		unregisterrpc __P((register struct servtab *sep));
2732657Scsgr
2741553Srgrimesstruct biltin {
2751553Srgrimes	char	*bi_service;		/* internally provided service name */
2761553Srgrimes	int	bi_socktype;		/* type of socket supported */
2771553Srgrimes	short	bi_fork;		/* 1 if should fork before call */
27830847Sdima	int	bi_maxchild;		/* max number of children (default) */
2791553Srgrimes	void	(*bi_fn)();		/* function which performs it */
2801553Srgrimes} biltins[] = {
2811553Srgrimes	/* Echo received data */
2821553Srgrimes	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream },
2831553Srgrimes	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg },
2841553Srgrimes
2851553Srgrimes	/* Internet /dev/null */
2861553Srgrimes	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream },
2871553Srgrimes	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg },
2881553Srgrimes
28945089Smarkm	/* Return 32 bit time since 1970 */
2901553Srgrimes	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream },
2911553Srgrimes	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg },
2921553Srgrimes
2931553Srgrimes	/* Return human-readable time */
2941553Srgrimes	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream },
2951553Srgrimes	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg },
2961553Srgrimes
2971553Srgrimes	/* Familiar character generator */
2981553Srgrimes	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream },
2991553Srgrimes	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg },
3001553Srgrimes
3011553Srgrimes	{ "tcpmux",	SOCK_STREAM,	1, 0,	(void (*)())tcpmux },
3021553Srgrimes
30340910Sphk	{ "ident",	SOCK_STREAM,	1, 0,	ident_stream },
30440910Sphk
3051553Srgrimes	{ NULL }
3061553Srgrimes};
3071553Srgrimes
3081553Srgrimes#define NUMINT	(sizeof(intab) / sizeof(struct inent))
3091553Srgrimeschar	*CONFIG = _PATH_INETDCONF;
31017482Sjulianchar	*pid_file = _PATH_INETDPID;
31113142Speter
31213142Speter#ifdef OLD_SETPROCTITLE
3131553Srgrimeschar	**Argv;
3141553Srgrimeschar 	*LastArg;
31513142Speter#endif
3161553Srgrimes
3171553Srgrimesint
31833794Spstgetvalue(arg, value, whine)
31933794Spst	char *arg, *whine;
32033794Spst	int  *value;
32133794Spst{
32233794Spst	int  tmp;
32333794Spst	char *p;
32433794Spst
32533794Spst	tmp = strtol(arg, &p, 0);
32633794Spst	if (tmp < 1 || *p) {
32733794Spst		syslog(LOG_ERR, whine, arg);
32833794Spst		return 1;			/* failure */
32933794Spst	}
33033794Spst	*value = tmp;
33133794Spst	return 0;				/* success */
33233794Spst}
33333794Spst
33433794Spstint
3351553Srgrimesmain(argc, argv, envp)
3361553Srgrimes	int argc;
3371553Srgrimes	char *argv[], *envp[];
3381553Srgrimes{
3391553Srgrimes	struct servtab *sep;
3401553Srgrimes	struct passwd *pwd;
34130807Sache	struct group *grp;
34235848Sguido	struct sigaction sa, sapipe;
3431553Srgrimes	int tmpint, ch, dofork;
3441553Srgrimes	pid_t pid;
3451553Srgrimes	char buf[50];
3462659Scsgr	struct  sockaddr_in peer;
3472659Scsgr	int i;
34821640Speter#ifdef LOGIN_CAP
34921640Speter	login_cap_t *lc = NULL;
35021640Speter#endif
35145089Smarkm#ifdef LIBWRAP
35245089Smarkm	struct request_info req;
35345089Smarkm	int denied;
35445089Smarkm	char *service = NULL;
35545089Smarkm#endif
3561553Srgrimes
35713142Speter
35813142Speter#ifdef OLD_SETPROCTITLE
3591553Srgrimes	Argv = argv;
3601553Srgrimes	if (envp == 0 || *envp == 0)
3611553Srgrimes		envp = argv;
3621553Srgrimes	while (*envp)
3631553Srgrimes		envp++;
3641553Srgrimes	LastArg = envp[-1] + strlen(envp[-1]);
36513142Speter#endif
3661553Srgrimes
3671553Srgrimes	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
3681553Srgrimes
36917482Sjulian	bind_address.s_addr = htonl(INADDR_ANY);
37033794Spst	while ((ch = getopt(argc, argv, "dlR:a:c:C:p:")) != -1)
3711553Srgrimes		switch(ch) {
3721553Srgrimes		case 'd':
3731553Srgrimes			debug = 1;
3741553Srgrimes			options |= SO_DEBUG;
3751553Srgrimes			break;
3762659Scsgr		case 'l':
3772659Scsgr			log = 1;
3782659Scsgr			break;
37933794Spst		case 'R':
38033794Spst			getvalue(optarg, &toomany,
38133794Spst				"-R %s: bad value for service invocation rate");
3821553Srgrimes			break;
38333794Spst		case 'c':
38433794Spst			getvalue(optarg, &maxchild,
38533794Spst				"-c %s: bad value for maximum children");
38633794Spst			break;
38733794Spst		case 'C':
38833794Spst			getvalue(optarg, &maxcpm,
38933794Spst				"-C %s: bad value for maximum children/minute");
39033794Spst			break;
39117482Sjulian		case 'a':
39217482Sjulian			if (!inet_aton(optarg, &bind_address)) {
39317482Sjulian				syslog(LOG_ERR,
39417482Sjulian			         "-a %s: invalid IP address", optarg);
39519617Sjulian				exit(EX_USAGE);
39617482Sjulian			}
39717482Sjulian			break;
39817482Sjulian		case 'p':
39917482Sjulian			pid_file = optarg;
40017482Sjulian			break;
4011553Srgrimes		case '?':
4021553Srgrimes		default:
4031553Srgrimes			syslog(LOG_ERR,
40417482Sjulian				"usage: inetd [-dl] [-a address] [-R rate]"
40533794Spst				" [-c maximum] [-C rate]"
40617482Sjulian				" [-p pidfile] [conf-file]");
40719617Sjulian			exit(EX_USAGE);
4081553Srgrimes		}
4091553Srgrimes	argc -= optind;
4101553Srgrimes	argv += optind;
4111553Srgrimes
4121553Srgrimes	if (argc > 0)
4131553Srgrimes		CONFIG = argv[0];
4141553Srgrimes	if (debug == 0) {
41511447Swollman		FILE *fp;
41612024Speter		if (daemon(0, 0) < 0) {
41712024Speter			syslog(LOG_WARNING, "daemon(0,0) failed: %m");
41812024Speter		}
41912024Speter		/*
42012024Speter		 * In case somebody has started inetd manually, we need to
42112024Speter		 * clear the logname, so that old servers run as root do not
42212024Speter		 * get the user's logname..
42312024Speter		 */
42412024Speter		if (setlogin("") < 0) {
42512024Speter			syslog(LOG_WARNING, "cannot clear logname: %m");
42612024Speter			/* no big deal if it fails.. */
42712024Speter		}
42811447Swollman		pid = getpid();
42917482Sjulian		fp = fopen(pid_file, "w");
43011447Swollman		if (fp) {
43111447Swollman			fprintf(fp, "%ld\n", (long)pid);
43211447Swollman			fclose(fp);
43311447Swollman		} else {
43417482Sjulian			syslog(LOG_WARNING, "%s: %m", pid_file);
43511447Swollman		}
4361553Srgrimes	}
43735948Sbde	sa.sa_flags = 0;
43835948Sbde	sigemptyset(&sa.sa_mask);
43935948Sbde	sigaddset(&sa.sa_mask, SIGALRM);
44035948Sbde	sigaddset(&sa.sa_mask, SIGCHLD);
44135948Sbde	sigaddset(&sa.sa_mask, SIGHUP);
44242122Sdes	sa.sa_handler = flag_retry;
44335848Sguido	sigaction(SIGALRM, &sa, (struct sigaction *)0);
44442122Sdes	config();
44542122Sdes	sa.sa_handler = flag_config;
44635848Sguido	sigaction(SIGHUP, &sa, (struct sigaction *)0);
44742122Sdes	sa.sa_handler = flag_reapchild;
44835848Sguido	sigaction(SIGCHLD, &sa, (struct sigaction *)0);
44935848Sguido	sa.sa_handler = SIG_IGN;
45035948Sbde	sigaction(SIGPIPE, &sa, &sapipe);
4511553Srgrimes
4521553Srgrimes	{
4531553Srgrimes		/* space for daemons to overwrite environment for ps */
4541553Srgrimes#define	DUMMYSIZE	100
4551553Srgrimes		char dummy[DUMMYSIZE];
4561553Srgrimes
45719298Salex		(void)memset(dummy, 'x', DUMMYSIZE - 1);
4581553Srgrimes		dummy[DUMMYSIZE - 1] = '\0';
4591553Srgrimes		(void)setenv("inetd_dummy", dummy, 1);
4601553Srgrimes	}
4611553Srgrimes
46242250Sdes	if (pipe(signalpipe) != 0) {
46342250Sdes		syslog(LOG_ERR, "pipe: %%m");
46442250Sdes		exit(EX_OSERR);
46542122Sdes	}
46642122Sdes	FD_SET(signalpipe[0], &allsock);
46742122Sdes	if (signalpipe[0]>maxsock) maxsock = signalpipe[0];
46841685Sdillon
4691553Srgrimes	for (;;) {
4701553Srgrimes	    int n, ctrl;
4711553Srgrimes	    fd_set readable;
4721553Srgrimes
4731553Srgrimes	    if (nsock == 0) {
47442122Sdes		(void) sigblock(SIGBLOCK);
4751553Srgrimes		while (nsock == 0)
4761553Srgrimes		    sigpause(0L);
47742122Sdes		(void) sigsetmask(0L);
4781553Srgrimes	    }
4791553Srgrimes	    readable = allsock;
48042122Sdes	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
48142122Sdes		(fd_set *)0, (struct timeval *)0)) <= 0) {
48242122Sdes		    if (n < 0 && errno != EINTR) {
4831553Srgrimes			syslog(LOG_WARNING, "select: %m");
48428907Simp			sleep(1);
48528907Simp		    }
4861553Srgrimes		    continue;
4871553Srgrimes	    }
48842122Sdes	    /* handle any queued signal flags */
48942250Sdes	    if (FD_ISSET(signalpipe[0], &readable)) {
49042122Sdes		int n;
49142250Sdes		if (ioctl(signalpipe[0], FIONREAD, &n) != 0) {
49242122Sdes		    syslog(LOG_ERR, "ioctl: %m");
49342122Sdes		    exit(EX_OSERR);
49442122Sdes		}
49542250Sdes		while (--n >= 0) {
49642250Sdes		    char c;
49742250Sdes		    if (read(signalpipe[0], &c, 1) != 1) {
49842250Sdes			syslog(LOG_ERR, "read: %m");
49942250Sdes			exit(EX_OSERR);
50042250Sdes		    }
50142250Sdes		    if (debug)
50242250Sdes			warnx("Handling signal flag %c", c);
50342250Sdes		    switch(c) {
50442250Sdes		    case 'A': /* sigalrm */
50542250Sdes			retry();
50642250Sdes			break;
50742250Sdes		    case 'C': /* sigchld */
50842250Sdes			reapchild();
50942250Sdes			break;
51042250Sdes		    case 'H': /* sighup */
51142250Sdes			config();
51242250Sdes			break;
51342250Sdes		    }
51442250Sdes		}
51542122Sdes	    }
5161553Srgrimes	    for (sep = servtab; n && sep; sep = sep->se_next)
5171553Srgrimes	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
5181553Srgrimes		    n--;
5191553Srgrimes		    if (debug)
52029602Scharnier			    warnx("someone wants %s", sep->se_service);
52119618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
5221553Srgrimes			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
5231553Srgrimes				(int *)0);
5241553Srgrimes			    if (debug)
52529602Scharnier				    warnx("accept, ctrl %d", ctrl);
5261553Srgrimes			    if (ctrl < 0) {
5271553Srgrimes				    if (errno != EINTR)
5281553Srgrimes					    syslog(LOG_WARNING,
5291553Srgrimes						"accept (for %s): %m",
53037844Sphk						sep->se_service);
53137816Sphk                                      if (sep->se_accept &&
53237816Sphk                                          sep->se_socktype == SOCK_STREAM)
53337816Sphk                                              close(ctrl);
5341553Srgrimes				    continue;
5351553Srgrimes			    }
53630847Sdima			    if (cpmip(sep, ctrl) < 0) {
53730847Sdima				close(ctrl);
53830847Sdima				continue;
53930847Sdima			    }
54019617Sjulian			    if (log) {
5412659Scsgr				i = sizeof peer;
54230847Sdima				if (getpeername(ctrl, (struct sockaddr *)
5432659Scsgr						&peer, &i)) {
5442659Scsgr					syslog(LOG_WARNING,
5452659Scsgr						"getpeername(for %s): %m",
5462659Scsgr						sep->se_service);
54730847Sdima					close(ctrl);
5482659Scsgr					continue;
5492659Scsgr				}
5502659Scsgr				syslog(LOG_INFO,"%s from %s",
5512659Scsgr					sep->se_service,
5522659Scsgr					inet_ntoa(peer.sin_addr));
5532659Scsgr			    }
5541553Srgrimes		    } else
5551553Srgrimes			    ctrl = sep->se_fd;
55642122Sdes		    (void) sigblock(SIGBLOCK);
5571553Srgrimes		    pid = 0;
55845089Smarkm#ifdef LIBWRAP_INTERNAL
55945089Smarkm		    dofork = 1;
56045089Smarkm#else
5611553Srgrimes		    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
56245089Smarkm#endif
5631553Srgrimes		    if (dofork) {
5641553Srgrimes			    if (sep->se_count++ == 0)
56537856Sache				(void)gettimeofday(&sep->se_time, (struct timezone *)NULL);
5661553Srgrimes			    else if (sep->se_count >= toomany) {
5671553Srgrimes				struct timeval now;
5681553Srgrimes
56937856Sache				(void)gettimeofday(&now, (struct timezone *)NULL);
5701553Srgrimes				if (now.tv_sec - sep->se_time.tv_sec >
5711553Srgrimes				    CNT_INTVL) {
5721553Srgrimes					sep->se_time = now;
5731553Srgrimes					sep->se_count = 1;
5741553Srgrimes				} else {
5751553Srgrimes					syslog(LOG_ERR,
5761553Srgrimes			"%s/%s server failing (looping), service terminated",
5771553Srgrimes					    sep->se_service, sep->se_proto);
5781553Srgrimes					close_sep(sep);
57942122Sdes					sigsetmask(0L);
5801553Srgrimes					if (!timingout) {
5811553Srgrimes						timingout = 1;
5821553Srgrimes						alarm(RETRYTIME);
5831553Srgrimes					}
5841553Srgrimes					continue;
5851553Srgrimes				}
5861553Srgrimes			    }
5871553Srgrimes			    pid = fork();
5881553Srgrimes		    }
5891553Srgrimes		    if (pid < 0) {
5901553Srgrimes			    syslog(LOG_ERR, "fork: %m");
59119618Sjulian			    if (sep->se_accept &&
5921553Srgrimes				sep->se_socktype == SOCK_STREAM)
5931553Srgrimes				    close(ctrl);
59442122Sdes			    sigsetmask(0L);
5951553Srgrimes			    sleep(1);
5961553Srgrimes			    continue;
5971553Srgrimes		    }
59819618Sjulian		    if (pid)
59919618Sjulian			addchild(sep, pid);
60042122Sdes		    sigsetmask(0L);
6011553Srgrimes		    if (pid == 0) {
6021553Srgrimes			    if (dofork) {
6031553Srgrimes				if (debug)
60429602Scharnier					warnx("+ closing from %d", maxsock);
6051553Srgrimes				for (tmpint = maxsock; tmpint > 2; tmpint--)
6061553Srgrimes					if (tmpint != ctrl)
60719617Sjulian						(void) close(tmpint);
6081553Srgrimes			    }
60935829Sguido			    /*
61035829Sguido			     * Call tcpmux to find the real service to exec.
61135829Sguido			     */
61235829Sguido			    if (sep->se_bi &&
61335829Sguido				sep->se_bi->bi_fn == (void (*)()) tcpmux) {
61435829Sguido				    sep = tcpmux(ctrl);
61535829Sguido				    if (sep == NULL) {
61635829Sguido					    close(ctrl);
61735829Sguido					    _exit(0);
61835829Sguido				    }
61935829Sguido			    }
62045089Smarkm#ifdef LIBWRAP
62145089Smarkm#ifndef LIBWRAP_INTERNAL
62245089Smarkm			    if (sep->se_bi == 0)
62345089Smarkm#endif
62445089Smarkm			    if (sep->se_accept
62545089Smarkm				&& sep->se_socktype == SOCK_STREAM) {
62645089Smarkm				request_init(&req,
62745089Smarkm				    RQ_DAEMON, sep->se_argv[0] ?
62845089Smarkm					 sep->se_argv[0] : sep->se_service,
62945089Smarkm					RQ_FILE, ctrl, NULL);
63045089Smarkm				fromhost(&req);
63145089Smarkm				denied = !hosts_access(&req);
63245089Smarkm				if (denied || log) {
63345089Smarkm				    sp = getservbyport(sep->se_ctrladdr.sin_port, sep->se_proto);
63445089Smarkm				    if (sp == NULL) {
63545089Smarkm					(void)snprintf(buf, sizeof buf, "%d",
63645089Smarkm					   ntohs(sep->se_ctrladdr.sin_port));
63745089Smarkm					service = buf;
63845089Smarkm				    } else
63945089Smarkm					service = sp->s_name;
64045089Smarkm				}
64145089Smarkm				if (denied) {
64245089Smarkm				    syslog(deny_severity,
64345089Smarkm				        "refused connection from %.500s, service %s (%s)",
64445089Smarkm				        eval_client(&req), service, sep->se_proto);
64545089Smarkm				    goto reject;
64645089Smarkm				}
64745089Smarkm				if (log) {
64845089Smarkm				    syslog(allow_severity,
64945089Smarkm				        "connection from %.500s, service %s (%s)",
65045089Smarkm					eval_client(&req), service, sep->se_proto);
65145089Smarkm				}
65245089Smarkm			    }
65345089Smarkm#endif /* LIBWRAP */
65419617Sjulian			    if (sep->se_bi) {
6551553Srgrimes				(*sep->se_bi->bi_fn)(ctrl, sep);
65619617Sjulian				/* NOTREACHED */
65719617Sjulian			    } else {
6581553Srgrimes				if (debug)
65929602Scharnier					warnx("%d execl %s",
66029602Scharnier						getpid(), sep->se_server);
6611553Srgrimes				dup2(ctrl, 0);
6621553Srgrimes				close(ctrl);
6631553Srgrimes				dup2(0, 1);
6641553Srgrimes				dup2(0, 2);
6651553Srgrimes				if ((pwd = getpwnam(sep->se_user)) == NULL) {
6661553Srgrimes					syslog(LOG_ERR,
6671553Srgrimes					    "%s/%s: %s: No such user",
6681553Srgrimes						sep->se_service, sep->se_proto,
6691553Srgrimes						sep->se_user);
6701553Srgrimes					if (sep->se_socktype != SOCK_STREAM)
6711553Srgrimes						recv(0, buf, sizeof (buf), 0);
67219617Sjulian					_exit(EX_NOUSER);
6731553Srgrimes				}
67430807Sache				grp = NULL;
67530807Sache				if (   sep->se_group != NULL
67630807Sache				    && (grp = getgrnam(sep->se_group)) == NULL
67730807Sache				   ) {
67830807Sache					syslog(LOG_ERR,
67930807Sache					    "%s/%s: %s: No such group",
68030807Sache						sep->se_service, sep->se_proto,
68130807Sache						sep->se_group);
68230807Sache					if (sep->se_socktype != SOCK_STREAM)
68330807Sache						recv(0, buf, sizeof (buf), 0);
68430807Sache					_exit(EX_NOUSER);
68530807Sache				}
68630807Sache				if (grp != NULL)
68730807Sache					pwd->pw_gid = grp->gr_gid;
68821640Speter#ifdef LOGIN_CAP
68930792Sache				if ((lc = login_getclass(sep->se_class)) == NULL) {
69030792Sache					/* error syslogged by getclass */
69130792Sache					syslog(LOG_ERR,
69230792Sache					    "%s/%s: %s: login class error",
69337850Sache						sep->se_service, sep->se_proto,
69437850Sache						sep->se_class);
69530792Sache					if (sep->se_socktype != SOCK_STREAM)
69630792Sache						recv(0, buf, sizeof (buf), 0);
69730792Sache					_exit(EX_NOUSER);
69830792Sache				}
69921640Speter#endif
70012024Speter				if (setsid() < 0) {
70112024Speter					syslog(LOG_ERR,
70212024Speter						"%s: can't setsid(): %m",
70312024Speter						 sep->se_service);
70419617Sjulian					/* _exit(EX_OSERR); not fatal yet */
70512024Speter				}
70621640Speter#ifdef LOGIN_CAP
70721640Speter				if (setusercontext(lc, pwd, pwd->pw_uid,
70821640Speter				    LOGIN_SETALL) != 0) {
70921640Speter					syslog(LOG_ERR,
71021640Speter					 "%s: can't setusercontext(..%s..): %m",
71121640Speter					 sep->se_service, sep->se_user);
71221640Speter					_exit(EX_OSERR);
71321640Speter				}
71421640Speter#else
7151553Srgrimes				if (pwd->pw_uid) {
71612024Speter					if (setlogin(sep->se_user) < 0) {
71712024Speter						syslog(LOG_ERR,
71812024Speter						 "%s: can't setlogin(%s): %m",
71912024Speter						 sep->se_service, sep->se_user);
72019617Sjulian						/* _exit(EX_OSERR); not yet */
72112024Speter					}
7221553Srgrimes					if (setgid(pwd->pw_gid) < 0) {
7231553Srgrimes						syslog(LOG_ERR,
7248857Srgrimes						  "%s: can't set gid %d: %m",
7251553Srgrimes						  sep->se_service, pwd->pw_gid);
72619617Sjulian						_exit(EX_OSERR);
7271553Srgrimes					}
7281553Srgrimes					(void) initgroups(pwd->pw_name,
7291553Srgrimes							pwd->pw_gid);
7301553Srgrimes					if (setuid(pwd->pw_uid) < 0) {
7311553Srgrimes						syslog(LOG_ERR,
7328857Srgrimes						  "%s: can't set uid %d: %m",
7331553Srgrimes						  sep->se_service, pwd->pw_uid);
73419617Sjulian						_exit(EX_OSERR);
7351553Srgrimes					}
7361553Srgrimes				}
73721640Speter#endif
73835948Sbde				sigaction(SIGPIPE, &sapipe,
73935948Sbde				    (struct sigaction *)0);
7401553Srgrimes				execv(sep->se_server, sep->se_argv);
74145089Smarkm				syslog(LOG_ERR,
74245089Smarkm				    "cannot execute %s: %m", sep->se_server);
74345089Smarkm#ifdef LIBWRAP
74445089Smarkm			    reject:
74545089Smarkm#endif
7461553Srgrimes				if (sep->se_socktype != SOCK_STREAM)
7471553Srgrimes					recv(0, buf, sizeof (buf), 0);
74819617Sjulian				_exit(EX_OSERR);
7491553Srgrimes			    }
7501553Srgrimes		    }
75119618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
7521553Srgrimes			    close(ctrl);
7531553Srgrimes		}
7541553Srgrimes	}
7551553Srgrimes}
7561553Srgrimes
75719618Sjulian/*
75842122Sdes * Add a signal flag to the signal flag queue for later handling
75942122Sdes */
76042122Sdes
76142122Sdesvoid flag_signal(c)
76242122Sdes    char c;
76342122Sdes{
76442250Sdes	if (write(signalpipe[1], &c, 1) != 1) {
76542250Sdes		syslog(LOG_ERR, "write: %m");
76642250Sdes		exit(EX_OSERR);
76742250Sdes	}
76842122Sdes}
76942122Sdes
77042122Sdes/*
77119618Sjulian * Record a new child pid for this service. If we've reached the
77219618Sjulian * limit on children, then stop accepting incoming requests.
77319618Sjulian */
77419618Sjulian
7751553Srgrimesvoid
77619618Sjulianaddchild(struct servtab *sep, pid_t pid)
77719618Sjulian{
77819618Sjulian#ifdef SANITY_CHECK
77919618Sjulian	if (sep->se_numchild >= sep->se_maxchild) {
78019618Sjulian		syslog(LOG_ERR, "%s: %d >= %d",
78119618Sjulian		    __FUNCTION__, sep->se_numchild, sep->se_maxchild);
78219618Sjulian		exit(EX_SOFTWARE);
78319618Sjulian	}
78419618Sjulian#endif
78519618Sjulian	if (sep->se_maxchild == 0)
78619618Sjulian		return;
78719618Sjulian	sep->se_pids[sep->se_numchild++] = pid;
78819618Sjulian	if (sep->se_numchild == sep->se_maxchild)
78919618Sjulian		disable(sep);
79019618Sjulian}
79119618Sjulian
79219618Sjulian/*
79319618Sjulian * Some child process has exited. See if it's on somebody's list.
79419618Sjulian */
79519618Sjulian
79619618Sjulianvoid
79742122Sdesflag_reapchild(signo)
7981553Srgrimes	int signo;
7991553Srgrimes{
80042250Sdes	flag_signal('C');
80142122Sdes}
80242122Sdes
80342122Sdesvoid
80442122Sdesreapchild()
80542122Sdes{
80619618Sjulian	int k, status;
8071553Srgrimes	pid_t pid;
8081553Srgrimes	struct servtab *sep;
8091553Srgrimes
8101553Srgrimes	for (;;) {
8111553Srgrimes		pid = wait3(&status, WNOHANG, (struct rusage *)0);
8121553Srgrimes		if (pid <= 0)
8131553Srgrimes			break;
8141553Srgrimes		if (debug)
81529602Scharnier			warnx("%d reaped, status %#x", pid, status);
81619618Sjulian		for (sep = servtab; sep; sep = sep->se_next) {
81719618Sjulian			for (k = 0; k < sep->se_numchild; k++)
81819618Sjulian				if (sep->se_pids[k] == pid)
81919618Sjulian					break;
82019618Sjulian			if (k == sep->se_numchild)
82119618Sjulian				continue;
82219618Sjulian			if (sep->se_numchild == sep->se_maxchild)
82319618Sjulian				enable(sep);
82419618Sjulian			sep->se_pids[k] = sep->se_pids[--sep->se_numchild];
82519618Sjulian			if (status)
82619618Sjulian				syslog(LOG_WARNING,
82719618Sjulian				    "%s[%d]: exit status 0x%x",
82819618Sjulian				    sep->se_server, pid, status);
82919618Sjulian			break;
83019618Sjulian		}
8311553Srgrimes	}
8321553Srgrimes}
8331553Srgrimes
8341553Srgrimesvoid
83542122Sdesflag_config(signo)
8361553Srgrimes	int signo;
8371553Srgrimes{
83842250Sdes	flag_signal('H');
83942122Sdes}
84042122Sdes
84142122Sdesvoid config()
84242122Sdes{
84319618Sjulian	struct servtab *sep, *new, **sepp;
84442122Sdes	long omask;
8451553Srgrimes
8461553Srgrimes	if (!setconfig()) {
8471553Srgrimes		syslog(LOG_ERR, "%s: %m", CONFIG);
8481553Srgrimes		return;
8491553Srgrimes	}
8501553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
8511553Srgrimes		sep->se_checked = 0;
85219618Sjulian	while ((new = getconfigent())) {
85330807Sache		if (getpwnam(new->se_user) == NULL) {
8541553Srgrimes			syslog(LOG_ERR,
8551553Srgrimes				"%s/%s: No such user '%s', service ignored",
85619618Sjulian				new->se_service, new->se_proto, new->se_user);
8571553Srgrimes			continue;
8581553Srgrimes		}
85930807Sache		if (new->se_group && getgrnam(new->se_group) == NULL) {
86030807Sache			syslog(LOG_ERR,
86130807Sache				"%s/%s: No such group '%s', service ignored",
86230807Sache				new->se_service, new->se_proto, new->se_group);
86330807Sache			continue;
86430807Sache		}
86530792Sache#ifdef LOGIN_CAP
86630792Sache		if (login_getclass(new->se_class) == NULL) {
86730792Sache			/* error syslogged by getclass */
86830792Sache			syslog(LOG_ERR,
86937850Sache				"%s/%s: %s: login class error, service ignored",
87037850Sache				new->se_service, new->se_proto, new->se_class);
87130792Sache			continue;
87230792Sache		}
87330792Sache#endif
8741553Srgrimes		for (sep = servtab; sep; sep = sep->se_next)
87519618Sjulian			if (strcmp(sep->se_service, new->se_service) == 0 &&
87619618Sjulian			    strcmp(sep->se_proto, new->se_proto) == 0)
8771553Srgrimes				break;
8781553Srgrimes		if (sep != 0) {
8791553Srgrimes			int i;
8801553Srgrimes
88119618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; }
88242122Sdes			omask = sigblock(SIGBLOCK);
88319618Sjulian			/* copy over outstanding child pids */
88419618Sjulian			if (sep->se_maxchild && new->se_maxchild) {
88519618Sjulian				new->se_numchild = sep->se_numchild;
88619618Sjulian				if (new->se_numchild > new->se_maxchild)
88719618Sjulian					new->se_numchild = new->se_maxchild;
88819618Sjulian				memcpy(new->se_pids, sep->se_pids,
88919618Sjulian				    new->se_numchild * sizeof(*new->se_pids));
89019618Sjulian			}
89119618Sjulian			SWAP(sep->se_pids, new->se_pids);
89219618Sjulian			sep->se_maxchild = new->se_maxchild;
89319618Sjulian			sep->se_numchild = new->se_numchild;
89430847Sdima			sep->se_maxcpm = new->se_maxcpm;
89519618Sjulian			/* might need to turn on or off service now */
89619618Sjulian			if (sep->se_fd >= 0) {
89719618Sjulian			      if (sep->se_maxchild
89819618Sjulian				  && sep->se_numchild == sep->se_maxchild) {
89919618Sjulian				      if (FD_ISSET(sep->se_fd, &allsock))
90019618Sjulian					  disable(sep);
90119618Sjulian			      } else {
90219618Sjulian				      if (!FD_ISSET(sep->se_fd, &allsock))
90319618Sjulian					  enable(sep);
90419618Sjulian			      }
90519618Sjulian			}
90619618Sjulian			sep->se_accept = new->se_accept;
90730807Sache			SWAP(sep->se_user, new->se_user);
90830807Sache			SWAP(sep->se_group, new->se_group);
90930792Sache#ifdef LOGIN_CAP
91030807Sache			SWAP(sep->se_class, new->se_class);
91130792Sache#endif
91230807Sache			SWAP(sep->se_server, new->se_server);
9131553Srgrimes			for (i = 0; i < MAXARGV; i++)
91419618Sjulian				SWAP(sep->se_argv[i], new->se_argv[i]);
91542122Sdes			sigsetmask(omask);
91619618Sjulian			freeconfig(new);
9171553Srgrimes			if (debug)
9181553Srgrimes				print_service("REDO", sep);
9191553Srgrimes		} else {
92019618Sjulian			sep = enter(new);
9211553Srgrimes			if (debug)
9221553Srgrimes				print_service("ADD ", sep);
9231553Srgrimes		}
9241553Srgrimes		sep->se_checked = 1;
9251553Srgrimes		if (ISMUX(sep)) {
9261553Srgrimes			sep->se_fd = -1;
9271553Srgrimes			continue;
9281553Srgrimes		}
9292657Scsgr		if (!sep->se_rpc) {
9302657Scsgr			sp = getservbyname(sep->se_service, sep->se_proto);
9312657Scsgr			if (sp == 0) {
9322657Scsgr				syslog(LOG_ERR, "%s/%s: unknown service",
9332657Scsgr			    	sep->se_service, sep->se_proto);
9342657Scsgr				sep->se_checked = 0;
9352657Scsgr				continue;
9362657Scsgr			}
9372657Scsgr			if (sp->s_port != sep->se_ctrladdr.sin_port) {
9382657Scsgr				sep->se_ctrladdr.sin_family = AF_INET;
93922306Sjulian				sep->se_ctrladdr.sin_addr = bind_address;
9402657Scsgr				sep->se_ctrladdr.sin_port = sp->s_port;
9412657Scsgr				if (sep->se_fd >= 0)
9422657Scsgr					close_sep(sep);
9432657Scsgr			}
9442657Scsgr		} else {
9452657Scsgr			rpc = getrpcbyname(sep->se_service);
9462657Scsgr			if (rpc == 0) {
9472657Scsgr				syslog(LOG_ERR, "%s/%s unknown RPC service.",
9482657Scsgr					sep->se_service, sep->se_proto);
9492657Scsgr				if (sep->se_fd != -1)
9502657Scsgr					(void) close(sep->se_fd);
9512657Scsgr				sep->se_fd = -1;
9522657Scsgr					continue;
9532657Scsgr			}
9542657Scsgr			if (rpc->r_number != sep->se_rpc_prog) {
9552657Scsgr				if (sep->se_rpc_prog)
9562657Scsgr					unregisterrpc(sep);
9572657Scsgr				sep->se_rpc_prog = rpc->r_number;
9582657Scsgr				if (sep->se_fd != -1)
9592657Scsgr					(void) close(sep->se_fd);
9602657Scsgr				sep->se_fd = -1;
9612657Scsgr			}
9621553Srgrimes		}
9631553Srgrimes		if (sep->se_fd == -1)
9641553Srgrimes			setup(sep);
9651553Srgrimes	}
9661553Srgrimes	endconfig();
9671553Srgrimes	/*
9681553Srgrimes	 * Purge anything not looked at above.
9691553Srgrimes	 */
97042122Sdes	omask = sigblock(SIGBLOCK);
9711553Srgrimes	sepp = &servtab;
97219617Sjulian	while ((sep = *sepp)) {
9731553Srgrimes		if (sep->se_checked) {
9741553Srgrimes			sepp = &sep->se_next;
9751553Srgrimes			continue;
9761553Srgrimes		}
9771553Srgrimes		*sepp = sep->se_next;
9781553Srgrimes		if (sep->se_fd >= 0)
9791553Srgrimes			close_sep(sep);
9801553Srgrimes		if (debug)
9811553Srgrimes			print_service("FREE", sep);
9822657Scsgr		if (sep->se_rpc && sep->se_rpc_prog > 0)
9832657Scsgr			unregisterrpc(sep);
9841553Srgrimes		freeconfig(sep);
9851553Srgrimes		free((char *)sep);
9861553Srgrimes	}
98742122Sdes	(void) sigsetmask(omask);
9881553Srgrimes}
9891553Srgrimes
9901553Srgrimesvoid
9912657Scsgrunregisterrpc(sep)
9922657Scsgr	struct servtab *sep;
9932657Scsgr{
9942657Scsgr        int i;
9952657Scsgr        struct servtab *sepp;
99642122Sdes	long omask;
9972657Scsgr
99842122Sdes	omask = sigblock(SIGBLOCK);
9992657Scsgr        for (sepp = servtab; sepp; sepp = sepp->se_next) {
10002657Scsgr                if (sepp == sep)
10012657Scsgr                        continue;
10022657Scsgr		if (sep->se_checked == 0 ||
10032657Scsgr                    !sepp->se_rpc ||
10042657Scsgr                    sep->se_rpc_prog != sepp->se_rpc_prog)
10052657Scsgr			continue;
10062657Scsgr                return;
10072657Scsgr        }
10082657Scsgr        if (debug)
10092657Scsgr                print_service("UNREG", sep);
10102657Scsgr        for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++)
10112657Scsgr                pmap_unset(sep->se_rpc_prog, i);
10122657Scsgr        if (sep->se_fd != -1)
10132657Scsgr                (void) close(sep->se_fd);
10142657Scsgr        sep->se_fd = -1;
101542122Sdes	(void) sigsetmask(omask);
10162657Scsgr}
10172657Scsgr
10182657Scsgrvoid
101942122Sdesflag_retry(signo)
10201553Srgrimes	int signo;
10211553Srgrimes{
102242250Sdes	flag_signal('A');
102342122Sdes}
102442122Sdes
102542122Sdesvoid
102642122Sdesretry()
102742122Sdes{
10281553Srgrimes	struct servtab *sep;
10291553Srgrimes
10301553Srgrimes	timingout = 0;
10311553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
103219617Sjulian		if (sep->se_fd == -1 && !ISMUX(sep))
10331553Srgrimes			setup(sep);
10341553Srgrimes}
10351553Srgrimes
10361553Srgrimesvoid
10371553Srgrimessetup(sep)
10381553Srgrimes	struct servtab *sep;
10391553Srgrimes{
10401553Srgrimes	int on = 1;
10411553Srgrimes
10421553Srgrimes	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
10431553Srgrimes		if (debug)
104429602Scharnier			warn("socket failed on %s/%s",
104529602Scharnier				sep->se_service, sep->se_proto);
10461553Srgrimes		syslog(LOG_ERR, "%s/%s: socket: %m",
10471553Srgrimes		    sep->se_service, sep->se_proto);
10481553Srgrimes		return;
10491553Srgrimes	}
10501553Srgrimes#define	turnon(fd, opt) \
10511553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
10521553Srgrimes	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
10531553Srgrimes	    turnon(sep->se_fd, SO_DEBUG) < 0)
10541553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
10551553Srgrimes	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
10561553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
105725253Swollman#ifdef SO_PRIVSTATE
105813956Swollman	if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
105913956Swollman		syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
106025253Swollman#endif
10611553Srgrimes#undef turnon
106236042Sguido	if (sep->se_type == TTCP_TYPE)
106336042Sguido		if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH,
106436042Sguido		    (char *)&on, sizeof (on)) < 0)
106536042Sguido			syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m");
10661553Srgrimes	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
10671553Srgrimes	    sizeof (sep->se_ctrladdr)) < 0) {
10681553Srgrimes		if (debug)
106929602Scharnier			warn("bind failed on %s/%s",
107029602Scharnier				sep->se_service, sep->se_proto);
10711553Srgrimes		syslog(LOG_ERR, "%s/%s: bind: %m",
10721553Srgrimes		    sep->se_service, sep->se_proto);
10731553Srgrimes		(void) close(sep->se_fd);
10741553Srgrimes		sep->se_fd = -1;
10751553Srgrimes		if (!timingout) {
10761553Srgrimes			timingout = 1;
10771553Srgrimes			alarm(RETRYTIME);
10781553Srgrimes		}
10791553Srgrimes		return;
10801553Srgrimes	}
10812657Scsgr        if (sep->se_rpc) {
10822657Scsgr                int i, len = sizeof(struct sockaddr);
10832657Scsgr
10848857Srgrimes                if (getsockname(sep->se_fd,
10852657Scsgr				(struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
10862657Scsgr                        syslog(LOG_ERR, "%s/%s: getsockname: %m",
10872657Scsgr                               sep->se_service, sep->se_proto);
10882657Scsgr                        (void) close(sep->se_fd);
10892657Scsgr                        sep->se_fd = -1;
10908857Srgrimes                        return;
10912657Scsgr                }
10922657Scsgr                if (debug)
10932657Scsgr                        print_service("REG ", sep);
10942657Scsgr                for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
10952657Scsgr                        pmap_unset(sep->se_rpc_prog, i);
10962657Scsgr                        pmap_set(sep->se_rpc_prog, i,
10972657Scsgr                                 (sep->se_socktype == SOCK_DGRAM)
10982657Scsgr                                 ? IPPROTO_UDP : IPPROTO_TCP,
10992657Scsgr                                 ntohs(sep->se_ctrladdr.sin_port));
11002657Scsgr                }
11018857Srgrimes
11022657Scsgr        }
11031553Srgrimes	if (sep->se_socktype == SOCK_STREAM)
110417197Sdg		listen(sep->se_fd, 64);
110519618Sjulian	enable(sep);
11061553Srgrimes	if (debug) {
110729602Scharnier		warnx("registered %s on %d",
11081553Srgrimes			sep->se_server, sep->se_fd);
11091553Srgrimes	}
11101553Srgrimes}
11111553Srgrimes
11121553Srgrimes/*
11131553Srgrimes * Finish with a service and its socket.
11141553Srgrimes */
11151553Srgrimesvoid
11161553Srgrimesclose_sep(sep)
11171553Srgrimes	struct servtab *sep;
11181553Srgrimes{
11191553Srgrimes	if (sep->se_fd >= 0) {
112019618Sjulian		if (FD_ISSET(sep->se_fd, &allsock))
112119618Sjulian			disable(sep);
11221553Srgrimes		(void) close(sep->se_fd);
11231553Srgrimes		sep->se_fd = -1;
11241553Srgrimes	}
11251553Srgrimes	sep->se_count = 0;
112619618Sjulian	sep->se_numchild = 0;	/* forget about any existing children */
11271553Srgrimes}
11281553Srgrimes
11291553Srgrimesstruct servtab *
11301553Srgrimesenter(cp)
11311553Srgrimes	struct servtab *cp;
11321553Srgrimes{
11331553Srgrimes	struct servtab *sep;
113442122Sdes	long omask;
11351553Srgrimes
11361553Srgrimes	sep = (struct servtab *)malloc(sizeof (*sep));
11371553Srgrimes	if (sep == (struct servtab *)0) {
11381553Srgrimes		syslog(LOG_ERR, "Out of memory.");
113919617Sjulian		exit(EX_OSERR);
11401553Srgrimes	}
11411553Srgrimes	*sep = *cp;
11421553Srgrimes	sep->se_fd = -1;
114342122Sdes	omask = sigblock(SIGBLOCK);
11441553Srgrimes	sep->se_next = servtab;
11451553Srgrimes	servtab = sep;
114642122Sdes	sigsetmask(omask);
11471553Srgrimes	return (sep);
11481553Srgrimes}
11491553Srgrimes
115019618Sjulianvoid
115119618Sjulianenable(struct servtab *sep)
115219618Sjulian{
115319618Sjulian	if (debug)
115429602Scharnier		warnx(
115519618Sjulian		    "enabling %s, fd %d", sep->se_service, sep->se_fd);
115619618Sjulian#ifdef SANITY_CHECK
115719618Sjulian	if (sep->se_fd < 0) {
115819618Sjulian		syslog(LOG_ERR,
115919618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
116019618Sjulian		exit(EX_SOFTWARE);
116119618Sjulian	}
116219618Sjulian	if (ISMUX(sep)) {
116319618Sjulian		syslog(LOG_ERR,
116419618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
116519618Sjulian		exit(EX_SOFTWARE);
116619618Sjulian	}
116719618Sjulian	if (FD_ISSET(sep->se_fd, &allsock)) {
116819618Sjulian		syslog(LOG_ERR,
116919618Sjulian		    "%s: %s: not off", __FUNCTION__, sep->se_service);
117019618Sjulian		exit(EX_SOFTWARE);
117119618Sjulian	}
117219618Sjulian#endif
117319618Sjulian	FD_SET(sep->se_fd, &allsock);
117419618Sjulian	nsock++;
117519618Sjulian	if (sep->se_fd > maxsock)
117619618Sjulian		maxsock = sep->se_fd;
117719618Sjulian}
117819618Sjulian
117919618Sjulianvoid
118019618Sjuliandisable(struct servtab *sep)
118119618Sjulian{
118219618Sjulian	if (debug)
118329602Scharnier		warnx(
118419618Sjulian		    "disabling %s, fd %d", sep->se_service, sep->se_fd);
118519618Sjulian#ifdef SANITY_CHECK
118619618Sjulian	if (sep->se_fd < 0) {
118719618Sjulian		syslog(LOG_ERR,
118819618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
118919618Sjulian		exit(EX_SOFTWARE);
119019618Sjulian	}
119119618Sjulian	if (ISMUX(sep)) {
119219618Sjulian		syslog(LOG_ERR,
119319618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
119419618Sjulian		exit(EX_SOFTWARE);
119519618Sjulian	}
119619618Sjulian	if (!FD_ISSET(sep->se_fd, &allsock)) {
119719618Sjulian		syslog(LOG_ERR,
119819618Sjulian		    "%s: %s: not on", __FUNCTION__, sep->se_service);
119919618Sjulian		exit(EX_SOFTWARE);
120019618Sjulian	}
120119618Sjulian	if (nsock == 0) {
120219618Sjulian		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
120319618Sjulian		exit(EX_SOFTWARE);
120419618Sjulian	}
120519618Sjulian#endif
120619618Sjulian	FD_CLR(sep->se_fd, &allsock);
120719618Sjulian	nsock--;
120819618Sjulian	if (sep->se_fd == maxsock)
120919618Sjulian		maxsock--;
121019618Sjulian}
121119618Sjulian
12121553SrgrimesFILE	*fconfig = NULL;
12131553Srgrimesstruct	servtab serv;
12141553Srgrimeschar	line[LINE_MAX];
12151553Srgrimes
12161553Srgrimesint
12171553Srgrimessetconfig()
12181553Srgrimes{
12191553Srgrimes
12201553Srgrimes	if (fconfig != NULL) {
12211553Srgrimes		fseek(fconfig, 0L, SEEK_SET);
12221553Srgrimes		return (1);
12231553Srgrimes	}
12241553Srgrimes	fconfig = fopen(CONFIG, "r");
12251553Srgrimes	return (fconfig != NULL);
12261553Srgrimes}
12271553Srgrimes
12281553Srgrimesvoid
12291553Srgrimesendconfig()
12301553Srgrimes{
12311553Srgrimes	if (fconfig) {
12321553Srgrimes		(void) fclose(fconfig);
12331553Srgrimes		fconfig = NULL;
12341553Srgrimes	}
12351553Srgrimes}
12361553Srgrimes
12371553Srgrimesstruct servtab *
12381553Srgrimesgetconfigent()
12391553Srgrimes{
12401553Srgrimes	struct servtab *sep = &serv;
12411553Srgrimes	int argc;
124219618Sjulian	char *cp, *arg, *s;
12432657Scsgr	char *versp;
12441553Srgrimes	static char TCPMUX_TOKEN[] = "tcpmux/";
12451553Srgrimes#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
12461553Srgrimes
12471553Srgrimesmore:
12481553Srgrimes	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
12491553Srgrimes		;
12501553Srgrimes	if (cp == NULL)
12511553Srgrimes		return ((struct servtab *)0);
12521553Srgrimes	/*
12531553Srgrimes	 * clear the static buffer, since some fields (se_ctrladdr,
12541553Srgrimes	 * for example) don't get initialized here.
12551553Srgrimes	 */
12561553Srgrimes	memset((caddr_t)sep, 0, sizeof *sep);
12571553Srgrimes	arg = skip(&cp);
12581553Srgrimes	if (cp == NULL) {
12591553Srgrimes		/* got an empty line containing just blanks/tabs. */
12601553Srgrimes		goto more;
12611553Srgrimes	}
12621553Srgrimes	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
12631553Srgrimes		char *c = arg + MUX_LEN;
12641553Srgrimes		if (*c == '+') {
12651553Srgrimes			sep->se_type = MUXPLUS_TYPE;
12661553Srgrimes			c++;
12671553Srgrimes		} else
12681553Srgrimes			sep->se_type = MUX_TYPE;
12691553Srgrimes		sep->se_service = newstr(c);
12701553Srgrimes	} else {
12711553Srgrimes		sep->se_service = newstr(arg);
12721553Srgrimes		sep->se_type = NORM_TYPE;
12731553Srgrimes	}
12741553Srgrimes	arg = sskip(&cp);
12751553Srgrimes	if (strcmp(arg, "stream") == 0)
12761553Srgrimes		sep->se_socktype = SOCK_STREAM;
12771553Srgrimes	else if (strcmp(arg, "dgram") == 0)
12781553Srgrimes		sep->se_socktype = SOCK_DGRAM;
12791553Srgrimes	else if (strcmp(arg, "rdm") == 0)
12801553Srgrimes		sep->se_socktype = SOCK_RDM;
12811553Srgrimes	else if (strcmp(arg, "seqpacket") == 0)
12821553Srgrimes		sep->se_socktype = SOCK_SEQPACKET;
12831553Srgrimes	else if (strcmp(arg, "raw") == 0)
12841553Srgrimes		sep->se_socktype = SOCK_RAW;
12851553Srgrimes	else
12861553Srgrimes		sep->se_socktype = -1;
128736042Sguido
128836042Sguido	arg = sskip(&cp);
128936042Sguido	if (strcmp(arg, "tcp/ttcp") == 0) {
129036042Sguido		sep->se_type = TTCP_TYPE;
129136042Sguido		sep->se_proto = newstr("tcp");
129236042Sguido	} else {
129336042Sguido		sep->se_proto = newstr(arg);
129436042Sguido	}
12952657Scsgr        if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
129619237Sjoerg                memmove(sep->se_proto, sep->se_proto + 4,
129719237Sjoerg                    strlen(sep->se_proto) + 1 - 4);
12982657Scsgr                sep->se_rpc = 1;
12992657Scsgr                sep->se_rpc_prog = sep->se_rpc_lowvers =
13002657Scsgr			sep->se_rpc_lowvers = 0;
13012657Scsgr                sep->se_ctrladdr.sin_family = AF_INET;
13022657Scsgr                sep->se_ctrladdr.sin_port = 0;
130317482Sjulian                sep->se_ctrladdr.sin_addr = bind_address;
13042657Scsgr                if ((versp = rindex(sep->se_service, '/'))) {
13052657Scsgr                        *versp++ = '\0';
13062657Scsgr                        switch (sscanf(versp, "%d-%d",
13072657Scsgr                                       &sep->se_rpc_lowvers,
13082657Scsgr                                       &sep->se_rpc_highvers)) {
13092657Scsgr                        case 2:
13102657Scsgr                                break;
13112657Scsgr                        case 1:
13122657Scsgr                                sep->se_rpc_highvers =
13132657Scsgr                                        sep->se_rpc_lowvers;
13142657Scsgr                                break;
13152657Scsgr                        default:
13168857Srgrimes                                syslog(LOG_ERR,
13178857Srgrimes					"bad RPC version specifier; %s\n",
13182657Scsgr					sep->se_service);
13192657Scsgr                                freeconfig(sep);
13202657Scsgr                                goto more;
13212657Scsgr                        }
13222657Scsgr                }
13232657Scsgr                else {
13242657Scsgr                        sep->se_rpc_lowvers =
13252657Scsgr                                sep->se_rpc_highvers = 1;
13262657Scsgr                }
13272657Scsgr        }
13281553Srgrimes	arg = sskip(&cp);
132919618Sjulian	if (!strncmp(arg, "wait", 4))
133019618Sjulian		sep->se_accept = 0;
133119618Sjulian	else if (!strncmp(arg, "nowait", 6))
133219618Sjulian		sep->se_accept = 1;
133319618Sjulian	else {
133419618Sjulian		syslog(LOG_ERR,
133519618Sjulian			"%s: bad wait/nowait for service %s",
133619618Sjulian			CONFIG, sep->se_service);
133719618Sjulian		goto more;
133819618Sjulian	}
133933794Spst	sep->se_maxchild = maxchild;
134033794Spst	sep->se_maxcpm = maxcpm;
134119618Sjulian	if ((s = strchr(arg, '/')) != NULL) {
134219618Sjulian		char *eptr;
134319618Sjulian		u_long val;
134419618Sjulian
134519618Sjulian		val = strtoul(s + 1, &eptr, 10);
134630847Sdima		if (eptr == s + 1 || val > MAX_MAXCHLD) {
134719618Sjulian			syslog(LOG_ERR,
134819618Sjulian				"%s: bad max-child for service %s",
134919618Sjulian				CONFIG, sep->se_service);
135019618Sjulian			goto more;
135119618Sjulian		}
135219618Sjulian		sep->se_maxchild = val;
135330847Sdima		if (*eptr == '/')
135430847Sdima			sep->se_maxcpm = strtol(eptr + 1, &eptr, 10);
135530847Sdima		/*
135630847Sdima		 * explicitly do not check for \0 for future expansion /
135730847Sdima		 * backwards compatibility
135830847Sdima		 */
135919618Sjulian	}
13601553Srgrimes	if (ISMUX(sep)) {
13611553Srgrimes		/*
136219618Sjulian		 * Silently enforce "nowait" mode for TCPMUX services
136319618Sjulian		 * since they don't have an assigned port to listen on.
13641553Srgrimes		 */
136519618Sjulian		sep->se_accept = 1;
13661553Srgrimes		if (strcmp(sep->se_proto, "tcp")) {
13678857Srgrimes			syslog(LOG_ERR,
13681553Srgrimes				"%s: bad protocol for tcpmux service %s",
13691553Srgrimes				CONFIG, sep->se_service);
13701553Srgrimes			goto more;
13711553Srgrimes		}
13721553Srgrimes		if (sep->se_socktype != SOCK_STREAM) {
13738857Srgrimes			syslog(LOG_ERR,
13741553Srgrimes				"%s: bad socket type for tcpmux service %s",
13751553Srgrimes				CONFIG, sep->se_service);
13761553Srgrimes			goto more;
13771553Srgrimes		}
13781553Srgrimes	}
13791553Srgrimes	sep->se_user = newstr(sskip(&cp));
138030792Sache#ifdef LOGIN_CAP
138130792Sache	if ((s = strrchr(sep->se_user, '/')) != NULL) {
138230792Sache		*s = '\0';
138330792Sache		sep->se_class = newstr(s + 1);
138430792Sache	} else
138530792Sache		sep->se_class = newstr(RESOURCE_RC);
138630792Sache#endif
138730807Sache	if ((s = strrchr(sep->se_user, ':')) != NULL) {
138830807Sache		*s = '\0';
138930807Sache		sep->se_group = newstr(s + 1);
139030807Sache	} else
139130807Sache		sep->se_group = NULL;
13921553Srgrimes	sep->se_server = newstr(sskip(&cp));
13931553Srgrimes	if (strcmp(sep->se_server, "internal") == 0) {
13941553Srgrimes		struct biltin *bi;
13951553Srgrimes
13961553Srgrimes		for (bi = biltins; bi->bi_service; bi++)
13971553Srgrimes			if (bi->bi_socktype == sep->se_socktype &&
13981553Srgrimes			    strcmp(bi->bi_service, sep->se_service) == 0)
13991553Srgrimes				break;
14001553Srgrimes		if (bi->bi_service == 0) {
14011553Srgrimes			syslog(LOG_ERR, "internal service %s unknown",
14021553Srgrimes				sep->se_service);
14031553Srgrimes			goto more;
14041553Srgrimes		}
140519618Sjulian		sep->se_accept = 1;	/* force accept mode for built-ins */
14061553Srgrimes		sep->se_bi = bi;
14071553Srgrimes	} else
14081553Srgrimes		sep->se_bi = NULL;
140919618Sjulian	if (sep->se_maxchild < 0)	/* apply default max-children */
141019618Sjulian		if (sep->se_bi)
141119618Sjulian			sep->se_maxchild = sep->se_bi->bi_maxchild;
141219618Sjulian		else
141319618Sjulian			sep->se_maxchild = sep->se_accept ? 0 : 1;
141419618Sjulian	if (sep->se_maxchild) {
141519618Sjulian		sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
141619618Sjulian		if (sep->se_pids == NULL) {
141719618Sjulian			syslog(LOG_ERR, "Out of memory.");
141819618Sjulian			exit(EX_OSERR);
141919618Sjulian		}
142019618Sjulian	}
14211553Srgrimes	argc = 0;
14221553Srgrimes	for (arg = skip(&cp); cp; arg = skip(&cp))
142319618Sjulian		if (argc < MAXARGV) {
14241553Srgrimes			sep->se_argv[argc++] = newstr(arg);
142519618Sjulian		} else {
142619618Sjulian			syslog(LOG_ERR,
142719618Sjulian				"%s: too many arguments for service %s",
142819618Sjulian				CONFIG, sep->se_service);
142919618Sjulian			goto more;
143019618Sjulian		}
14311553Srgrimes	while (argc <= MAXARGV)
14321553Srgrimes		sep->se_argv[argc++] = NULL;
14331553Srgrimes	return (sep);
14341553Srgrimes}
14351553Srgrimes
14361553Srgrimesvoid
14371553Srgrimesfreeconfig(cp)
14381553Srgrimes	struct servtab *cp;
14391553Srgrimes{
14401553Srgrimes	int i;
14411553Srgrimes
14421553Srgrimes	if (cp->se_service)
14431553Srgrimes		free(cp->se_service);
14441553Srgrimes	if (cp->se_proto)
14451553Srgrimes		free(cp->se_proto);
14461553Srgrimes	if (cp->se_user)
14471553Srgrimes		free(cp->se_user);
144830807Sache	if (cp->se_group)
144930807Sache		free(cp->se_group);
145030792Sache#ifdef LOGIN_CAP
145130792Sache	if (cp->se_class)
145230792Sache		free(cp->se_class);
145330792Sache#endif
14541553Srgrimes	if (cp->se_server)
14551553Srgrimes		free(cp->se_server);
145619618Sjulian	if (cp->se_pids)
145719618Sjulian		free(cp->se_pids);
14581553Srgrimes	for (i = 0; i < MAXARGV; i++)
14591553Srgrimes		if (cp->se_argv[i])
14601553Srgrimes			free(cp->se_argv[i]);
14611553Srgrimes}
14621553Srgrimes
14631553Srgrimes
14641553Srgrimes/*
14651553Srgrimes * Safe skip - if skip returns null, log a syntax error in the
14661553Srgrimes * configuration file and exit.
14671553Srgrimes */
14681553Srgrimeschar *
14691553Srgrimessskip(cpp)
14701553Srgrimes	char **cpp;
14711553Srgrimes{
14721553Srgrimes	char *cp;
14731553Srgrimes
14741553Srgrimes	cp = skip(cpp);
14751553Srgrimes	if (cp == NULL) {
14761553Srgrimes		syslog(LOG_ERR, "%s: syntax error", CONFIG);
147719617Sjulian		exit(EX_DATAERR);
14781553Srgrimes	}
14791553Srgrimes	return (cp);
14801553Srgrimes}
14811553Srgrimes
14821553Srgrimeschar *
14831553Srgrimesskip(cpp)
14841553Srgrimes	char **cpp;
14851553Srgrimes{
14861553Srgrimes	char *cp = *cpp;
14871553Srgrimes	char *start;
148811933Sadam	char quote = '\0';
14891553Srgrimes
14901553Srgrimesagain:
14911553Srgrimes	while (*cp == ' ' || *cp == '\t')
14921553Srgrimes		cp++;
14931553Srgrimes	if (*cp == '\0') {
14941553Srgrimes		int c;
14951553Srgrimes
14961553Srgrimes		c = getc(fconfig);
14971553Srgrimes		(void) ungetc(c, fconfig);
14981553Srgrimes		if (c == ' ' || c == '\t')
149919617Sjulian			if ((cp = nextline(fconfig)))
15001553Srgrimes				goto again;
15011553Srgrimes		*cpp = (char *)0;
15021553Srgrimes		return ((char *)0);
15031553Srgrimes	}
150411933Sadam	if (*cp == '"' || *cp == '\'')
150511933Sadam		quote = *cp++;
15061553Srgrimes	start = cp;
150711933Sadam	if (quote)
150811933Sadam		while (*cp && *cp != quote)
150911933Sadam			cp++;
151011933Sadam	else
151111933Sadam		while (*cp && *cp != ' ' && *cp != '\t')
151211933Sadam			cp++;
15131553Srgrimes	if (*cp != '\0')
15141553Srgrimes		*cp++ = '\0';
15151553Srgrimes	*cpp = cp;
15161553Srgrimes	return (start);
15171553Srgrimes}
15181553Srgrimes
15191553Srgrimeschar *
15201553Srgrimesnextline(fd)
15211553Srgrimes	FILE *fd;
15221553Srgrimes{
15231553Srgrimes	char *cp;
15241553Srgrimes
15251553Srgrimes	if (fgets(line, sizeof (line), fd) == NULL)
15261553Srgrimes		return ((char *)0);
15271553Srgrimes	cp = strchr(line, '\n');
15281553Srgrimes	if (cp)
15291553Srgrimes		*cp = '\0';
15301553Srgrimes	return (line);
15311553Srgrimes}
15321553Srgrimes
15331553Srgrimeschar *
15341553Srgrimesnewstr(cp)
15351553Srgrimes	char *cp;
15361553Srgrimes{
153719617Sjulian	if ((cp = strdup(cp ? cp : "")))
15381553Srgrimes		return (cp);
15391553Srgrimes	syslog(LOG_ERR, "strdup: %m");
154019617Sjulian	exit(EX_OSERR);
15411553Srgrimes}
15421553Srgrimes
154313142Speter#ifdef OLD_SETPROCTITLE
15441553Srgrimesvoid
154513142Speterinetd_setproctitle(a, s)
15461553Srgrimes	char *a;
15471553Srgrimes	int s;
15481553Srgrimes{
15491553Srgrimes	int size;
15501553Srgrimes	char *cp;
15511553Srgrimes	struct sockaddr_in sin;
15521553Srgrimes	char buf[80];
15531553Srgrimes
15541553Srgrimes	cp = Argv[0];
15551553Srgrimes	size = sizeof(sin);
15561553Srgrimes	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
15578857Srgrimes		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
15581553Srgrimes	else
15598857Srgrimes		(void) sprintf(buf, "-%s", a);
15601553Srgrimes	strncpy(cp, buf, LastArg - cp);
15611553Srgrimes	cp += strlen(cp);
15621553Srgrimes	while (cp < LastArg)
15631553Srgrimes		*cp++ = ' ';
15641553Srgrimes}
156513142Speter#else
156613142Spetervoid
156713142Speterinetd_setproctitle(a, s)
156813142Speter	char *a;
156913142Speter	int s;
157013142Speter{
157113142Speter	int size;
157213142Speter	struct sockaddr_in sin;
157313142Speter	char buf[80];
15741553Srgrimes
157513142Speter	size = sizeof(sin);
157613142Speter	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
157713142Speter		(void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr));
157813142Speter	else
157913142Speter		(void) sprintf(buf, "%s", a);
158013142Speter	setproctitle("%s", buf);
158113142Speter}
158213142Speter#endif
158313142Speter
158413142Speter
15851553Srgrimes/*
15861553Srgrimes * Internet services provided internally by inetd:
15871553Srgrimes */
15881553Srgrimes#define	BUFSIZE	8192
15891553Srgrimes
159040910Sphk#define IDENT_RESPONSE ":ERROR:HIDDEN-USER\r\n"
159140910Sphk
15921553Srgrimes/* ARGSUSED */
15931553Srgrimesvoid
159440910Sphkident_stream(s, sep)		/* Ident service */
159540910Sphk	int s;
159640910Sphk	struct servtab *sep;
159740910Sphk{
159840910Sphk	char buffer[BUFSIZE];
159940910Sphk	int i, j;
160040910Sphk
160140910Sphk	inetd_setproctitle(sep->se_service, s);
160240910Sphk	j = 0;
160340910Sphk	while ((i = read(s, buffer + j, sizeof(buffer) - j)) > 0) {
160440910Sphk		j += i;
160540910Sphk		buffer[j] = '\0';
160640910Sphk		if (strchr(buffer, '\n'))
160740910Sphk			break;
160840910Sphk		if (strchr(buffer, '\r'))
160940910Sphk			break;
161040910Sphk	}
161140910Sphk	while (j > 0 && (buffer[j-1] == '\n' || buffer[j-1] == '\r'))
161240910Sphk		j--;
161340910Sphk	write(s, buffer, j);
161440910Sphk	write(s, IDENT_RESPONSE, strlen(IDENT_RESPONSE));
161540910Sphk	exit(0);
161640910Sphk}
161740910Sphk/* ARGSUSED */
161840910Sphkvoid
16191553Srgrimesecho_stream(s, sep)		/* Echo service -- echo data back */
16201553Srgrimes	int s;
16211553Srgrimes	struct servtab *sep;
16221553Srgrimes{
16231553Srgrimes	char buffer[BUFSIZE];
16241553Srgrimes	int i;
16251553Srgrimes
162613142Speter	inetd_setproctitle(sep->se_service, s);
16271553Srgrimes	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
16281553Srgrimes	    write(s, buffer, i) > 0)
16291553Srgrimes		;
16301553Srgrimes	exit(0);
16311553Srgrimes}
16321553Srgrimes
16335182Swollmanint check_loop(sin, sep)
16345182Swollman	struct sockaddr_in *sin;
16355182Swollman	struct servtab *sep;
16365182Swollman{
16375182Swollman	struct servtab *se2;
16385182Swollman
16395182Swollman	for (se2 = servtab; se2; se2 = se2->se_next) {
16405182Swollman		if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
16415182Swollman			continue;
16425182Swollman
16435182Swollman		if (sin->sin_port == se2->se_ctrladdr.sin_port) {
16445182Swollman			syslog(LOG_WARNING,
16455182Swollman			       "%s/%s:%s/%s loop request REFUSED from %s",
16468857Srgrimes			       sep->se_service, sep->se_proto,
16475182Swollman			       se2->se_service, se2->se_proto,
16485182Swollman			       inet_ntoa(sin->sin_addr));
16495182Swollman			return 1;
16505182Swollman		}
16515182Swollman	}
16525182Swollman	return 0;
16535182Swollman}
16545182Swollman
16551553Srgrimes/* ARGSUSED */
16561553Srgrimesvoid
16571553Srgrimesecho_dg(s, sep)			/* Echo service -- echo data back */
16581553Srgrimes	int s;
16591553Srgrimes	struct servtab *sep;
16601553Srgrimes{
16611553Srgrimes	char buffer[BUFSIZE];
16621553Srgrimes	int i, size;
16635182Swollman	struct sockaddr_in sin;
16641553Srgrimes
16655182Swollman	size = sizeof(sin);
16668857Srgrimes	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
16675182Swollman			  (struct sockaddr *)&sin, &size)) < 0)
16681553Srgrimes		return;
16695182Swollman
16705182Swollman	if (check_loop(&sin, sep))
16715182Swollman		return;
16725182Swollman
16735182Swollman	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin,
16745182Swollman		      sizeof(sin));
16751553Srgrimes}
16761553Srgrimes
16771553Srgrimes/* ARGSUSED */
16781553Srgrimesvoid
16791553Srgrimesdiscard_stream(s, sep)		/* Discard service -- ignore data */
16801553Srgrimes	int s;
16811553Srgrimes	struct servtab *sep;
16821553Srgrimes{
16831553Srgrimes	int ret;
16841553Srgrimes	char buffer[BUFSIZE];
16851553Srgrimes
168613142Speter	inetd_setproctitle(sep->se_service, s);
16871553Srgrimes	while (1) {
16881553Srgrimes		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
16891553Srgrimes			;
16901553Srgrimes		if (ret == 0 || errno != EINTR)
16911553Srgrimes			break;
16921553Srgrimes	}
16931553Srgrimes	exit(0);
16941553Srgrimes}
16951553Srgrimes
16961553Srgrimes/* ARGSUSED */
16971553Srgrimesvoid
16981553Srgrimesdiscard_dg(s, sep)		/* Discard service -- ignore data */
16991553Srgrimes	int s;
17001553Srgrimes	struct servtab *sep;
17011553Srgrimes{
17021553Srgrimes	char buffer[BUFSIZE];
17031553Srgrimes
17041553Srgrimes	(void) read(s, buffer, sizeof(buffer));
17051553Srgrimes}
17061553Srgrimes
17071553Srgrimes#include <ctype.h>
17081553Srgrimes#define LINESIZ 72
17091553Srgrimeschar ring[128];
17101553Srgrimeschar *endring;
17111553Srgrimes
17121553Srgrimesvoid
17131553Srgrimesinitring()
17141553Srgrimes{
17151553Srgrimes	int i;
17161553Srgrimes
17171553Srgrimes	endring = ring;
17181553Srgrimes
17191553Srgrimes	for (i = 0; i <= 128; ++i)
17201553Srgrimes		if (isprint(i))
17211553Srgrimes			*endring++ = i;
17221553Srgrimes}
17231553Srgrimes
17241553Srgrimes/* ARGSUSED */
17251553Srgrimesvoid
17261553Srgrimeschargen_stream(s, sep)		/* Character generator */
17271553Srgrimes	int s;
17281553Srgrimes	struct servtab *sep;
17291553Srgrimes{
17301553Srgrimes	int len;
17311553Srgrimes	char *rs, text[LINESIZ+2];
17321553Srgrimes
173313142Speter	inetd_setproctitle(sep->se_service, s);
17341553Srgrimes
17351553Srgrimes	if (!endring) {
17361553Srgrimes		initring();
17371553Srgrimes		rs = ring;
17381553Srgrimes	}
17391553Srgrimes
17401553Srgrimes	text[LINESIZ] = '\r';
17411553Srgrimes	text[LINESIZ + 1] = '\n';
17421553Srgrimes	for (rs = ring;;) {
17431553Srgrimes		if ((len = endring - rs) >= LINESIZ)
17441553Srgrimes			memmove(text, rs, LINESIZ);
17451553Srgrimes		else {
17461553Srgrimes			memmove(text, rs, len);
17471553Srgrimes			memmove(text + len, ring, LINESIZ - len);
17481553Srgrimes		}
17491553Srgrimes		if (++rs == endring)
17501553Srgrimes			rs = ring;
17511553Srgrimes		if (write(s, text, sizeof(text)) != sizeof(text))
17521553Srgrimes			break;
17531553Srgrimes	}
17541553Srgrimes	exit(0);
17551553Srgrimes}
17561553Srgrimes
17571553Srgrimes/* ARGSUSED */
17581553Srgrimesvoid
17591553Srgrimeschargen_dg(s, sep)		/* Character generator */
17601553Srgrimes	int s;
17611553Srgrimes	struct servtab *sep;
17621553Srgrimes{
17635182Swollman	struct sockaddr_in sin;
17641553Srgrimes	static char *rs;
17651553Srgrimes	int len, size;
17661553Srgrimes	char text[LINESIZ+2];
17671553Srgrimes
17681553Srgrimes	if (endring == 0) {
17691553Srgrimes		initring();
17701553Srgrimes		rs = ring;
17711553Srgrimes	}
17721553Srgrimes
17735182Swollman	size = sizeof(sin);
17748857Srgrimes	if (recvfrom(s, text, sizeof(text), 0,
17755182Swollman		     (struct sockaddr *)&sin, &size) < 0)
17761553Srgrimes		return;
17771553Srgrimes
17785182Swollman	if (check_loop(&sin, sep))
17795182Swollman		return;
17805182Swollman
17811553Srgrimes	if ((len = endring - rs) >= LINESIZ)
17821553Srgrimes		memmove(text, rs, LINESIZ);
17831553Srgrimes	else {
17841553Srgrimes		memmove(text, rs, len);
17851553Srgrimes		memmove(text + len, ring, LINESIZ - len);
17861553Srgrimes	}
17871553Srgrimes	if (++rs == endring)
17881553Srgrimes		rs = ring;
17891553Srgrimes	text[LINESIZ] = '\r';
17901553Srgrimes	text[LINESIZ + 1] = '\n';
17918857Srgrimes	(void) sendto(s, text, sizeof(text), 0,
17925182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
17931553Srgrimes}
17941553Srgrimes
17951553Srgrimes/*
17961553Srgrimes * Return a machine readable date and time, in the form of the
17971553Srgrimes * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
17981553Srgrimes * returns the number of seconds since midnight, Jan 1, 1970,
17991553Srgrimes * we must add 2208988800 seconds to this figure to make up for
18001553Srgrimes * some seventy years Bell Labs was asleep.
18011553Srgrimes */
18021553Srgrimes
180342311Sdannyunsigned long
18041553Srgrimesmachtime()
18051553Srgrimes{
18061553Srgrimes	struct timeval tv;
18071553Srgrimes
180837856Sache	if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
18091553Srgrimes		if (debug)
181029602Scharnier			warnx("unable to get time of day");
18111553Srgrimes		return (0L);
18121553Srgrimes	}
18131553Srgrimes#define	OFFSET ((u_long)25567 * 24*60*60)
18141553Srgrimes	return (htonl((long)(tv.tv_sec + OFFSET)));
18151553Srgrimes#undef OFFSET
18161553Srgrimes}
18171553Srgrimes
18181553Srgrimes/* ARGSUSED */
18191553Srgrimesvoid
18201553Srgrimesmachtime_stream(s, sep)
18211553Srgrimes	int s;
18221553Srgrimes	struct servtab *sep;
18231553Srgrimes{
182442311Sdanny	unsigned long result;
18251553Srgrimes
18261553Srgrimes	result = machtime();
18271553Srgrimes	(void) write(s, (char *) &result, sizeof(result));
18281553Srgrimes}
18291553Srgrimes
18301553Srgrimes/* ARGSUSED */
18311553Srgrimesvoid
18321553Srgrimesmachtime_dg(s, sep)
18331553Srgrimes	int s;
18341553Srgrimes	struct servtab *sep;
18351553Srgrimes{
183642311Sdanny	unsigned long result;
18375182Swollman	struct sockaddr_in sin;
18381553Srgrimes	int size;
18391553Srgrimes
18405182Swollman	size = sizeof(sin);
18418857Srgrimes	if (recvfrom(s, (char *)&result, sizeof(result), 0,
18425182Swollman		     (struct sockaddr *)&sin, &size) < 0)
18431553Srgrimes		return;
18445182Swollman
18455182Swollman	if (check_loop(&sin, sep))
18465182Swollman		return;
18475182Swollman
18481553Srgrimes	result = machtime();
18498857Srgrimes	(void) sendto(s, (char *) &result, sizeof(result), 0,
18505182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
18511553Srgrimes}
18521553Srgrimes
18531553Srgrimes/* ARGSUSED */
18541553Srgrimesvoid
18551553Srgrimesdaytime_stream(s, sep)		/* Return human-readable time of day */
18561553Srgrimes	int s;
18571553Srgrimes	struct servtab *sep;
18581553Srgrimes{
18591553Srgrimes	char buffer[256];
18601553Srgrimes	time_t clock;
18611553Srgrimes
18621553Srgrimes	clock = time((time_t *) 0);
18631553Srgrimes
18641553Srgrimes	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
18651553Srgrimes	(void) write(s, buffer, strlen(buffer));
18661553Srgrimes}
18671553Srgrimes
18681553Srgrimes/* ARGSUSED */
18691553Srgrimesvoid
18701553Srgrimesdaytime_dg(s, sep)		/* Return human-readable time of day */
18711553Srgrimes	int s;
18721553Srgrimes	struct servtab *sep;
18731553Srgrimes{
18741553Srgrimes	char buffer[256];
18751553Srgrimes	time_t clock;
18765182Swollman	struct sockaddr_in sin;
18771553Srgrimes	int size;
18781553Srgrimes
18791553Srgrimes	clock = time((time_t *) 0);
18801553Srgrimes
18815182Swollman	size = sizeof(sin);
18828857Srgrimes	if (recvfrom(s, buffer, sizeof(buffer), 0,
18835182Swollman		     (struct sockaddr *)&sin, &size) < 0)
18841553Srgrimes		return;
18855182Swollman
18865182Swollman	if (check_loop(&sin, sep))
18875182Swollman		return;
18885182Swollman
18891553Srgrimes	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
18908857Srgrimes	(void) sendto(s, buffer, strlen(buffer), 0,
18915182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
18921553Srgrimes}
18931553Srgrimes
18941553Srgrimes/*
18951553Srgrimes * print_service:
18961553Srgrimes *	Dump relevant information to stderr
18971553Srgrimes */
18981553Srgrimesvoid
18991553Srgrimesprint_service(action, sep)
19001553Srgrimes	char *action;
19011553Srgrimes	struct servtab *sep;
19021553Srgrimes{
190319617Sjulian	fprintf(stderr,
190430792Sache#ifdef LOGIN_CAP
190538380Sjb	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%p server=%s\n",
190630792Sache#else
190738380Sjb	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%p server=%s\n",
190830792Sache#endif
190919617Sjulian	    action, sep->se_service, sep->se_proto,
191030807Sache	    sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group,
191130792Sache#ifdef LOGIN_CAP
191230792Sache	    sep->se_class,
191330792Sache#endif
191438417Sjb	    (void *) sep->se_bi, sep->se_server);
19151553Srgrimes}
19161553Srgrimes
19171553Srgrimes/*
19181553Srgrimes *  Based on TCPMUX.C by Mark K. Lottor November 1988
19191553Srgrimes *  sri-nic::ps:<mkl>tcpmux.c
19201553Srgrimes */
19211553Srgrimes
19221553Srgrimes
19231553Srgrimesstatic int		/* # of characters upto \r,\n or \0 */
19241553Srgrimesgetline(fd, buf, len)
19251553Srgrimes	int fd;
19261553Srgrimes	char *buf;
19271553Srgrimes	int len;
19281553Srgrimes{
19291553Srgrimes	int count = 0, n;
193035848Sguido	struct sigaction sa;
19311553Srgrimes
193235948Sbde	sa.sa_flags = 0;
193335948Sbde	sigemptyset(&sa.sa_mask);
193435848Sguido	sa.sa_handler = SIG_DFL;
193535848Sguido	sigaction(SIGALRM, &sa, (struct sigaction *)0);
19361553Srgrimes	do {
193735829Sguido		alarm(10);
19381553Srgrimes		n = read(fd, buf, len-count);
193935829Sguido		alarm(0);
19401553Srgrimes		if (n == 0)
194142122Sdes			return (count);
194242122Sdes		if (n < 0)
194342122Sdes			return (-1);
19441553Srgrimes		while (--n >= 0) {
194542122Sdes			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
194642122Sdes				return (count);
19471553Srgrimes			count++;
19481553Srgrimes			buf++;
19491553Srgrimes		}
195042122Sdes	} while (count < len);
19511553Srgrimes	return (count);
19521553Srgrimes}
19531553Srgrimes
19541553Srgrimes#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
19551553Srgrimes
19561553Srgrimes#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
19571553Srgrimes
19581553Srgrimesstruct servtab *
19591553Srgrimestcpmux(s)
19601553Srgrimes	int s;
19611553Srgrimes{
19621553Srgrimes	struct servtab *sep;
19631553Srgrimes	char service[MAX_SERV_LEN+1];
19641553Srgrimes	int len;
19651553Srgrimes
19661553Srgrimes	/* Get requested service name */
19671553Srgrimes	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
19681553Srgrimes		strwrite(s, "-Error reading service name\r\n");
19691553Srgrimes		return (NULL);
19701553Srgrimes	}
19711553Srgrimes	service[len] = '\0';
19721553Srgrimes
19731553Srgrimes	if (debug)
197429602Scharnier		warnx("tcpmux: someone wants %s", service);
19751553Srgrimes
19761553Srgrimes	/*
19771553Srgrimes	 * Help is a required command, and lists available services,
19781553Srgrimes	 * one per line.
19791553Srgrimes	 */
19801553Srgrimes	if (!strcasecmp(service, "help")) {
19811553Srgrimes		for (sep = servtab; sep; sep = sep->se_next) {
19821553Srgrimes			if (!ISMUX(sep))
19831553Srgrimes				continue;
19841553Srgrimes			(void)write(s,sep->se_service,strlen(sep->se_service));
19851553Srgrimes			strwrite(s, "\r\n");
19861553Srgrimes		}
19871553Srgrimes		return (NULL);
19881553Srgrimes	}
19891553Srgrimes
19901553Srgrimes	/* Try matching a service in inetd.conf with the request */
19911553Srgrimes	for (sep = servtab; sep; sep = sep->se_next) {
19921553Srgrimes		if (!ISMUX(sep))
19931553Srgrimes			continue;
19941553Srgrimes		if (!strcasecmp(service, sep->se_service)) {
19951553Srgrimes			if (ISMUXPLUS(sep)) {
19961553Srgrimes				strwrite(s, "+Go\r\n");
19971553Srgrimes			}
19981553Srgrimes			return (sep);
19991553Srgrimes		}
20001553Srgrimes	}
20011553Srgrimes	strwrite(s, "-Service not available\r\n");
20021553Srgrimes	return (NULL);
20031553Srgrimes}
200430847Sdima
200530847Sdima#define CPMHSIZE	256
200630847Sdima#define CPMHMASK	(CPMHSIZE-1)
200730847Sdima#define CHTGRAN		10
200830847Sdima#define CHTSIZE		6
200930847Sdima
201030847Sdimatypedef struct CTime {
201130847Sdima	unsigned long 	ct_Ticks;
201230847Sdima	int		ct_Count;
201330847Sdima} CTime;
201430847Sdima
201530847Sdimatypedef struct CHash {
201630847Sdima	struct in_addr	ch_Addr;
201730847Sdima	time_t		ch_LTime;
201830847Sdima	char		*ch_Service;
201930847Sdima	CTime		ch_Times[CHTSIZE];
202030847Sdima} CHash;
202130847Sdima
202230847SdimaCHash	CHashAry[CPMHSIZE];
202330847Sdima
202430847Sdimaint
202530847Sdimacpmip(sep, ctrl)
202630847Sdima	struct servtab *sep;
202730847Sdima	int ctrl;
202830847Sdima{
202930847Sdima	struct sockaddr_in rsin;
203030847Sdima	int rsinLen = sizeof(rsin);
203130847Sdima	int r = 0;
203230847Sdima
203330847Sdima	/*
203430847Sdima	 * If getpeername() fails, just let it through (if logging is
203530847Sdima	 * enabled the condition is caught elsewhere)
203630847Sdima	 */
203730847Sdima
203830847Sdima	if (sep->se_maxcpm > 0 &&
203930847Sdima	    getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 0 ) {
204030847Sdima		time_t t = time(NULL);
204130847Sdima		int hv = 0xABC3D20F;
204230847Sdima		int i;
204330847Sdima		int cnt = 0;
204430847Sdima		CHash *chBest = NULL;
204530847Sdima		unsigned int ticks = t / CHTGRAN;
204630847Sdima
204730847Sdima		{
204830847Sdima			char *p;
204930847Sdima			int i;
205030847Sdima
205130847Sdima			for (i = 0, p = (char *)&rsin.sin_addr;
205230847Sdima			    i < sizeof(rsin.sin_addr);
205330847Sdima			    ++i, ++p) {
205430847Sdima				hv = (hv << 5) ^ (hv >> 23) ^ *p;
205530847Sdima			}
205630847Sdima			hv = (hv ^ (hv >> 16));
205730847Sdima		}
205830847Sdima		for (i = 0; i < 5; ++i) {
205930847Sdima			CHash *ch = &CHashAry[(hv + i) & CPMHMASK];
206030847Sdima
206130847Sdima			if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr &&
206230847Sdima			    ch->ch_Service && strcmp(sep->se_service,
206330847Sdima			    ch->ch_Service) == 0) {
206430847Sdima				chBest = ch;
206530847Sdima				break;
206630847Sdima			}
206730847Sdima			if (chBest == NULL || ch->ch_LTime == 0 ||
206830847Sdima			    ch->ch_LTime < chBest->ch_LTime) {
206930847Sdima				chBest = ch;
207030847Sdima			}
207130847Sdima		}
207230847Sdima		if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr ||
207330847Sdima		    chBest->ch_Service == NULL ||
207430847Sdima		    strcmp(sep->se_service, chBest->ch_Service) != 0) {
207530847Sdima			chBest->ch_Addr = rsin.sin_addr;
207630847Sdima			if (chBest->ch_Service)
207730847Sdima				free(chBest->ch_Service);
207830847Sdima			chBest->ch_Service = strdup(sep->se_service);
207930847Sdima			bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
208030847Sdima		}
208130847Sdima		chBest->ch_LTime = t;
208230847Sdima		{
208330847Sdima			CTime *ct = &chBest->ch_Times[ticks % CHTSIZE];
208430847Sdima			if (ct->ct_Ticks != ticks) {
208530847Sdima				ct->ct_Ticks = ticks;
208630847Sdima				ct->ct_Count = 0;
208730847Sdima			}
208830847Sdima			++ct->ct_Count;
208930847Sdima		}
209030847Sdima		for (i = 0; i < CHTSIZE; ++i) {
209130847Sdima			CTime *ct = &chBest->ch_Times[i];
209230847Sdima			if (ct->ct_Ticks <= ticks &&
209330847Sdima			    ct->ct_Ticks >= ticks - CHTSIZE) {
209430847Sdima				cnt += ct->ct_Count;
209530847Sdima			}
209630847Sdima		}
209730847Sdima		if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) {
209830847Sdima			r = -1;
209930847Sdima			syslog(LOG_ERR,
210033794Spst			    "%s from %s exceeded counts/min (limit %d/min)",
210133794Spst			    sep->se_service, inet_ntoa(rsin.sin_addr),
210233794Spst			    sep->se_maxcpm);
210330847Sdima		}
210430847Sdima	}
210530847Sdima	return(r);
210630847Sdima}
2107