inetd.c revision 47972
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[] =
4547972Ssheldonh	"$Id: inetd.c,v 1.49 1999/05/11 12:50:14 des 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
14947972Ssheldonhint allow_severity;
15047972Ssheldonhint 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 */
21045588Smarkm	char	*se_server_name;	/* server program without path */
2111553Srgrimes#define	MAXARGV 20
2121553Srgrimes	char	*se_argv[MAXARGV+1];	/* program arguments */
2131553Srgrimes	int	se_fd;			/* open descriptor */
2141553Srgrimes	struct	sockaddr_in se_ctrladdr;/* bound address */
21519618Sjulian	u_char	se_type;		/* type: normal, mux, or mux+ */
21619618Sjulian	u_char	se_checked;		/* looked at during merge */
21719618Sjulian	u_char	se_accept;		/* i.e., wait/nowait mode */
21819618Sjulian	u_char	se_rpc;			/* ==1 if RPC service */
2192657Scsgr	int	se_rpc_prog;		/* RPC program number */
2202657Scsgr	u_int	se_rpc_lowvers;		/* RPC low version */
2212657Scsgr	u_int	se_rpc_highvers;	/* RPC high version */
2221553Srgrimes	int	se_count;		/* number started since se_time */
2231553Srgrimes	struct	timeval se_time;	/* start of se_count */
2241553Srgrimes	struct	servtab *se_next;
2251553Srgrimes} *servtab;
2261553Srgrimes
2271553Srgrimes#define NORM_TYPE	0
2281553Srgrimes#define MUX_TYPE	1
2291553Srgrimes#define MUXPLUS_TYPE	2
23036042Sguido#define TTCP_TYPE	3
2311553Srgrimes#define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
2321553Srgrimes			 ((sep)->se_type == MUXPLUS_TYPE))
2331553Srgrimes#define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
23436042Sguido#define ISTTCP(sep)	((sep)->se_type == TTCP_TYPE)
2351553Srgrimes
2361553Srgrimes
2371553Srgrimesvoid		chargen_dg __P((int, struct servtab *));
2381553Srgrimesvoid		chargen_stream __P((int, struct servtab *));
2391553Srgrimesvoid		close_sep __P((struct servtab *));
24042122Sdesvoid		flag_signal __P((char));
24142122Sdesvoid		flag_config __P((int));
24242122Sdesvoid		config __P((void));
2431553Srgrimesvoid		daytime_dg __P((int, struct servtab *));
2441553Srgrimesvoid		daytime_stream __P((int, struct servtab *));
2451553Srgrimesvoid		discard_dg __P((int, struct servtab *));
2461553Srgrimesvoid		discard_stream __P((int, struct servtab *));
2471553Srgrimesvoid		echo_dg __P((int, struct servtab *));
2481553Srgrimesvoid		echo_stream __P((int, struct servtab *));
2491553Srgrimesvoid		endconfig __P((void));
2501553Srgrimesstruct servtab *enter __P((struct servtab *));
2511553Srgrimesvoid		freeconfig __P((struct servtab *));
2521553Srgrimesstruct servtab *getconfigent __P((void));
25340910Sphkvoid		ident_stream __P((int, struct servtab *));
2541553Srgrimesvoid		machtime_dg __P((int, struct servtab *));
2551553Srgrimesvoid		machtime_stream __P((int, struct servtab *));
2561553Srgrimeschar	       *newstr __P((char *));
2571553Srgrimeschar	       *nextline __P((FILE *));
2581553Srgrimesvoid		print_service __P((char *, struct servtab *));
25919618Sjulianvoid		addchild __P((struct servtab *, int));
26042122Sdesvoid		flag_reapchild __P((int));
26142122Sdesvoid		reapchild __P((void));
26219618Sjulianvoid		enable __P((struct servtab *));
26319618Sjulianvoid		disable __P((struct servtab *));
26442122Sdesvoid		flag_retry __P((int));
26542122Sdesvoid		retry __P((void));
2661553Srgrimesint		setconfig __P((void));
2671553Srgrimesvoid		setup __P((struct servtab *));
2681553Srgrimeschar	       *sskip __P((char **));
2691553Srgrimeschar	       *skip __P((char **));
2701553Srgrimesstruct servtab *tcpmux __P((int));
27130847Sdimaint		cpmip __P((struct servtab *, int));
2721553Srgrimes
2732657Scsgrvoid		unregisterrpc __P((register struct servtab *sep));
2742657Scsgr
2751553Srgrimesstruct biltin {
2761553Srgrimes	char	*bi_service;		/* internally provided service name */
2771553Srgrimes	int	bi_socktype;		/* type of socket supported */
2781553Srgrimes	short	bi_fork;		/* 1 if should fork before call */
27930847Sdima	int	bi_maxchild;		/* max number of children (default) */
2801553Srgrimes	void	(*bi_fn)();		/* function which performs it */
2811553Srgrimes} biltins[] = {
2821553Srgrimes	/* Echo received data */
2831553Srgrimes	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream },
2841553Srgrimes	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg },
2851553Srgrimes
2861553Srgrimes	/* Internet /dev/null */
2871553Srgrimes	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream },
2881553Srgrimes	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg },
2891553Srgrimes
29045089Smarkm	/* Return 32 bit time since 1970 */
2911553Srgrimes	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream },
2921553Srgrimes	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg },
2931553Srgrimes
2941553Srgrimes	/* Return human-readable time */
2951553Srgrimes	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream },
2961553Srgrimes	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg },
2971553Srgrimes
2981553Srgrimes	/* Familiar character generator */
2991553Srgrimes	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream },
3001553Srgrimes	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg },
3011553Srgrimes
3021553Srgrimes	{ "tcpmux",	SOCK_STREAM,	1, 0,	(void (*)())tcpmux },
3031553Srgrimes
30440910Sphk	{ "ident",	SOCK_STREAM,	1, 0,	ident_stream },
30540910Sphk
3061553Srgrimes	{ NULL }
3071553Srgrimes};
3081553Srgrimes
3091553Srgrimes#define NUMINT	(sizeof(intab) / sizeof(struct inent))
3101553Srgrimeschar	*CONFIG = _PATH_INETDCONF;
31117482Sjulianchar	*pid_file = _PATH_INETDPID;
31213142Speter
31313142Speter#ifdef OLD_SETPROCTITLE
3141553Srgrimeschar	**Argv;
3151553Srgrimeschar 	*LastArg;
31613142Speter#endif
3171553Srgrimes
3181553Srgrimesint
31933794Spstgetvalue(arg, value, whine)
32033794Spst	char *arg, *whine;
32133794Spst	int  *value;
32233794Spst{
32333794Spst	int  tmp;
32433794Spst	char *p;
32533794Spst
32633794Spst	tmp = strtol(arg, &p, 0);
32733794Spst	if (tmp < 1 || *p) {
32833794Spst		syslog(LOG_ERR, whine, arg);
32933794Spst		return 1;			/* failure */
33033794Spst	}
33133794Spst	*value = tmp;
33233794Spst	return 0;				/* success */
33333794Spst}
33433794Spst
33533794Spstint
3361553Srgrimesmain(argc, argv, envp)
3371553Srgrimes	int argc;
3381553Srgrimes	char *argv[], *envp[];
3391553Srgrimes{
3401553Srgrimes	struct servtab *sep;
3411553Srgrimes	struct passwd *pwd;
34230807Sache	struct group *grp;
34335848Sguido	struct sigaction sa, sapipe;
3441553Srgrimes	int tmpint, ch, dofork;
3451553Srgrimes	pid_t pid;
3461553Srgrimes	char buf[50];
34721640Speter#ifdef LOGIN_CAP
34821640Speter	login_cap_t *lc = NULL;
34921640Speter#endif
35045089Smarkm#ifdef LIBWRAP
35145089Smarkm	struct request_info req;
35245089Smarkm	int denied;
35345089Smarkm	char *service = NULL;
35447972Ssheldonh#else
35547972Ssheldonh	struct  sockaddr_in peer;
35647972Ssheldonh	int i;
35745089Smarkm#endif
3581553Srgrimes
35913142Speter
36013142Speter#ifdef OLD_SETPROCTITLE
3611553Srgrimes	Argv = argv;
3621553Srgrimes	if (envp == 0 || *envp == 0)
3631553Srgrimes		envp = argv;
3641553Srgrimes	while (*envp)
3651553Srgrimes		envp++;
3661553Srgrimes	LastArg = envp[-1] + strlen(envp[-1]);
36713142Speter#endif
3681553Srgrimes
3691553Srgrimes	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
3701553Srgrimes
37117482Sjulian	bind_address.s_addr = htonl(INADDR_ANY);
37233794Spst	while ((ch = getopt(argc, argv, "dlR:a:c:C:p:")) != -1)
3731553Srgrimes		switch(ch) {
3741553Srgrimes		case 'd':
3751553Srgrimes			debug = 1;
3761553Srgrimes			options |= SO_DEBUG;
3771553Srgrimes			break;
3782659Scsgr		case 'l':
3792659Scsgr			log = 1;
3802659Scsgr			break;
38133794Spst		case 'R':
38233794Spst			getvalue(optarg, &toomany,
38333794Spst				"-R %s: bad value for service invocation rate");
3841553Srgrimes			break;
38533794Spst		case 'c':
38633794Spst			getvalue(optarg, &maxchild,
38733794Spst				"-c %s: bad value for maximum children");
38833794Spst			break;
38933794Spst		case 'C':
39033794Spst			getvalue(optarg, &maxcpm,
39133794Spst				"-C %s: bad value for maximum children/minute");
39233794Spst			break;
39317482Sjulian		case 'a':
39417482Sjulian			if (!inet_aton(optarg, &bind_address)) {
39517482Sjulian				syslog(LOG_ERR,
39617482Sjulian			         "-a %s: invalid IP address", optarg);
39719617Sjulian				exit(EX_USAGE);
39817482Sjulian			}
39917482Sjulian			break;
40017482Sjulian		case 'p':
40117482Sjulian			pid_file = optarg;
40217482Sjulian			break;
4031553Srgrimes		case '?':
4041553Srgrimes		default:
4051553Srgrimes			syslog(LOG_ERR,
40617482Sjulian				"usage: inetd [-dl] [-a address] [-R rate]"
40733794Spst				" [-c maximum] [-C rate]"
40817482Sjulian				" [-p pidfile] [conf-file]");
40919617Sjulian			exit(EX_USAGE);
4101553Srgrimes		}
4111553Srgrimes	argc -= optind;
4121553Srgrimes	argv += optind;
4131553Srgrimes
4141553Srgrimes	if (argc > 0)
4151553Srgrimes		CONFIG = argv[0];
4161553Srgrimes	if (debug == 0) {
41711447Swollman		FILE *fp;
41812024Speter		if (daemon(0, 0) < 0) {
41912024Speter			syslog(LOG_WARNING, "daemon(0,0) failed: %m");
42012024Speter		}
42112024Speter		/*
42212024Speter		 * In case somebody has started inetd manually, we need to
42312024Speter		 * clear the logname, so that old servers run as root do not
42412024Speter		 * get the user's logname..
42512024Speter		 */
42612024Speter		if (setlogin("") < 0) {
42712024Speter			syslog(LOG_WARNING, "cannot clear logname: %m");
42812024Speter			/* no big deal if it fails.. */
42912024Speter		}
43011447Swollman		pid = getpid();
43117482Sjulian		fp = fopen(pid_file, "w");
43211447Swollman		if (fp) {
43311447Swollman			fprintf(fp, "%ld\n", (long)pid);
43411447Swollman			fclose(fp);
43511447Swollman		} else {
43617482Sjulian			syslog(LOG_WARNING, "%s: %m", pid_file);
43711447Swollman		}
4381553Srgrimes	}
43935948Sbde	sa.sa_flags = 0;
44035948Sbde	sigemptyset(&sa.sa_mask);
44135948Sbde	sigaddset(&sa.sa_mask, SIGALRM);
44235948Sbde	sigaddset(&sa.sa_mask, SIGCHLD);
44335948Sbde	sigaddset(&sa.sa_mask, SIGHUP);
44442122Sdes	sa.sa_handler = flag_retry;
44535848Sguido	sigaction(SIGALRM, &sa, (struct sigaction *)0);
44642122Sdes	config();
44742122Sdes	sa.sa_handler = flag_config;
44835848Sguido	sigaction(SIGHUP, &sa, (struct sigaction *)0);
44942122Sdes	sa.sa_handler = flag_reapchild;
45035848Sguido	sigaction(SIGCHLD, &sa, (struct sigaction *)0);
45135848Sguido	sa.sa_handler = SIG_IGN;
45235948Sbde	sigaction(SIGPIPE, &sa, &sapipe);
4531553Srgrimes
4541553Srgrimes	{
4551553Srgrimes		/* space for daemons to overwrite environment for ps */
4561553Srgrimes#define	DUMMYSIZE	100
4571553Srgrimes		char dummy[DUMMYSIZE];
4581553Srgrimes
45919298Salex		(void)memset(dummy, 'x', DUMMYSIZE - 1);
4601553Srgrimes		dummy[DUMMYSIZE - 1] = '\0';
4611553Srgrimes		(void)setenv("inetd_dummy", dummy, 1);
4621553Srgrimes	}
4631553Srgrimes
46442250Sdes	if (pipe(signalpipe) != 0) {
46542250Sdes		syslog(LOG_ERR, "pipe: %%m");
46642250Sdes		exit(EX_OSERR);
46742122Sdes	}
46842122Sdes	FD_SET(signalpipe[0], &allsock);
46947015Sdes	nsock++;
47047015Sdes	if (signalpipe[0] > maxsock)
47147015Sdes	    maxsock = signalpipe[0];
47241685Sdillon
4731553Srgrimes	for (;;) {
4741553Srgrimes	    int n, ctrl;
4751553Srgrimes	    fd_set readable;
4761553Srgrimes
4771553Srgrimes	    if (nsock == 0) {
47847015Sdes		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
47947015Sdes		exit(EX_SOFTWARE);
4801553Srgrimes	    }
4811553Srgrimes	    readable = allsock;
48242122Sdes	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
48342122Sdes		(fd_set *)0, (struct timeval *)0)) <= 0) {
48442122Sdes		    if (n < 0 && errno != EINTR) {
4851553Srgrimes			syslog(LOG_WARNING, "select: %m");
48628907Simp			sleep(1);
48728907Simp		    }
4881553Srgrimes		    continue;
4891553Srgrimes	    }
49042122Sdes	    /* handle any queued signal flags */
49142250Sdes	    if (FD_ISSET(signalpipe[0], &readable)) {
49242122Sdes		int n;
49342250Sdes		if (ioctl(signalpipe[0], FIONREAD, &n) != 0) {
49442122Sdes		    syslog(LOG_ERR, "ioctl: %m");
49542122Sdes		    exit(EX_OSERR);
49642122Sdes		}
49742250Sdes		while (--n >= 0) {
49842250Sdes		    char c;
49942250Sdes		    if (read(signalpipe[0], &c, 1) != 1) {
50042250Sdes			syslog(LOG_ERR, "read: %m");
50142250Sdes			exit(EX_OSERR);
50242250Sdes		    }
50342250Sdes		    if (debug)
50442250Sdes			warnx("Handling signal flag %c", c);
50542250Sdes		    switch(c) {
50642250Sdes		    case 'A': /* sigalrm */
50742250Sdes			retry();
50842250Sdes			break;
50942250Sdes		    case 'C': /* sigchld */
51042250Sdes			reapchild();
51142250Sdes			break;
51242250Sdes		    case 'H': /* sighup */
51342250Sdes			config();
51442250Sdes			break;
51542250Sdes		    }
51642250Sdes		}
51742122Sdes	    }
5181553Srgrimes	    for (sep = servtab; n && sep; sep = sep->se_next)
5191553Srgrimes	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
5201553Srgrimes		    n--;
5211553Srgrimes		    if (debug)
52229602Scharnier			    warnx("someone wants %s", sep->se_service);
52319618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
5241553Srgrimes			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
5251553Srgrimes				(int *)0);
5261553Srgrimes			    if (debug)
52729602Scharnier				    warnx("accept, ctrl %d", ctrl);
5281553Srgrimes			    if (ctrl < 0) {
5291553Srgrimes				    if (errno != EINTR)
5301553Srgrimes					    syslog(LOG_WARNING,
5311553Srgrimes						"accept (for %s): %m",
53237844Sphk						sep->se_service);
53337816Sphk                                      if (sep->se_accept &&
53437816Sphk                                          sep->se_socktype == SOCK_STREAM)
53537816Sphk                                              close(ctrl);
5361553Srgrimes				    continue;
5371553Srgrimes			    }
53830847Sdima			    if (cpmip(sep, ctrl) < 0) {
53930847Sdima				close(ctrl);
54030847Sdima				continue;
54130847Sdima			    }
54247972Ssheldonh#ifndef LIBWRAP
54319617Sjulian			    if (log) {
5442659Scsgr				i = sizeof peer;
54530847Sdima				if (getpeername(ctrl, (struct sockaddr *)
5462659Scsgr						&peer, &i)) {
5472659Scsgr					syslog(LOG_WARNING,
5482659Scsgr						"getpeername(for %s): %m",
5492659Scsgr						sep->se_service);
55030847Sdima					close(ctrl);
5512659Scsgr					continue;
5522659Scsgr				}
5532659Scsgr				syslog(LOG_INFO,"%s from %s",
5542659Scsgr					sep->se_service,
5552659Scsgr					inet_ntoa(peer.sin_addr));
5562659Scsgr			    }
55747972Ssheldonh#endif
5581553Srgrimes		    } else
5591553Srgrimes			    ctrl = sep->se_fd;
56042122Sdes		    (void) sigblock(SIGBLOCK);
5611553Srgrimes		    pid = 0;
56245089Smarkm#ifdef LIBWRAP_INTERNAL
56347972Ssheldonh		    /*
56447972Ssheldonh		     * When builtins are wrapped, avoid a minor optimization
56547972Ssheldonh		     * that breaks hosts_options(5) twist.
56647972Ssheldonh		     */
56745089Smarkm		    dofork = 1;
56845089Smarkm#else
5691553Srgrimes		    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
57045089Smarkm#endif
5711553Srgrimes		    if (dofork) {
5721553Srgrimes			    if (sep->se_count++ == 0)
57337856Sache				(void)gettimeofday(&sep->se_time, (struct timezone *)NULL);
5741553Srgrimes			    else if (sep->se_count >= toomany) {
5751553Srgrimes				struct timeval now;
5761553Srgrimes
57737856Sache				(void)gettimeofday(&now, (struct timezone *)NULL);
5781553Srgrimes				if (now.tv_sec - sep->se_time.tv_sec >
5791553Srgrimes				    CNT_INTVL) {
5801553Srgrimes					sep->se_time = now;
5811553Srgrimes					sep->se_count = 1;
5821553Srgrimes				} else {
5831553Srgrimes					syslog(LOG_ERR,
5841553Srgrimes			"%s/%s server failing (looping), service terminated",
5851553Srgrimes					    sep->se_service, sep->se_proto);
5861553Srgrimes					close_sep(sep);
58742122Sdes					sigsetmask(0L);
5881553Srgrimes					if (!timingout) {
5891553Srgrimes						timingout = 1;
5901553Srgrimes						alarm(RETRYTIME);
5911553Srgrimes					}
5921553Srgrimes					continue;
5931553Srgrimes				}
5941553Srgrimes			    }
5951553Srgrimes			    pid = fork();
5961553Srgrimes		    }
5971553Srgrimes		    if (pid < 0) {
5981553Srgrimes			    syslog(LOG_ERR, "fork: %m");
59919618Sjulian			    if (sep->se_accept &&
6001553Srgrimes				sep->se_socktype == SOCK_STREAM)
6011553Srgrimes				    close(ctrl);
60242122Sdes			    sigsetmask(0L);
6031553Srgrimes			    sleep(1);
6041553Srgrimes			    continue;
6051553Srgrimes		    }
60619618Sjulian		    if (pid)
60719618Sjulian			addchild(sep, pid);
60842122Sdes		    sigsetmask(0L);
6091553Srgrimes		    if (pid == 0) {
6101553Srgrimes			    if (dofork) {
6111553Srgrimes				if (debug)
61229602Scharnier					warnx("+ closing from %d", maxsock);
6131553Srgrimes				for (tmpint = maxsock; tmpint > 2; tmpint--)
6141553Srgrimes					if (tmpint != ctrl)
61519617Sjulian						(void) close(tmpint);
6161553Srgrimes			    }
61735829Sguido			    /*
61835829Sguido			     * Call tcpmux to find the real service to exec.
61935829Sguido			     */
62035829Sguido			    if (sep->se_bi &&
62135829Sguido				sep->se_bi->bi_fn == (void (*)()) tcpmux) {
62235829Sguido				    sep = tcpmux(ctrl);
62335829Sguido				    if (sep == NULL) {
62435829Sguido					    close(ctrl);
62535829Sguido					    _exit(0);
62635829Sguido				    }
62735829Sguido			    }
62845089Smarkm#ifdef LIBWRAP
62945089Smarkm#ifndef LIBWRAP_INTERNAL
63045089Smarkm			    if (sep->se_bi == 0)
63145089Smarkm#endif
63245089Smarkm			    if (sep->se_accept
63345089Smarkm				&& sep->se_socktype == SOCK_STREAM) {
63447972Ssheldonh				service = sep->se_server_name ?
63547972Ssheldonh				    sep->se_server_name : sep->se_service;
63647972Ssheldonh				request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, NULL);
63745089Smarkm				fromhost(&req);
63847972Ssheldonh				deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
63947972Ssheldonh				allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
64045089Smarkm				denied = !hosts_access(&req);
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);
7481553Srgrimes			    }
74947972Ssheldonh			    if (dofork)
75047972Ssheldonh				_exit(0);
7511553Srgrimes		    }
75219618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
7531553Srgrimes			    close(ctrl);
7541553Srgrimes		}
7551553Srgrimes	}
7561553Srgrimes}
7571553Srgrimes
75819618Sjulian/*
75942122Sdes * Add a signal flag to the signal flag queue for later handling
76042122Sdes */
76142122Sdes
76242122Sdesvoid flag_signal(c)
76342122Sdes    char c;
76442122Sdes{
76542250Sdes	if (write(signalpipe[1], &c, 1) != 1) {
76642250Sdes		syslog(LOG_ERR, "write: %m");
76742250Sdes		exit(EX_OSERR);
76842250Sdes	}
76942122Sdes}
77042122Sdes
77142122Sdes/*
77219618Sjulian * Record a new child pid for this service. If we've reached the
77319618Sjulian * limit on children, then stop accepting incoming requests.
77419618Sjulian */
77519618Sjulian
7761553Srgrimesvoid
77719618Sjulianaddchild(struct servtab *sep, pid_t pid)
77819618Sjulian{
77919618Sjulian#ifdef SANITY_CHECK
78019618Sjulian	if (sep->se_numchild >= sep->se_maxchild) {
78119618Sjulian		syslog(LOG_ERR, "%s: %d >= %d",
78219618Sjulian		    __FUNCTION__, sep->se_numchild, sep->se_maxchild);
78319618Sjulian		exit(EX_SOFTWARE);
78419618Sjulian	}
78519618Sjulian#endif
78619618Sjulian	if (sep->se_maxchild == 0)
78719618Sjulian		return;
78819618Sjulian	sep->se_pids[sep->se_numchild++] = pid;
78919618Sjulian	if (sep->se_numchild == sep->se_maxchild)
79019618Sjulian		disable(sep);
79119618Sjulian}
79219618Sjulian
79319618Sjulian/*
79419618Sjulian * Some child process has exited. See if it's on somebody's list.
79519618Sjulian */
79619618Sjulian
79719618Sjulianvoid
79842122Sdesflag_reapchild(signo)
7991553Srgrimes	int signo;
8001553Srgrimes{
80142250Sdes	flag_signal('C');
80242122Sdes}
80342122Sdes
80442122Sdesvoid
80542122Sdesreapchild()
80642122Sdes{
80719618Sjulian	int k, status;
8081553Srgrimes	pid_t pid;
8091553Srgrimes	struct servtab *sep;
8101553Srgrimes
8111553Srgrimes	for (;;) {
8121553Srgrimes		pid = wait3(&status, WNOHANG, (struct rusage *)0);
8131553Srgrimes		if (pid <= 0)
8141553Srgrimes			break;
8151553Srgrimes		if (debug)
81629602Scharnier			warnx("%d reaped, status %#x", pid, status);
81719618Sjulian		for (sep = servtab; sep; sep = sep->se_next) {
81819618Sjulian			for (k = 0; k < sep->se_numchild; k++)
81919618Sjulian				if (sep->se_pids[k] == pid)
82019618Sjulian					break;
82119618Sjulian			if (k == sep->se_numchild)
82219618Sjulian				continue;
82319618Sjulian			if (sep->se_numchild == sep->se_maxchild)
82419618Sjulian				enable(sep);
82519618Sjulian			sep->se_pids[k] = sep->se_pids[--sep->se_numchild];
82619618Sjulian			if (status)
82719618Sjulian				syslog(LOG_WARNING,
82819618Sjulian				    "%s[%d]: exit status 0x%x",
82919618Sjulian				    sep->se_server, pid, status);
83019618Sjulian			break;
83119618Sjulian		}
8321553Srgrimes	}
8331553Srgrimes}
8341553Srgrimes
8351553Srgrimesvoid
83642122Sdesflag_config(signo)
8371553Srgrimes	int signo;
8381553Srgrimes{
83942250Sdes	flag_signal('H');
84042122Sdes}
84142122Sdes
84242122Sdesvoid config()
84342122Sdes{
84419618Sjulian	struct servtab *sep, *new, **sepp;
84542122Sdes	long omask;
8461553Srgrimes
8471553Srgrimes	if (!setconfig()) {
8481553Srgrimes		syslog(LOG_ERR, "%s: %m", CONFIG);
8491553Srgrimes		return;
8501553Srgrimes	}
8511553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
8521553Srgrimes		sep->se_checked = 0;
85319618Sjulian	while ((new = getconfigent())) {
85430807Sache		if (getpwnam(new->se_user) == NULL) {
8551553Srgrimes			syslog(LOG_ERR,
8561553Srgrimes				"%s/%s: No such user '%s', service ignored",
85719618Sjulian				new->se_service, new->se_proto, new->se_user);
8581553Srgrimes			continue;
8591553Srgrimes		}
86030807Sache		if (new->se_group && getgrnam(new->se_group) == NULL) {
86130807Sache			syslog(LOG_ERR,
86230807Sache				"%s/%s: No such group '%s', service ignored",
86330807Sache				new->se_service, new->se_proto, new->se_group);
86430807Sache			continue;
86530807Sache		}
86630792Sache#ifdef LOGIN_CAP
86730792Sache		if (login_getclass(new->se_class) == NULL) {
86830792Sache			/* error syslogged by getclass */
86930792Sache			syslog(LOG_ERR,
87037850Sache				"%s/%s: %s: login class error, service ignored",
87137850Sache				new->se_service, new->se_proto, new->se_class);
87230792Sache			continue;
87330792Sache		}
87430792Sache#endif
8751553Srgrimes		for (sep = servtab; sep; sep = sep->se_next)
87619618Sjulian			if (strcmp(sep->se_service, new->se_service) == 0 &&
87719618Sjulian			    strcmp(sep->se_proto, new->se_proto) == 0)
8781553Srgrimes				break;
8791553Srgrimes		if (sep != 0) {
8801553Srgrimes			int i;
8811553Srgrimes
88219618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; }
88342122Sdes			omask = sigblock(SIGBLOCK);
88419618Sjulian			/* copy over outstanding child pids */
88519618Sjulian			if (sep->se_maxchild && new->se_maxchild) {
88619618Sjulian				new->se_numchild = sep->se_numchild;
88719618Sjulian				if (new->se_numchild > new->se_maxchild)
88819618Sjulian					new->se_numchild = new->se_maxchild;
88919618Sjulian				memcpy(new->se_pids, sep->se_pids,
89019618Sjulian				    new->se_numchild * sizeof(*new->se_pids));
89119618Sjulian			}
89219618Sjulian			SWAP(sep->se_pids, new->se_pids);
89319618Sjulian			sep->se_maxchild = new->se_maxchild;
89419618Sjulian			sep->se_numchild = new->se_numchild;
89530847Sdima			sep->se_maxcpm = new->se_maxcpm;
89619618Sjulian			/* might need to turn on or off service now */
89719618Sjulian			if (sep->se_fd >= 0) {
89819618Sjulian			      if (sep->se_maxchild
89919618Sjulian				  && sep->se_numchild == sep->se_maxchild) {
90019618Sjulian				      if (FD_ISSET(sep->se_fd, &allsock))
90119618Sjulian					  disable(sep);
90219618Sjulian			      } else {
90319618Sjulian				      if (!FD_ISSET(sep->se_fd, &allsock))
90419618Sjulian					  enable(sep);
90519618Sjulian			      }
90619618Sjulian			}
90719618Sjulian			sep->se_accept = new->se_accept;
90830807Sache			SWAP(sep->se_user, new->se_user);
90930807Sache			SWAP(sep->se_group, new->se_group);
91030792Sache#ifdef LOGIN_CAP
91130807Sache			SWAP(sep->se_class, new->se_class);
91230792Sache#endif
91330807Sache			SWAP(sep->se_server, new->se_server);
91447972Ssheldonh			SWAP(sep->se_server_name, new->se_server_name);
9151553Srgrimes			for (i = 0; i < MAXARGV; i++)
91619618Sjulian				SWAP(sep->se_argv[i], new->se_argv[i]);
91742122Sdes			sigsetmask(omask);
91819618Sjulian			freeconfig(new);
9191553Srgrimes			if (debug)
9201553Srgrimes				print_service("REDO", sep);
9211553Srgrimes		} else {
92219618Sjulian			sep = enter(new);
9231553Srgrimes			if (debug)
9241553Srgrimes				print_service("ADD ", sep);
9251553Srgrimes		}
9261553Srgrimes		sep->se_checked = 1;
9271553Srgrimes		if (ISMUX(sep)) {
9281553Srgrimes			sep->se_fd = -1;
9291553Srgrimes			continue;
9301553Srgrimes		}
9312657Scsgr		if (!sep->se_rpc) {
9322657Scsgr			sp = getservbyname(sep->se_service, sep->se_proto);
9332657Scsgr			if (sp == 0) {
9342657Scsgr				syslog(LOG_ERR, "%s/%s: unknown service",
9352657Scsgr			    	sep->se_service, sep->se_proto);
9362657Scsgr				sep->se_checked = 0;
9372657Scsgr				continue;
9382657Scsgr			}
9392657Scsgr			if (sp->s_port != sep->se_ctrladdr.sin_port) {
9402657Scsgr				sep->se_ctrladdr.sin_family = AF_INET;
94122306Sjulian				sep->se_ctrladdr.sin_addr = bind_address;
9422657Scsgr				sep->se_ctrladdr.sin_port = sp->s_port;
9432657Scsgr				if (sep->se_fd >= 0)
9442657Scsgr					close_sep(sep);
9452657Scsgr			}
9462657Scsgr		} else {
9472657Scsgr			rpc = getrpcbyname(sep->se_service);
9482657Scsgr			if (rpc == 0) {
9492657Scsgr				syslog(LOG_ERR, "%s/%s unknown RPC service.",
9502657Scsgr					sep->se_service, sep->se_proto);
9512657Scsgr				if (sep->se_fd != -1)
9522657Scsgr					(void) close(sep->se_fd);
9532657Scsgr				sep->se_fd = -1;
9542657Scsgr					continue;
9552657Scsgr			}
9562657Scsgr			if (rpc->r_number != sep->se_rpc_prog) {
9572657Scsgr				if (sep->se_rpc_prog)
9582657Scsgr					unregisterrpc(sep);
9592657Scsgr				sep->se_rpc_prog = rpc->r_number;
9602657Scsgr				if (sep->se_fd != -1)
9612657Scsgr					(void) close(sep->se_fd);
9622657Scsgr				sep->se_fd = -1;
9632657Scsgr			}
9641553Srgrimes		}
9651553Srgrimes		if (sep->se_fd == -1)
9661553Srgrimes			setup(sep);
9671553Srgrimes	}
9681553Srgrimes	endconfig();
9691553Srgrimes	/*
9701553Srgrimes	 * Purge anything not looked at above.
9711553Srgrimes	 */
97242122Sdes	omask = sigblock(SIGBLOCK);
9731553Srgrimes	sepp = &servtab;
97419617Sjulian	while ((sep = *sepp)) {
9751553Srgrimes		if (sep->se_checked) {
9761553Srgrimes			sepp = &sep->se_next;
9771553Srgrimes			continue;
9781553Srgrimes		}
9791553Srgrimes		*sepp = sep->se_next;
9801553Srgrimes		if (sep->se_fd >= 0)
9811553Srgrimes			close_sep(sep);
9821553Srgrimes		if (debug)
9831553Srgrimes			print_service("FREE", sep);
9842657Scsgr		if (sep->se_rpc && sep->se_rpc_prog > 0)
9852657Scsgr			unregisterrpc(sep);
9861553Srgrimes		freeconfig(sep);
9871553Srgrimes		free((char *)sep);
9881553Srgrimes	}
98942122Sdes	(void) sigsetmask(omask);
9901553Srgrimes}
9911553Srgrimes
9921553Srgrimesvoid
9932657Scsgrunregisterrpc(sep)
9942657Scsgr	struct servtab *sep;
9952657Scsgr{
9962657Scsgr        int i;
9972657Scsgr        struct servtab *sepp;
99842122Sdes	long omask;
9992657Scsgr
100042122Sdes	omask = sigblock(SIGBLOCK);
10012657Scsgr        for (sepp = servtab; sepp; sepp = sepp->se_next) {
10022657Scsgr                if (sepp == sep)
10032657Scsgr                        continue;
10042657Scsgr		if (sep->se_checked == 0 ||
10052657Scsgr                    !sepp->se_rpc ||
10062657Scsgr                    sep->se_rpc_prog != sepp->se_rpc_prog)
10072657Scsgr			continue;
10082657Scsgr                return;
10092657Scsgr        }
10102657Scsgr        if (debug)
10112657Scsgr                print_service("UNREG", sep);
10122657Scsgr        for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++)
10132657Scsgr                pmap_unset(sep->se_rpc_prog, i);
10142657Scsgr        if (sep->se_fd != -1)
10152657Scsgr                (void) close(sep->se_fd);
10162657Scsgr        sep->se_fd = -1;
101742122Sdes	(void) sigsetmask(omask);
10182657Scsgr}
10192657Scsgr
10202657Scsgrvoid
102142122Sdesflag_retry(signo)
10221553Srgrimes	int signo;
10231553Srgrimes{
102442250Sdes	flag_signal('A');
102542122Sdes}
102642122Sdes
102742122Sdesvoid
102842122Sdesretry()
102942122Sdes{
10301553Srgrimes	struct servtab *sep;
10311553Srgrimes
10321553Srgrimes	timingout = 0;
10331553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
103419617Sjulian		if (sep->se_fd == -1 && !ISMUX(sep))
10351553Srgrimes			setup(sep);
10361553Srgrimes}
10371553Srgrimes
10381553Srgrimesvoid
10391553Srgrimessetup(sep)
10401553Srgrimes	struct servtab *sep;
10411553Srgrimes{
10421553Srgrimes	int on = 1;
10431553Srgrimes
10441553Srgrimes	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
10451553Srgrimes		if (debug)
104629602Scharnier			warn("socket failed on %s/%s",
104729602Scharnier				sep->se_service, sep->se_proto);
10481553Srgrimes		syslog(LOG_ERR, "%s/%s: socket: %m",
10491553Srgrimes		    sep->se_service, sep->se_proto);
10501553Srgrimes		return;
10511553Srgrimes	}
10521553Srgrimes#define	turnon(fd, opt) \
10531553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
10541553Srgrimes	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
10551553Srgrimes	    turnon(sep->se_fd, SO_DEBUG) < 0)
10561553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
10571553Srgrimes	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
10581553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
105925253Swollman#ifdef SO_PRIVSTATE
106013956Swollman	if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
106113956Swollman		syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
106225253Swollman#endif
10631553Srgrimes#undef turnon
106436042Sguido	if (sep->se_type == TTCP_TYPE)
106536042Sguido		if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH,
106636042Sguido		    (char *)&on, sizeof (on)) < 0)
106736042Sguido			syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m");
10681553Srgrimes	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
10691553Srgrimes	    sizeof (sep->se_ctrladdr)) < 0) {
10701553Srgrimes		if (debug)
107129602Scharnier			warn("bind failed on %s/%s",
107229602Scharnier				sep->se_service, sep->se_proto);
10731553Srgrimes		syslog(LOG_ERR, "%s/%s: bind: %m",
10741553Srgrimes		    sep->se_service, sep->se_proto);
10751553Srgrimes		(void) close(sep->se_fd);
10761553Srgrimes		sep->se_fd = -1;
10771553Srgrimes		if (!timingout) {
10781553Srgrimes			timingout = 1;
10791553Srgrimes			alarm(RETRYTIME);
10801553Srgrimes		}
10811553Srgrimes		return;
10821553Srgrimes	}
10832657Scsgr        if (sep->se_rpc) {
10842657Scsgr                int i, len = sizeof(struct sockaddr);
10852657Scsgr
10868857Srgrimes                if (getsockname(sep->se_fd,
10872657Scsgr				(struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
10882657Scsgr                        syslog(LOG_ERR, "%s/%s: getsockname: %m",
10892657Scsgr                               sep->se_service, sep->se_proto);
10902657Scsgr                        (void) close(sep->se_fd);
10912657Scsgr                        sep->se_fd = -1;
10928857Srgrimes                        return;
10932657Scsgr                }
10942657Scsgr                if (debug)
10952657Scsgr                        print_service("REG ", sep);
10962657Scsgr                for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
10972657Scsgr                        pmap_unset(sep->se_rpc_prog, i);
10982657Scsgr                        pmap_set(sep->se_rpc_prog, i,
10992657Scsgr                                 (sep->se_socktype == SOCK_DGRAM)
11002657Scsgr                                 ? IPPROTO_UDP : IPPROTO_TCP,
11012657Scsgr                                 ntohs(sep->se_ctrladdr.sin_port));
11022657Scsgr                }
11038857Srgrimes
11042657Scsgr        }
11051553Srgrimes	if (sep->se_socktype == SOCK_STREAM)
110617197Sdg		listen(sep->se_fd, 64);
110719618Sjulian	enable(sep);
11081553Srgrimes	if (debug) {
110929602Scharnier		warnx("registered %s on %d",
11101553Srgrimes			sep->se_server, sep->se_fd);
11111553Srgrimes	}
11121553Srgrimes}
11131553Srgrimes
11141553Srgrimes/*
11151553Srgrimes * Finish with a service and its socket.
11161553Srgrimes */
11171553Srgrimesvoid
11181553Srgrimesclose_sep(sep)
11191553Srgrimes	struct servtab *sep;
11201553Srgrimes{
11211553Srgrimes	if (sep->se_fd >= 0) {
112219618Sjulian		if (FD_ISSET(sep->se_fd, &allsock))
112319618Sjulian			disable(sep);
11241553Srgrimes		(void) close(sep->se_fd);
11251553Srgrimes		sep->se_fd = -1;
11261553Srgrimes	}
11271553Srgrimes	sep->se_count = 0;
112819618Sjulian	sep->se_numchild = 0;	/* forget about any existing children */
11291553Srgrimes}
11301553Srgrimes
11311553Srgrimesstruct servtab *
11321553Srgrimesenter(cp)
11331553Srgrimes	struct servtab *cp;
11341553Srgrimes{
11351553Srgrimes	struct servtab *sep;
113642122Sdes	long omask;
11371553Srgrimes
11381553Srgrimes	sep = (struct servtab *)malloc(sizeof (*sep));
11391553Srgrimes	if (sep == (struct servtab *)0) {
11401553Srgrimes		syslog(LOG_ERR, "Out of memory.");
114119617Sjulian		exit(EX_OSERR);
11421553Srgrimes	}
11431553Srgrimes	*sep = *cp;
11441553Srgrimes	sep->se_fd = -1;
114542122Sdes	omask = sigblock(SIGBLOCK);
11461553Srgrimes	sep->se_next = servtab;
11471553Srgrimes	servtab = sep;
114842122Sdes	sigsetmask(omask);
11491553Srgrimes	return (sep);
11501553Srgrimes}
11511553Srgrimes
115219618Sjulianvoid
115319618Sjulianenable(struct servtab *sep)
115419618Sjulian{
115519618Sjulian	if (debug)
115629602Scharnier		warnx(
115719618Sjulian		    "enabling %s, fd %d", sep->se_service, sep->se_fd);
115819618Sjulian#ifdef SANITY_CHECK
115919618Sjulian	if (sep->se_fd < 0) {
116019618Sjulian		syslog(LOG_ERR,
116119618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
116219618Sjulian		exit(EX_SOFTWARE);
116319618Sjulian	}
116419618Sjulian	if (ISMUX(sep)) {
116519618Sjulian		syslog(LOG_ERR,
116619618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
116719618Sjulian		exit(EX_SOFTWARE);
116819618Sjulian	}
116919618Sjulian	if (FD_ISSET(sep->se_fd, &allsock)) {
117019618Sjulian		syslog(LOG_ERR,
117119618Sjulian		    "%s: %s: not off", __FUNCTION__, sep->se_service);
117219618Sjulian		exit(EX_SOFTWARE);
117319618Sjulian	}
117419618Sjulian#endif
117519618Sjulian	FD_SET(sep->se_fd, &allsock);
117619618Sjulian	nsock++;
117719618Sjulian	if (sep->se_fd > maxsock)
117819618Sjulian		maxsock = sep->se_fd;
117919618Sjulian}
118019618Sjulian
118119618Sjulianvoid
118219618Sjuliandisable(struct servtab *sep)
118319618Sjulian{
118419618Sjulian	if (debug)
118529602Scharnier		warnx(
118619618Sjulian		    "disabling %s, fd %d", sep->se_service, sep->se_fd);
118719618Sjulian#ifdef SANITY_CHECK
118819618Sjulian	if (sep->se_fd < 0) {
118919618Sjulian		syslog(LOG_ERR,
119019618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
119119618Sjulian		exit(EX_SOFTWARE);
119219618Sjulian	}
119319618Sjulian	if (ISMUX(sep)) {
119419618Sjulian		syslog(LOG_ERR,
119519618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
119619618Sjulian		exit(EX_SOFTWARE);
119719618Sjulian	}
119819618Sjulian	if (!FD_ISSET(sep->se_fd, &allsock)) {
119919618Sjulian		syslog(LOG_ERR,
120019618Sjulian		    "%s: %s: not on", __FUNCTION__, sep->se_service);
120119618Sjulian		exit(EX_SOFTWARE);
120219618Sjulian	}
120319618Sjulian	if (nsock == 0) {
120419618Sjulian		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
120519618Sjulian		exit(EX_SOFTWARE);
120619618Sjulian	}
120719618Sjulian#endif
120819618Sjulian	FD_CLR(sep->se_fd, &allsock);
120919618Sjulian	nsock--;
121019618Sjulian	if (sep->se_fd == maxsock)
121119618Sjulian		maxsock--;
121219618Sjulian}
121319618Sjulian
12141553SrgrimesFILE	*fconfig = NULL;
12151553Srgrimesstruct	servtab serv;
12161553Srgrimeschar	line[LINE_MAX];
12171553Srgrimes
12181553Srgrimesint
12191553Srgrimessetconfig()
12201553Srgrimes{
12211553Srgrimes
12221553Srgrimes	if (fconfig != NULL) {
12231553Srgrimes		fseek(fconfig, 0L, SEEK_SET);
12241553Srgrimes		return (1);
12251553Srgrimes	}
12261553Srgrimes	fconfig = fopen(CONFIG, "r");
12271553Srgrimes	return (fconfig != NULL);
12281553Srgrimes}
12291553Srgrimes
12301553Srgrimesvoid
12311553Srgrimesendconfig()
12321553Srgrimes{
12331553Srgrimes	if (fconfig) {
12341553Srgrimes		(void) fclose(fconfig);
12351553Srgrimes		fconfig = NULL;
12361553Srgrimes	}
12371553Srgrimes}
12381553Srgrimes
12391553Srgrimesstruct servtab *
12401553Srgrimesgetconfigent()
12411553Srgrimes{
12421553Srgrimes	struct servtab *sep = &serv;
12431553Srgrimes	int argc;
124419618Sjulian	char *cp, *arg, *s;
12452657Scsgr	char *versp;
12461553Srgrimes	static char TCPMUX_TOKEN[] = "tcpmux/";
12471553Srgrimes#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
12481553Srgrimes
12491553Srgrimesmore:
12501553Srgrimes	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
12511553Srgrimes		;
12521553Srgrimes	if (cp == NULL)
12531553Srgrimes		return ((struct servtab *)0);
12541553Srgrimes	/*
12551553Srgrimes	 * clear the static buffer, since some fields (se_ctrladdr,
12561553Srgrimes	 * for example) don't get initialized here.
12571553Srgrimes	 */
12581553Srgrimes	memset((caddr_t)sep, 0, sizeof *sep);
12591553Srgrimes	arg = skip(&cp);
12601553Srgrimes	if (cp == NULL) {
12611553Srgrimes		/* got an empty line containing just blanks/tabs. */
12621553Srgrimes		goto more;
12631553Srgrimes	}
12641553Srgrimes	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
12651553Srgrimes		char *c = arg + MUX_LEN;
12661553Srgrimes		if (*c == '+') {
12671553Srgrimes			sep->se_type = MUXPLUS_TYPE;
12681553Srgrimes			c++;
12691553Srgrimes		} else
12701553Srgrimes			sep->se_type = MUX_TYPE;
12711553Srgrimes		sep->se_service = newstr(c);
12721553Srgrimes	} else {
12731553Srgrimes		sep->se_service = newstr(arg);
12741553Srgrimes		sep->se_type = NORM_TYPE;
12751553Srgrimes	}
12761553Srgrimes	arg = sskip(&cp);
12771553Srgrimes	if (strcmp(arg, "stream") == 0)
12781553Srgrimes		sep->se_socktype = SOCK_STREAM;
12791553Srgrimes	else if (strcmp(arg, "dgram") == 0)
12801553Srgrimes		sep->se_socktype = SOCK_DGRAM;
12811553Srgrimes	else if (strcmp(arg, "rdm") == 0)
12821553Srgrimes		sep->se_socktype = SOCK_RDM;
12831553Srgrimes	else if (strcmp(arg, "seqpacket") == 0)
12841553Srgrimes		sep->se_socktype = SOCK_SEQPACKET;
12851553Srgrimes	else if (strcmp(arg, "raw") == 0)
12861553Srgrimes		sep->se_socktype = SOCK_RAW;
12871553Srgrimes	else
12881553Srgrimes		sep->se_socktype = -1;
128936042Sguido
129036042Sguido	arg = sskip(&cp);
129136042Sguido	if (strcmp(arg, "tcp/ttcp") == 0) {
129236042Sguido		sep->se_type = TTCP_TYPE;
129336042Sguido		sep->se_proto = newstr("tcp");
129436042Sguido	} else {
129536042Sguido		sep->se_proto = newstr(arg);
129636042Sguido	}
12972657Scsgr        if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
129819237Sjoerg                memmove(sep->se_proto, sep->se_proto + 4,
129919237Sjoerg                    strlen(sep->se_proto) + 1 - 4);
13002657Scsgr                sep->se_rpc = 1;
13012657Scsgr                sep->se_rpc_prog = sep->se_rpc_lowvers =
13022657Scsgr			sep->se_rpc_lowvers = 0;
13032657Scsgr                sep->se_ctrladdr.sin_family = AF_INET;
13042657Scsgr                sep->se_ctrladdr.sin_port = 0;
130517482Sjulian                sep->se_ctrladdr.sin_addr = bind_address;
13062657Scsgr                if ((versp = rindex(sep->se_service, '/'))) {
13072657Scsgr                        *versp++ = '\0';
13082657Scsgr                        switch (sscanf(versp, "%d-%d",
13092657Scsgr                                       &sep->se_rpc_lowvers,
13102657Scsgr                                       &sep->se_rpc_highvers)) {
13112657Scsgr                        case 2:
13122657Scsgr                                break;
13132657Scsgr                        case 1:
13142657Scsgr                                sep->se_rpc_highvers =
13152657Scsgr                                        sep->se_rpc_lowvers;
13162657Scsgr                                break;
13172657Scsgr                        default:
13188857Srgrimes                                syslog(LOG_ERR,
13198857Srgrimes					"bad RPC version specifier; %s\n",
13202657Scsgr					sep->se_service);
13212657Scsgr                                freeconfig(sep);
13222657Scsgr                                goto more;
13232657Scsgr                        }
13242657Scsgr                }
13252657Scsgr                else {
13262657Scsgr                        sep->se_rpc_lowvers =
13272657Scsgr                                sep->se_rpc_highvers = 1;
13282657Scsgr                }
13292657Scsgr        }
13301553Srgrimes	arg = sskip(&cp);
133119618Sjulian	if (!strncmp(arg, "wait", 4))
133219618Sjulian		sep->se_accept = 0;
133319618Sjulian	else if (!strncmp(arg, "nowait", 6))
133419618Sjulian		sep->se_accept = 1;
133519618Sjulian	else {
133619618Sjulian		syslog(LOG_ERR,
133719618Sjulian			"%s: bad wait/nowait for service %s",
133819618Sjulian			CONFIG, sep->se_service);
133919618Sjulian		goto more;
134019618Sjulian	}
134133794Spst	sep->se_maxchild = maxchild;
134233794Spst	sep->se_maxcpm = maxcpm;
134319618Sjulian	if ((s = strchr(arg, '/')) != NULL) {
134419618Sjulian		char *eptr;
134519618Sjulian		u_long val;
134619618Sjulian
134719618Sjulian		val = strtoul(s + 1, &eptr, 10);
134830847Sdima		if (eptr == s + 1 || val > MAX_MAXCHLD) {
134919618Sjulian			syslog(LOG_ERR,
135019618Sjulian				"%s: bad max-child for service %s",
135119618Sjulian				CONFIG, sep->se_service);
135219618Sjulian			goto more;
135319618Sjulian		}
135419618Sjulian		sep->se_maxchild = val;
135530847Sdima		if (*eptr == '/')
135630847Sdima			sep->se_maxcpm = strtol(eptr + 1, &eptr, 10);
135730847Sdima		/*
135830847Sdima		 * explicitly do not check for \0 for future expansion /
135930847Sdima		 * backwards compatibility
136030847Sdima		 */
136119618Sjulian	}
13621553Srgrimes	if (ISMUX(sep)) {
13631553Srgrimes		/*
136419618Sjulian		 * Silently enforce "nowait" mode for TCPMUX services
136519618Sjulian		 * since they don't have an assigned port to listen on.
13661553Srgrimes		 */
136719618Sjulian		sep->se_accept = 1;
13681553Srgrimes		if (strcmp(sep->se_proto, "tcp")) {
13698857Srgrimes			syslog(LOG_ERR,
13701553Srgrimes				"%s: bad protocol for tcpmux service %s",
13711553Srgrimes				CONFIG, sep->se_service);
13721553Srgrimes			goto more;
13731553Srgrimes		}
13741553Srgrimes		if (sep->se_socktype != SOCK_STREAM) {
13758857Srgrimes			syslog(LOG_ERR,
13761553Srgrimes				"%s: bad socket type for tcpmux service %s",
13771553Srgrimes				CONFIG, sep->se_service);
13781553Srgrimes			goto more;
13791553Srgrimes		}
13801553Srgrimes	}
13811553Srgrimes	sep->se_user = newstr(sskip(&cp));
138230792Sache#ifdef LOGIN_CAP
138330792Sache	if ((s = strrchr(sep->se_user, '/')) != NULL) {
138430792Sache		*s = '\0';
138530792Sache		sep->se_class = newstr(s + 1);
138630792Sache	} else
138730792Sache		sep->se_class = newstr(RESOURCE_RC);
138830792Sache#endif
138930807Sache	if ((s = strrchr(sep->se_user, ':')) != NULL) {
139030807Sache		*s = '\0';
139130807Sache		sep->se_group = newstr(s + 1);
139230807Sache	} else
139330807Sache		sep->se_group = NULL;
13941553Srgrimes	sep->se_server = newstr(sskip(&cp));
139545588Smarkm	if ((sep->se_server_name = rindex(sep->se_server, '/')))
139645588Smarkm		sep->se_server_name++;
13971553Srgrimes	if (strcmp(sep->se_server, "internal") == 0) {
13981553Srgrimes		struct biltin *bi;
13991553Srgrimes
14001553Srgrimes		for (bi = biltins; bi->bi_service; bi++)
14011553Srgrimes			if (bi->bi_socktype == sep->se_socktype &&
14021553Srgrimes			    strcmp(bi->bi_service, sep->se_service) == 0)
14031553Srgrimes				break;
14041553Srgrimes		if (bi->bi_service == 0) {
14051553Srgrimes			syslog(LOG_ERR, "internal service %s unknown",
14061553Srgrimes				sep->se_service);
14071553Srgrimes			goto more;
14081553Srgrimes		}
140919618Sjulian		sep->se_accept = 1;	/* force accept mode for built-ins */
14101553Srgrimes		sep->se_bi = bi;
14111553Srgrimes	} else
14121553Srgrimes		sep->se_bi = NULL;
141345588Smarkm	if (sep->se_maxchild < 0) {	/* apply default max-children */
141419618Sjulian		if (sep->se_bi)
141519618Sjulian			sep->se_maxchild = sep->se_bi->bi_maxchild;
141619618Sjulian		else
141719618Sjulian			sep->se_maxchild = sep->se_accept ? 0 : 1;
141845588Smarkm	}
141919618Sjulian	if (sep->se_maxchild) {
142019618Sjulian		sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
142119618Sjulian		if (sep->se_pids == NULL) {
142219618Sjulian			syslog(LOG_ERR, "Out of memory.");
142319618Sjulian			exit(EX_OSERR);
142419618Sjulian		}
142519618Sjulian	}
14261553Srgrimes	argc = 0;
14271553Srgrimes	for (arg = skip(&cp); cp; arg = skip(&cp))
142819618Sjulian		if (argc < MAXARGV) {
14291553Srgrimes			sep->se_argv[argc++] = newstr(arg);
143019618Sjulian		} else {
143119618Sjulian			syslog(LOG_ERR,
143219618Sjulian				"%s: too many arguments for service %s",
143319618Sjulian				CONFIG, sep->se_service);
143419618Sjulian			goto more;
143519618Sjulian		}
14361553Srgrimes	while (argc <= MAXARGV)
14371553Srgrimes		sep->se_argv[argc++] = NULL;
14381553Srgrimes	return (sep);
14391553Srgrimes}
14401553Srgrimes
14411553Srgrimesvoid
14421553Srgrimesfreeconfig(cp)
14431553Srgrimes	struct servtab *cp;
14441553Srgrimes{
14451553Srgrimes	int i;
14461553Srgrimes
14471553Srgrimes	if (cp->se_service)
14481553Srgrimes		free(cp->se_service);
14491553Srgrimes	if (cp->se_proto)
14501553Srgrimes		free(cp->se_proto);
14511553Srgrimes	if (cp->se_user)
14521553Srgrimes		free(cp->se_user);
145330807Sache	if (cp->se_group)
145430807Sache		free(cp->se_group);
145530792Sache#ifdef LOGIN_CAP
145630792Sache	if (cp->se_class)
145730792Sache		free(cp->se_class);
145830792Sache#endif
14591553Srgrimes	if (cp->se_server)
14601553Srgrimes		free(cp->se_server);
146119618Sjulian	if (cp->se_pids)
146219618Sjulian		free(cp->se_pids);
14631553Srgrimes	for (i = 0; i < MAXARGV; i++)
14641553Srgrimes		if (cp->se_argv[i])
14651553Srgrimes			free(cp->se_argv[i]);
14661553Srgrimes}
14671553Srgrimes
14681553Srgrimes
14691553Srgrimes/*
14701553Srgrimes * Safe skip - if skip returns null, log a syntax error in the
14711553Srgrimes * configuration file and exit.
14721553Srgrimes */
14731553Srgrimeschar *
14741553Srgrimessskip(cpp)
14751553Srgrimes	char **cpp;
14761553Srgrimes{
14771553Srgrimes	char *cp;
14781553Srgrimes
14791553Srgrimes	cp = skip(cpp);
14801553Srgrimes	if (cp == NULL) {
14811553Srgrimes		syslog(LOG_ERR, "%s: syntax error", CONFIG);
148219617Sjulian		exit(EX_DATAERR);
14831553Srgrimes	}
14841553Srgrimes	return (cp);
14851553Srgrimes}
14861553Srgrimes
14871553Srgrimeschar *
14881553Srgrimesskip(cpp)
14891553Srgrimes	char **cpp;
14901553Srgrimes{
14911553Srgrimes	char *cp = *cpp;
14921553Srgrimes	char *start;
149311933Sadam	char quote = '\0';
14941553Srgrimes
14951553Srgrimesagain:
14961553Srgrimes	while (*cp == ' ' || *cp == '\t')
14971553Srgrimes		cp++;
14981553Srgrimes	if (*cp == '\0') {
14991553Srgrimes		int c;
15001553Srgrimes
15011553Srgrimes		c = getc(fconfig);
15021553Srgrimes		(void) ungetc(c, fconfig);
15031553Srgrimes		if (c == ' ' || c == '\t')
150419617Sjulian			if ((cp = nextline(fconfig)))
15051553Srgrimes				goto again;
15061553Srgrimes		*cpp = (char *)0;
15071553Srgrimes		return ((char *)0);
15081553Srgrimes	}
150911933Sadam	if (*cp == '"' || *cp == '\'')
151011933Sadam		quote = *cp++;
15111553Srgrimes	start = cp;
151211933Sadam	if (quote)
151311933Sadam		while (*cp && *cp != quote)
151411933Sadam			cp++;
151511933Sadam	else
151611933Sadam		while (*cp && *cp != ' ' && *cp != '\t')
151711933Sadam			cp++;
15181553Srgrimes	if (*cp != '\0')
15191553Srgrimes		*cp++ = '\0';
15201553Srgrimes	*cpp = cp;
15211553Srgrimes	return (start);
15221553Srgrimes}
15231553Srgrimes
15241553Srgrimeschar *
15251553Srgrimesnextline(fd)
15261553Srgrimes	FILE *fd;
15271553Srgrimes{
15281553Srgrimes	char *cp;
15291553Srgrimes
15301553Srgrimes	if (fgets(line, sizeof (line), fd) == NULL)
15311553Srgrimes		return ((char *)0);
15321553Srgrimes	cp = strchr(line, '\n');
15331553Srgrimes	if (cp)
15341553Srgrimes		*cp = '\0';
15351553Srgrimes	return (line);
15361553Srgrimes}
15371553Srgrimes
15381553Srgrimeschar *
15391553Srgrimesnewstr(cp)
15401553Srgrimes	char *cp;
15411553Srgrimes{
154219617Sjulian	if ((cp = strdup(cp ? cp : "")))
15431553Srgrimes		return (cp);
15441553Srgrimes	syslog(LOG_ERR, "strdup: %m");
154519617Sjulian	exit(EX_OSERR);
15461553Srgrimes}
15471553Srgrimes
154813142Speter#ifdef OLD_SETPROCTITLE
15491553Srgrimesvoid
155013142Speterinetd_setproctitle(a, s)
15511553Srgrimes	char *a;
15521553Srgrimes	int s;
15531553Srgrimes{
15541553Srgrimes	int size;
15551553Srgrimes	char *cp;
15561553Srgrimes	struct sockaddr_in sin;
15571553Srgrimes	char buf[80];
15581553Srgrimes
15591553Srgrimes	cp = Argv[0];
15601553Srgrimes	size = sizeof(sin);
15611553Srgrimes	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
15628857Srgrimes		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
15631553Srgrimes	else
15648857Srgrimes		(void) sprintf(buf, "-%s", a);
15651553Srgrimes	strncpy(cp, buf, LastArg - cp);
15661553Srgrimes	cp += strlen(cp);
15671553Srgrimes	while (cp < LastArg)
15681553Srgrimes		*cp++ = ' ';
15691553Srgrimes}
157013142Speter#else
157113142Spetervoid
157213142Speterinetd_setproctitle(a, s)
157313142Speter	char *a;
157413142Speter	int s;
157513142Speter{
157613142Speter	int size;
157713142Speter	struct sockaddr_in sin;
157813142Speter	char buf[80];
15791553Srgrimes
158013142Speter	size = sizeof(sin);
158113142Speter	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
158213142Speter		(void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr));
158313142Speter	else
158413142Speter		(void) sprintf(buf, "%s", a);
158513142Speter	setproctitle("%s", buf);
158613142Speter}
158713142Speter#endif
158813142Speter
158913142Speter
15901553Srgrimes/*
15911553Srgrimes * Internet services provided internally by inetd:
15921553Srgrimes */
15931553Srgrimes#define	BUFSIZE	8192
15941553Srgrimes
159540910Sphk#define IDENT_RESPONSE ":ERROR:HIDDEN-USER\r\n"
159640910Sphk
15971553Srgrimes/* ARGSUSED */
15981553Srgrimesvoid
159940910Sphkident_stream(s, sep)		/* Ident service */
160040910Sphk	int s;
160140910Sphk	struct servtab *sep;
160240910Sphk{
160340910Sphk	char buffer[BUFSIZE];
160440910Sphk	int i, j;
160540910Sphk
160640910Sphk	inetd_setproctitle(sep->se_service, s);
160740910Sphk	j = 0;
160840910Sphk	while ((i = read(s, buffer + j, sizeof(buffer) - j)) > 0) {
160940910Sphk		j += i;
161040910Sphk		buffer[j] = '\0';
161140910Sphk		if (strchr(buffer, '\n'))
161240910Sphk			break;
161340910Sphk		if (strchr(buffer, '\r'))
161440910Sphk			break;
161540910Sphk	}
161640910Sphk	while (j > 0 && (buffer[j-1] == '\n' || buffer[j-1] == '\r'))
161740910Sphk		j--;
161840910Sphk	write(s, buffer, j);
161940910Sphk	write(s, IDENT_RESPONSE, strlen(IDENT_RESPONSE));
162040910Sphk	exit(0);
162140910Sphk}
162240910Sphk/* ARGSUSED */
162340910Sphkvoid
16241553Srgrimesecho_stream(s, sep)		/* Echo service -- echo data back */
16251553Srgrimes	int s;
16261553Srgrimes	struct servtab *sep;
16271553Srgrimes{
16281553Srgrimes	char buffer[BUFSIZE];
16291553Srgrimes	int i;
16301553Srgrimes
163113142Speter	inetd_setproctitle(sep->se_service, s);
16321553Srgrimes	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
16331553Srgrimes	    write(s, buffer, i) > 0)
16341553Srgrimes		;
16351553Srgrimes	exit(0);
16361553Srgrimes}
16371553Srgrimes
16385182Swollmanint check_loop(sin, sep)
16395182Swollman	struct sockaddr_in *sin;
16405182Swollman	struct servtab *sep;
16415182Swollman{
16425182Swollman	struct servtab *se2;
16435182Swollman
16445182Swollman	for (se2 = servtab; se2; se2 = se2->se_next) {
16455182Swollman		if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
16465182Swollman			continue;
16475182Swollman
16485182Swollman		if (sin->sin_port == se2->se_ctrladdr.sin_port) {
16495182Swollman			syslog(LOG_WARNING,
16505182Swollman			       "%s/%s:%s/%s loop request REFUSED from %s",
16518857Srgrimes			       sep->se_service, sep->se_proto,
16525182Swollman			       se2->se_service, se2->se_proto,
16535182Swollman			       inet_ntoa(sin->sin_addr));
16545182Swollman			return 1;
16555182Swollman		}
16565182Swollman	}
16575182Swollman	return 0;
16585182Swollman}
16595182Swollman
16601553Srgrimes/* ARGSUSED */
16611553Srgrimesvoid
16621553Srgrimesecho_dg(s, sep)			/* Echo service -- echo data back */
16631553Srgrimes	int s;
16641553Srgrimes	struct servtab *sep;
16651553Srgrimes{
16661553Srgrimes	char buffer[BUFSIZE];
16671553Srgrimes	int i, size;
16685182Swollman	struct sockaddr_in sin;
16691553Srgrimes
16705182Swollman	size = sizeof(sin);
16718857Srgrimes	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
16725182Swollman			  (struct sockaddr *)&sin, &size)) < 0)
16731553Srgrimes		return;
16745182Swollman
16755182Swollman	if (check_loop(&sin, sep))
16765182Swollman		return;
16775182Swollman
16785182Swollman	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin,
16795182Swollman		      sizeof(sin));
16801553Srgrimes}
16811553Srgrimes
16821553Srgrimes/* ARGSUSED */
16831553Srgrimesvoid
16841553Srgrimesdiscard_stream(s, sep)		/* Discard service -- ignore data */
16851553Srgrimes	int s;
16861553Srgrimes	struct servtab *sep;
16871553Srgrimes{
16881553Srgrimes	int ret;
16891553Srgrimes	char buffer[BUFSIZE];
16901553Srgrimes
169113142Speter	inetd_setproctitle(sep->se_service, s);
16921553Srgrimes	while (1) {
16931553Srgrimes		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
16941553Srgrimes			;
16951553Srgrimes		if (ret == 0 || errno != EINTR)
16961553Srgrimes			break;
16971553Srgrimes	}
16981553Srgrimes	exit(0);
16991553Srgrimes}
17001553Srgrimes
17011553Srgrimes/* ARGSUSED */
17021553Srgrimesvoid
17031553Srgrimesdiscard_dg(s, sep)		/* Discard service -- ignore data */
17041553Srgrimes	int s;
17051553Srgrimes	struct servtab *sep;
17061553Srgrimes{
17071553Srgrimes	char buffer[BUFSIZE];
17081553Srgrimes
17091553Srgrimes	(void) read(s, buffer, sizeof(buffer));
17101553Srgrimes}
17111553Srgrimes
17121553Srgrimes#include <ctype.h>
17131553Srgrimes#define LINESIZ 72
17141553Srgrimeschar ring[128];
17151553Srgrimeschar *endring;
17161553Srgrimes
17171553Srgrimesvoid
17181553Srgrimesinitring()
17191553Srgrimes{
17201553Srgrimes	int i;
17211553Srgrimes
17221553Srgrimes	endring = ring;
17231553Srgrimes
17241553Srgrimes	for (i = 0; i <= 128; ++i)
17251553Srgrimes		if (isprint(i))
17261553Srgrimes			*endring++ = i;
17271553Srgrimes}
17281553Srgrimes
17291553Srgrimes/* ARGSUSED */
17301553Srgrimesvoid
17311553Srgrimeschargen_stream(s, sep)		/* Character generator */
17321553Srgrimes	int s;
17331553Srgrimes	struct servtab *sep;
17341553Srgrimes{
17351553Srgrimes	int len;
17361553Srgrimes	char *rs, text[LINESIZ+2];
17371553Srgrimes
173813142Speter	inetd_setproctitle(sep->se_service, s);
17391553Srgrimes
17401553Srgrimes	if (!endring) {
17411553Srgrimes		initring();
17421553Srgrimes		rs = ring;
17431553Srgrimes	}
17441553Srgrimes
17451553Srgrimes	text[LINESIZ] = '\r';
17461553Srgrimes	text[LINESIZ + 1] = '\n';
17471553Srgrimes	for (rs = ring;;) {
17481553Srgrimes		if ((len = endring - rs) >= LINESIZ)
17491553Srgrimes			memmove(text, rs, LINESIZ);
17501553Srgrimes		else {
17511553Srgrimes			memmove(text, rs, len);
17521553Srgrimes			memmove(text + len, ring, LINESIZ - len);
17531553Srgrimes		}
17541553Srgrimes		if (++rs == endring)
17551553Srgrimes			rs = ring;
17561553Srgrimes		if (write(s, text, sizeof(text)) != sizeof(text))
17571553Srgrimes			break;
17581553Srgrimes	}
17591553Srgrimes	exit(0);
17601553Srgrimes}
17611553Srgrimes
17621553Srgrimes/* ARGSUSED */
17631553Srgrimesvoid
17641553Srgrimeschargen_dg(s, sep)		/* Character generator */
17651553Srgrimes	int s;
17661553Srgrimes	struct servtab *sep;
17671553Srgrimes{
17685182Swollman	struct sockaddr_in sin;
17691553Srgrimes	static char *rs;
17701553Srgrimes	int len, size;
17711553Srgrimes	char text[LINESIZ+2];
17721553Srgrimes
17731553Srgrimes	if (endring == 0) {
17741553Srgrimes		initring();
17751553Srgrimes		rs = ring;
17761553Srgrimes	}
17771553Srgrimes
17785182Swollman	size = sizeof(sin);
17798857Srgrimes	if (recvfrom(s, text, sizeof(text), 0,
17805182Swollman		     (struct sockaddr *)&sin, &size) < 0)
17811553Srgrimes		return;
17821553Srgrimes
17835182Swollman	if (check_loop(&sin, sep))
17845182Swollman		return;
17855182Swollman
17861553Srgrimes	if ((len = endring - rs) >= LINESIZ)
17871553Srgrimes		memmove(text, rs, LINESIZ);
17881553Srgrimes	else {
17891553Srgrimes		memmove(text, rs, len);
17901553Srgrimes		memmove(text + len, ring, LINESIZ - len);
17911553Srgrimes	}
17921553Srgrimes	if (++rs == endring)
17931553Srgrimes		rs = ring;
17941553Srgrimes	text[LINESIZ] = '\r';
17951553Srgrimes	text[LINESIZ + 1] = '\n';
17968857Srgrimes	(void) sendto(s, text, sizeof(text), 0,
17975182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
17981553Srgrimes}
17991553Srgrimes
18001553Srgrimes/*
18011553Srgrimes * Return a machine readable date and time, in the form of the
18021553Srgrimes * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
18031553Srgrimes * returns the number of seconds since midnight, Jan 1, 1970,
18041553Srgrimes * we must add 2208988800 seconds to this figure to make up for
18051553Srgrimes * some seventy years Bell Labs was asleep.
18061553Srgrimes */
18071553Srgrimes
180842311Sdannyunsigned long
18091553Srgrimesmachtime()
18101553Srgrimes{
18111553Srgrimes	struct timeval tv;
18121553Srgrimes
181337856Sache	if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
18141553Srgrimes		if (debug)
181529602Scharnier			warnx("unable to get time of day");
18161553Srgrimes		return (0L);
18171553Srgrimes	}
18181553Srgrimes#define	OFFSET ((u_long)25567 * 24*60*60)
18191553Srgrimes	return (htonl((long)(tv.tv_sec + OFFSET)));
18201553Srgrimes#undef OFFSET
18211553Srgrimes}
18221553Srgrimes
18231553Srgrimes/* ARGSUSED */
18241553Srgrimesvoid
18251553Srgrimesmachtime_stream(s, sep)
18261553Srgrimes	int s;
18271553Srgrimes	struct servtab *sep;
18281553Srgrimes{
182942311Sdanny	unsigned long result;
18301553Srgrimes
18311553Srgrimes	result = machtime();
18321553Srgrimes	(void) write(s, (char *) &result, sizeof(result));
18331553Srgrimes}
18341553Srgrimes
18351553Srgrimes/* ARGSUSED */
18361553Srgrimesvoid
18371553Srgrimesmachtime_dg(s, sep)
18381553Srgrimes	int s;
18391553Srgrimes	struct servtab *sep;
18401553Srgrimes{
184142311Sdanny	unsigned long result;
18425182Swollman	struct sockaddr_in sin;
18431553Srgrimes	int size;
18441553Srgrimes
18455182Swollman	size = sizeof(sin);
18468857Srgrimes	if (recvfrom(s, (char *)&result, sizeof(result), 0,
18475182Swollman		     (struct sockaddr *)&sin, &size) < 0)
18481553Srgrimes		return;
18495182Swollman
18505182Swollman	if (check_loop(&sin, sep))
18515182Swollman		return;
18525182Swollman
18531553Srgrimes	result = machtime();
18548857Srgrimes	(void) sendto(s, (char *) &result, sizeof(result), 0,
18555182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
18561553Srgrimes}
18571553Srgrimes
18581553Srgrimes/* ARGSUSED */
18591553Srgrimesvoid
18601553Srgrimesdaytime_stream(s, sep)		/* Return human-readable time of day */
18611553Srgrimes	int s;
18621553Srgrimes	struct servtab *sep;
18631553Srgrimes{
18641553Srgrimes	char buffer[256];
18651553Srgrimes	time_t clock;
18661553Srgrimes
18671553Srgrimes	clock = time((time_t *) 0);
18681553Srgrimes
18691553Srgrimes	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
18701553Srgrimes	(void) write(s, buffer, strlen(buffer));
18711553Srgrimes}
18721553Srgrimes
18731553Srgrimes/* ARGSUSED */
18741553Srgrimesvoid
18751553Srgrimesdaytime_dg(s, sep)		/* Return human-readable time of day */
18761553Srgrimes	int s;
18771553Srgrimes	struct servtab *sep;
18781553Srgrimes{
18791553Srgrimes	char buffer[256];
18801553Srgrimes	time_t clock;
18815182Swollman	struct sockaddr_in sin;
18821553Srgrimes	int size;
18831553Srgrimes
18841553Srgrimes	clock = time((time_t *) 0);
18851553Srgrimes
18865182Swollman	size = sizeof(sin);
18878857Srgrimes	if (recvfrom(s, buffer, sizeof(buffer), 0,
18885182Swollman		     (struct sockaddr *)&sin, &size) < 0)
18891553Srgrimes		return;
18905182Swollman
18915182Swollman	if (check_loop(&sin, sep))
18925182Swollman		return;
18935182Swollman
18941553Srgrimes	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
18958857Srgrimes	(void) sendto(s, buffer, strlen(buffer), 0,
18965182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
18971553Srgrimes}
18981553Srgrimes
18991553Srgrimes/*
19001553Srgrimes * print_service:
19011553Srgrimes *	Dump relevant information to stderr
19021553Srgrimes */
19031553Srgrimesvoid
19041553Srgrimesprint_service(action, sep)
19051553Srgrimes	char *action;
19061553Srgrimes	struct servtab *sep;
19071553Srgrimes{
190819617Sjulian	fprintf(stderr,
190930792Sache#ifdef LOGIN_CAP
191038380Sjb	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%p server=%s\n",
191130792Sache#else
191238380Sjb	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%p server=%s\n",
191330792Sache#endif
191419617Sjulian	    action, sep->se_service, sep->se_proto,
191530807Sache	    sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group,
191630792Sache#ifdef LOGIN_CAP
191730792Sache	    sep->se_class,
191830792Sache#endif
191938417Sjb	    (void *) sep->se_bi, sep->se_server);
19201553Srgrimes}
19211553Srgrimes
19221553Srgrimes/*
19231553Srgrimes *  Based on TCPMUX.C by Mark K. Lottor November 1988
19241553Srgrimes *  sri-nic::ps:<mkl>tcpmux.c
19251553Srgrimes */
19261553Srgrimes
19271553Srgrimes
19281553Srgrimesstatic int		/* # of characters upto \r,\n or \0 */
19291553Srgrimesgetline(fd, buf, len)
19301553Srgrimes	int fd;
19311553Srgrimes	char *buf;
19321553Srgrimes	int len;
19331553Srgrimes{
19341553Srgrimes	int count = 0, n;
193535848Sguido	struct sigaction sa;
19361553Srgrimes
193735948Sbde	sa.sa_flags = 0;
193835948Sbde	sigemptyset(&sa.sa_mask);
193935848Sguido	sa.sa_handler = SIG_DFL;
194035848Sguido	sigaction(SIGALRM, &sa, (struct sigaction *)0);
19411553Srgrimes	do {
194235829Sguido		alarm(10);
19431553Srgrimes		n = read(fd, buf, len-count);
194435829Sguido		alarm(0);
19451553Srgrimes		if (n == 0)
194642122Sdes			return (count);
194742122Sdes		if (n < 0)
194842122Sdes			return (-1);
19491553Srgrimes		while (--n >= 0) {
195042122Sdes			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
195142122Sdes				return (count);
19521553Srgrimes			count++;
19531553Srgrimes			buf++;
19541553Srgrimes		}
195542122Sdes	} while (count < len);
19561553Srgrimes	return (count);
19571553Srgrimes}
19581553Srgrimes
19591553Srgrimes#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
19601553Srgrimes
19611553Srgrimes#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
19621553Srgrimes
19631553Srgrimesstruct servtab *
19641553Srgrimestcpmux(s)
19651553Srgrimes	int s;
19661553Srgrimes{
19671553Srgrimes	struct servtab *sep;
19681553Srgrimes	char service[MAX_SERV_LEN+1];
19691553Srgrimes	int len;
19701553Srgrimes
19711553Srgrimes	/* Get requested service name */
19721553Srgrimes	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
19731553Srgrimes		strwrite(s, "-Error reading service name\r\n");
19741553Srgrimes		return (NULL);
19751553Srgrimes	}
19761553Srgrimes	service[len] = '\0';
19771553Srgrimes
19781553Srgrimes	if (debug)
197929602Scharnier		warnx("tcpmux: someone wants %s", service);
19801553Srgrimes
19811553Srgrimes	/*
19821553Srgrimes	 * Help is a required command, and lists available services,
19831553Srgrimes	 * one per line.
19841553Srgrimes	 */
19851553Srgrimes	if (!strcasecmp(service, "help")) {
19861553Srgrimes		for (sep = servtab; sep; sep = sep->se_next) {
19871553Srgrimes			if (!ISMUX(sep))
19881553Srgrimes				continue;
19891553Srgrimes			(void)write(s,sep->se_service,strlen(sep->se_service));
19901553Srgrimes			strwrite(s, "\r\n");
19911553Srgrimes		}
19921553Srgrimes		return (NULL);
19931553Srgrimes	}
19941553Srgrimes
19951553Srgrimes	/* Try matching a service in inetd.conf with the request */
19961553Srgrimes	for (sep = servtab; sep; sep = sep->se_next) {
19971553Srgrimes		if (!ISMUX(sep))
19981553Srgrimes			continue;
19991553Srgrimes		if (!strcasecmp(service, sep->se_service)) {
20001553Srgrimes			if (ISMUXPLUS(sep)) {
20011553Srgrimes				strwrite(s, "+Go\r\n");
20021553Srgrimes			}
20031553Srgrimes			return (sep);
20041553Srgrimes		}
20051553Srgrimes	}
20061553Srgrimes	strwrite(s, "-Service not available\r\n");
20071553Srgrimes	return (NULL);
20081553Srgrimes}
200930847Sdima
201030847Sdima#define CPMHSIZE	256
201130847Sdima#define CPMHMASK	(CPMHSIZE-1)
201230847Sdima#define CHTGRAN		10
201330847Sdima#define CHTSIZE		6
201430847Sdima
201530847Sdimatypedef struct CTime {
201630847Sdima	unsigned long 	ct_Ticks;
201730847Sdima	int		ct_Count;
201830847Sdima} CTime;
201930847Sdima
202030847Sdimatypedef struct CHash {
202130847Sdima	struct in_addr	ch_Addr;
202230847Sdima	time_t		ch_LTime;
202330847Sdima	char		*ch_Service;
202430847Sdima	CTime		ch_Times[CHTSIZE];
202530847Sdima} CHash;
202630847Sdima
202730847SdimaCHash	CHashAry[CPMHSIZE];
202830847Sdima
202930847Sdimaint
203030847Sdimacpmip(sep, ctrl)
203130847Sdima	struct servtab *sep;
203230847Sdima	int ctrl;
203330847Sdima{
203430847Sdima	struct sockaddr_in rsin;
203530847Sdima	int rsinLen = sizeof(rsin);
203630847Sdima	int r = 0;
203730847Sdima
203830847Sdima	/*
203930847Sdima	 * If getpeername() fails, just let it through (if logging is
204030847Sdima	 * enabled the condition is caught elsewhere)
204130847Sdima	 */
204230847Sdima
204330847Sdima	if (sep->se_maxcpm > 0 &&
204430847Sdima	    getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 0 ) {
204530847Sdima		time_t t = time(NULL);
204630847Sdima		int hv = 0xABC3D20F;
204730847Sdima		int i;
204830847Sdima		int cnt = 0;
204930847Sdima		CHash *chBest = NULL;
205030847Sdima		unsigned int ticks = t / CHTGRAN;
205130847Sdima
205230847Sdima		{
205330847Sdima			char *p;
205430847Sdima			int i;
205530847Sdima
205630847Sdima			for (i = 0, p = (char *)&rsin.sin_addr;
205730847Sdima			    i < sizeof(rsin.sin_addr);
205830847Sdima			    ++i, ++p) {
205930847Sdima				hv = (hv << 5) ^ (hv >> 23) ^ *p;
206030847Sdima			}
206130847Sdima			hv = (hv ^ (hv >> 16));
206230847Sdima		}
206330847Sdima		for (i = 0; i < 5; ++i) {
206430847Sdima			CHash *ch = &CHashAry[(hv + i) & CPMHMASK];
206530847Sdima
206630847Sdima			if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr &&
206730847Sdima			    ch->ch_Service && strcmp(sep->se_service,
206830847Sdima			    ch->ch_Service) == 0) {
206930847Sdima				chBest = ch;
207030847Sdima				break;
207130847Sdima			}
207230847Sdima			if (chBest == NULL || ch->ch_LTime == 0 ||
207330847Sdima			    ch->ch_LTime < chBest->ch_LTime) {
207430847Sdima				chBest = ch;
207530847Sdima			}
207630847Sdima		}
207730847Sdima		if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr ||
207830847Sdima		    chBest->ch_Service == NULL ||
207930847Sdima		    strcmp(sep->se_service, chBest->ch_Service) != 0) {
208030847Sdima			chBest->ch_Addr = rsin.sin_addr;
208130847Sdima			if (chBest->ch_Service)
208230847Sdima				free(chBest->ch_Service);
208330847Sdima			chBest->ch_Service = strdup(sep->se_service);
208430847Sdima			bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
208530847Sdima		}
208630847Sdima		chBest->ch_LTime = t;
208730847Sdima		{
208830847Sdima			CTime *ct = &chBest->ch_Times[ticks % CHTSIZE];
208930847Sdima			if (ct->ct_Ticks != ticks) {
209030847Sdima				ct->ct_Ticks = ticks;
209130847Sdima				ct->ct_Count = 0;
209230847Sdima			}
209330847Sdima			++ct->ct_Count;
209430847Sdima		}
209530847Sdima		for (i = 0; i < CHTSIZE; ++i) {
209630847Sdima			CTime *ct = &chBest->ch_Times[i];
209730847Sdima			if (ct->ct_Ticks <= ticks &&
209830847Sdima			    ct->ct_Ticks >= ticks - CHTSIZE) {
209930847Sdima				cnt += ct->ct_Count;
210030847Sdima			}
210130847Sdima		}
210230847Sdima		if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) {
210330847Sdima			r = -1;
210430847Sdima			syslog(LOG_ERR,
210533794Spst			    "%s from %s exceeded counts/min (limit %d/min)",
210633794Spst			    sep->se_service, inet_ntoa(rsin.sin_addr),
210733794Spst			    sep->se_maxcpm);
210830847Sdima		}
210930847Sdima	}
211030847Sdima	return(r);
211130847Sdima}
2112