inetd.c revision 42122
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[] =
4542122Sdes	"$Id: inetd.c,v 1.41 1998/11/04 19:39:46 phk 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
13521640Speter#ifdef LOGIN_CAP
13621640Speter#include <login_cap.h>
13730792Sache
13830792Sache/* see init.c */
13930792Sache#define RESOURCE_RC "daemon"
14030792Sache
14121640Speter#endif
14221640Speter
1431553Srgrimes#include "pathnames.h"
1441553Srgrimes
14533794Spst#ifndef	MAXCHILD
14633794Spst#define	MAXCHILD	-1		/* maximum number of this service
14733794Spst					   < 0 = no limit */
14833794Spst#endif
14933794Spst
15033794Spst#ifndef	MAXCPM
15133794Spst#define	MAXCPM		-1		/* rate limit invocations from a
15233794Spst					   single remote address,
15333794Spst					   < 0 = no limit */
15433794Spst#endif
15533794Spst
1562659Scsgr#define	TOOMANY		256		/* don't start more than TOOMANY */
1571553Srgrimes#define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
1581553Srgrimes#define	RETRYTIME	(60*10)		/* retry after bind or server fail */
15919618Sjulian#define MAX_MAXCHLD	32767		/* max allowable max children */
1601553Srgrimes
1611553Srgrimes#define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
1621553Srgrimes
1631553Srgrimesint	debug = 0;
1642659Scsgrint	log = 0;
1651553Srgrimesint	nsock, maxsock;
1661553Srgrimesfd_set	allsock;
1671553Srgrimesint	options;
1681553Srgrimesint	timingout;
1691553Srgrimesint	toomany = TOOMANY;
17033794Spstint	maxchild = MAXCPM;
17133794Spstint	maxcpm = MAXCHILD;
1721553Srgrimesstruct	servent *sp;
1732657Scsgrstruct	rpcent *rpc;
17417482Sjulianstruct	in_addr bind_address;
17542122Sdesint	signalpipe[2];
1761553Srgrimes
1771553Srgrimesstruct	servtab {
1781553Srgrimes	char	*se_service;		/* name of service */
1791553Srgrimes	int	se_socktype;		/* type of socket to use */
1801553Srgrimes	char	*se_proto;		/* protocol used */
18130847Sdima	int	se_maxchild;		/* max number of children */
18230847Sdima	int	se_maxcpm;		/* max connects per IP per minute */
18330847Sdima	int	se_numchild;		/* current number of children */
18419618Sjulian	pid_t	*se_pids;		/* array of child pids */
1851553Srgrimes	char	*se_user;		/* user name to run as */
18630807Sache	char    *se_group;              /* group name to run as */
18730792Sache#ifdef  LOGIN_CAP
18830792Sache	char    *se_class;              /* login class name to run with */
18930792Sache#endif
1901553Srgrimes	struct	biltin *se_bi;		/* if built-in, description */
1911553Srgrimes	char	*se_server;		/* server program */
1921553Srgrimes#define	MAXARGV 20
1931553Srgrimes	char	*se_argv[MAXARGV+1];	/* program arguments */
1941553Srgrimes	int	se_fd;			/* open descriptor */
1951553Srgrimes	struct	sockaddr_in se_ctrladdr;/* bound address */
19619618Sjulian	u_char	se_type;		/* type: normal, mux, or mux+ */
19719618Sjulian	u_char	se_checked;		/* looked at during merge */
19819618Sjulian	u_char	se_accept;		/* i.e., wait/nowait mode */
19919618Sjulian	u_char	se_rpc;			/* ==1 if RPC service */
2002657Scsgr	int	se_rpc_prog;		/* RPC program number */
2012657Scsgr	u_int	se_rpc_lowvers;		/* RPC low version */
2022657Scsgr	u_int	se_rpc_highvers;	/* RPC high version */
2031553Srgrimes	int	se_count;		/* number started since se_time */
2041553Srgrimes	struct	timeval se_time;	/* start of se_count */
2051553Srgrimes	struct	servtab *se_next;
2061553Srgrimes} *servtab;
2071553Srgrimes
2081553Srgrimes#define NORM_TYPE	0
2091553Srgrimes#define MUX_TYPE	1
2101553Srgrimes#define MUXPLUS_TYPE	2
21136042Sguido#define TTCP_TYPE	3
2121553Srgrimes#define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
2131553Srgrimes			 ((sep)->se_type == MUXPLUS_TYPE))
2141553Srgrimes#define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
21536042Sguido#define ISTTCP(sep)	((sep)->se_type == TTCP_TYPE)
2161553Srgrimes
2171553Srgrimes
2181553Srgrimesvoid		chargen_dg __P((int, struct servtab *));
2191553Srgrimesvoid		chargen_stream __P((int, struct servtab *));
2201553Srgrimesvoid		close_sep __P((struct servtab *));
22142122Sdesvoid		flag_signal __P((char));
22242122Sdesvoid		flag_config __P((int));
22342122Sdesvoid		config __P((void));
2241553Srgrimesvoid		daytime_dg __P((int, struct servtab *));
2251553Srgrimesvoid		daytime_stream __P((int, struct servtab *));
2261553Srgrimesvoid		discard_dg __P((int, struct servtab *));
2271553Srgrimesvoid		discard_stream __P((int, struct servtab *));
2281553Srgrimesvoid		echo_dg __P((int, struct servtab *));
2291553Srgrimesvoid		echo_stream __P((int, struct servtab *));
2301553Srgrimesvoid		endconfig __P((void));
2311553Srgrimesstruct servtab *enter __P((struct servtab *));
2321553Srgrimesvoid		freeconfig __P((struct servtab *));
2331553Srgrimesstruct servtab *getconfigent __P((void));
23440910Sphkvoid		ident_stream __P((int, struct servtab *));
2351553Srgrimesvoid		machtime_dg __P((int, struct servtab *));
2361553Srgrimesvoid		machtime_stream __P((int, struct servtab *));
2371553Srgrimeschar	       *newstr __P((char *));
2381553Srgrimeschar	       *nextline __P((FILE *));
2391553Srgrimesvoid		print_service __P((char *, struct servtab *));
24019618Sjulianvoid		addchild __P((struct servtab *, int));
24142122Sdesvoid		flag_reapchild __P((int));
24242122Sdesvoid		reapchild __P((void));
24319618Sjulianvoid		enable __P((struct servtab *));
24419618Sjulianvoid		disable __P((struct servtab *));
24542122Sdesvoid		flag_retry __P((int));
24642122Sdesvoid		retry __P((void));
2471553Srgrimesint		setconfig __P((void));
2481553Srgrimesvoid		setup __P((struct servtab *));
2491553Srgrimeschar	       *sskip __P((char **));
2501553Srgrimeschar	       *skip __P((char **));
2511553Srgrimesstruct servtab *tcpmux __P((int));
25230847Sdimaint		cpmip __P((struct servtab *, int));
2531553Srgrimes
2542657Scsgrvoid		unregisterrpc __P((register struct servtab *sep));
2552657Scsgr
2561553Srgrimesstruct biltin {
2571553Srgrimes	char	*bi_service;		/* internally provided service name */
2581553Srgrimes	int	bi_socktype;		/* type of socket supported */
2591553Srgrimes	short	bi_fork;		/* 1 if should fork before call */
26030847Sdima	int	bi_maxchild;		/* max number of children (default) */
2611553Srgrimes	void	(*bi_fn)();		/* function which performs it */
2621553Srgrimes} biltins[] = {
2631553Srgrimes	/* Echo received data */
2641553Srgrimes	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream },
2651553Srgrimes	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg },
2661553Srgrimes
2671553Srgrimes	/* Internet /dev/null */
2681553Srgrimes	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream },
2691553Srgrimes	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg },
2701553Srgrimes
2711553Srgrimes	/* Return 32 bit time since 1970 */
2721553Srgrimes	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream },
2731553Srgrimes	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg },
2741553Srgrimes
2751553Srgrimes	/* Return human-readable time */
2761553Srgrimes	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream },
2771553Srgrimes	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg },
2781553Srgrimes
2791553Srgrimes	/* Familiar character generator */
2801553Srgrimes	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream },
2811553Srgrimes	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg },
2821553Srgrimes
2831553Srgrimes	{ "tcpmux",	SOCK_STREAM,	1, 0,	(void (*)())tcpmux },
2841553Srgrimes
28540910Sphk	{ "ident",	SOCK_STREAM,	1, 0,	ident_stream },
28640910Sphk
2871553Srgrimes	{ NULL }
2881553Srgrimes};
2891553Srgrimes
2901553Srgrimes#define NUMINT	(sizeof(intab) / sizeof(struct inent))
2911553Srgrimeschar	*CONFIG = _PATH_INETDCONF;
29217482Sjulianchar	*pid_file = _PATH_INETDPID;
29313142Speter
29413142Speter#ifdef OLD_SETPROCTITLE
2951553Srgrimeschar	**Argv;
2961553Srgrimeschar 	*LastArg;
29713142Speter#endif
2981553Srgrimes
2991553Srgrimesint
30033794Spstgetvalue(arg, value, whine)
30133794Spst	char *arg, *whine;
30233794Spst	int  *value;
30333794Spst{
30433794Spst	int  tmp;
30533794Spst	char *p;
30633794Spst
30733794Spst	tmp = strtol(arg, &p, 0);
30833794Spst	if (tmp < 1 || *p) {
30933794Spst		syslog(LOG_ERR, whine, arg);
31033794Spst		return 1;			/* failure */
31133794Spst	}
31233794Spst	*value = tmp;
31333794Spst	return 0;				/* success */
31433794Spst}
31533794Spst
31633794Spstint
3171553Srgrimesmain(argc, argv, envp)
3181553Srgrimes	int argc;
3191553Srgrimes	char *argv[], *envp[];
3201553Srgrimes{
3211553Srgrimes	struct servtab *sep;
3221553Srgrimes	struct passwd *pwd;
32330807Sache	struct group *grp;
32435848Sguido	struct sigaction sa, sapipe;
3251553Srgrimes	int tmpint, ch, dofork;
3261553Srgrimes	pid_t pid;
3271553Srgrimes	char buf[50];
3282659Scsgr	struct  sockaddr_in peer;
3292659Scsgr	int i;
33021640Speter#ifdef LOGIN_CAP
33121640Speter	login_cap_t *lc = NULL;
33221640Speter#endif
3331553Srgrimes
33413142Speter
33513142Speter#ifdef OLD_SETPROCTITLE
3361553Srgrimes	Argv = argv;
3371553Srgrimes	if (envp == 0 || *envp == 0)
3381553Srgrimes		envp = argv;
3391553Srgrimes	while (*envp)
3401553Srgrimes		envp++;
3411553Srgrimes	LastArg = envp[-1] + strlen(envp[-1]);
34213142Speter#endif
3431553Srgrimes
3441553Srgrimes	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
3451553Srgrimes
34617482Sjulian	bind_address.s_addr = htonl(INADDR_ANY);
34733794Spst	while ((ch = getopt(argc, argv, "dlR:a:c:C:p:")) != -1)
3481553Srgrimes		switch(ch) {
3491553Srgrimes		case 'd':
3501553Srgrimes			debug = 1;
3511553Srgrimes			options |= SO_DEBUG;
3521553Srgrimes			break;
3532659Scsgr		case 'l':
3542659Scsgr			log = 1;
3552659Scsgr			break;
35633794Spst		case 'R':
35733794Spst			getvalue(optarg, &toomany,
35833794Spst				"-R %s: bad value for service invocation rate");
3591553Srgrimes			break;
36033794Spst		case 'c':
36133794Spst			getvalue(optarg, &maxchild,
36233794Spst				"-c %s: bad value for maximum children");
36333794Spst			break;
36433794Spst		case 'C':
36533794Spst			getvalue(optarg, &maxcpm,
36633794Spst				"-C %s: bad value for maximum children/minute");
36733794Spst			break;
36817482Sjulian		case 'a':
36917482Sjulian			if (!inet_aton(optarg, &bind_address)) {
37017482Sjulian				syslog(LOG_ERR,
37117482Sjulian			         "-a %s: invalid IP address", optarg);
37219617Sjulian				exit(EX_USAGE);
37317482Sjulian			}
37417482Sjulian			break;
37517482Sjulian		case 'p':
37617482Sjulian			pid_file = optarg;
37717482Sjulian			break;
3781553Srgrimes		case '?':
3791553Srgrimes		default:
3801553Srgrimes			syslog(LOG_ERR,
38117482Sjulian				"usage: inetd [-dl] [-a address] [-R rate]"
38233794Spst				" [-c maximum] [-C rate]"
38317482Sjulian				" [-p pidfile] [conf-file]");
38419617Sjulian			exit(EX_USAGE);
3851553Srgrimes		}
3861553Srgrimes	argc -= optind;
3871553Srgrimes	argv += optind;
3881553Srgrimes
3891553Srgrimes	if (argc > 0)
3901553Srgrimes		CONFIG = argv[0];
3911553Srgrimes	if (debug == 0) {
39211447Swollman		FILE *fp;
39312024Speter		if (daemon(0, 0) < 0) {
39412024Speter			syslog(LOG_WARNING, "daemon(0,0) failed: %m");
39512024Speter		}
39612024Speter		/*
39712024Speter		 * In case somebody has started inetd manually, we need to
39812024Speter		 * clear the logname, so that old servers run as root do not
39912024Speter		 * get the user's logname..
40012024Speter		 */
40112024Speter		if (setlogin("") < 0) {
40212024Speter			syslog(LOG_WARNING, "cannot clear logname: %m");
40312024Speter			/* no big deal if it fails.. */
40412024Speter		}
40511447Swollman		pid = getpid();
40617482Sjulian		fp = fopen(pid_file, "w");
40711447Swollman		if (fp) {
40811447Swollman			fprintf(fp, "%ld\n", (long)pid);
40911447Swollman			fclose(fp);
41011447Swollman		} else {
41117482Sjulian			syslog(LOG_WARNING, "%s: %m", pid_file);
41211447Swollman		}
4131553Srgrimes	}
41435948Sbde	sa.sa_flags = 0;
41535948Sbde	sigemptyset(&sa.sa_mask);
41635948Sbde	sigaddset(&sa.sa_mask, SIGALRM);
41735948Sbde	sigaddset(&sa.sa_mask, SIGCHLD);
41835948Sbde	sigaddset(&sa.sa_mask, SIGHUP);
41942122Sdes	sa.sa_handler = flag_retry;
42035848Sguido	sigaction(SIGALRM, &sa, (struct sigaction *)0);
42142122Sdes	config();
42242122Sdes	sa.sa_handler = flag_config;
42335848Sguido	sigaction(SIGHUP, &sa, (struct sigaction *)0);
42442122Sdes	sa.sa_handler = flag_reapchild;
42535848Sguido	sigaction(SIGCHLD, &sa, (struct sigaction *)0);
42635848Sguido	sa.sa_handler = SIG_IGN;
42735948Sbde	sigaction(SIGPIPE, &sa, &sapipe);
4281553Srgrimes
4291553Srgrimes	{
4301553Srgrimes		/* space for daemons to overwrite environment for ps */
4311553Srgrimes#define	DUMMYSIZE	100
4321553Srgrimes		char dummy[DUMMYSIZE];
4331553Srgrimes
43419298Salex		(void)memset(dummy, 'x', DUMMYSIZE - 1);
4351553Srgrimes		dummy[DUMMYSIZE - 1] = '\0';
4361553Srgrimes		(void)setenv("inetd_dummy", dummy, 1);
4371553Srgrimes	}
4381553Srgrimes
43942122Sdes	if (pipe(signalpipe) != 0)
44042122Sdes	{
44142122Sdes	    syslog(LOG_ERR, "pipe: %%m");
44242122Sdes	    exit(EX_OSERR);
44342122Sdes	}
44442122Sdes	FD_SET(signalpipe[0], &allsock);
44542122Sdes	if (signalpipe[0]>maxsock) maxsock = signalpipe[0];
44641685Sdillon
4471553Srgrimes	for (;;) {
4481553Srgrimes	    int n, ctrl;
4491553Srgrimes	    fd_set readable;
4501553Srgrimes
4511553Srgrimes	    if (nsock == 0) {
45242122Sdes		(void) sigblock(SIGBLOCK);
4531553Srgrimes		while (nsock == 0)
4541553Srgrimes		    sigpause(0L);
45542122Sdes		(void) sigsetmask(0L);
4561553Srgrimes	    }
4571553Srgrimes	    readable = allsock;
45842122Sdes	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
45942122Sdes		(fd_set *)0, (struct timeval *)0)) <= 0) {
46042122Sdes		    if (n < 0 && errno != EINTR) {
4611553Srgrimes			syslog(LOG_WARNING, "select: %m");
46228907Simp			sleep(1);
46328907Simp		    }
4641553Srgrimes		    continue;
4651553Srgrimes	    }
46642122Sdes	    /* handle any queued signal flags */
46742122Sdes	    if (FD_ISSET(signalpipe[0], &readable))
46842122Sdes	    {
46942122Sdes		int n;
47042122Sdes		if (ioctl(signalpipe[0], FIONREAD, &n) == 0)
47142122Sdes		{
47242122Sdes		    while (--n >= 0)
47342122Sdes		    {
47442122Sdes			char c;
47542122Sdes			if (read(signalpipe[0], &c, 1) == 1)
47642122Sdes			{
47742122Sdes			    if (debug) warnx("Handling signal flag %c", c);
47842122Sdes		    	    switch(c)
47942122Sdes		    	    {
48042122Sdes		    	    case 'A': /* sigalrm */
48142122Sdes				retry(); break;
48242122Sdes		    	    case 'C': /* sigchld */
48342122Sdes				reapchild(); break;
48442122Sdes		    	    case 'H': /* sighup */
48542122Sdes				config(); break;
48642122Sdes		    	    }
48742122Sdes			}
48842122Sdes			else
48942122Sdes			{
49042122Sdes		    	    syslog(LOG_ERR, "read: %m");
49142122Sdes		    	    exit(EX_OSERR);
49242122Sdes			}
49342122Sdes		    }
49442122Sdes		}
49542122Sdes		else
49642122Sdes		{
49742122Sdes		    syslog(LOG_ERR, "ioctl: %m");
49842122Sdes		    exit(EX_OSERR);
49942122Sdes		}
50042122Sdes	    }
5011553Srgrimes	    for (sep = servtab; n && sep; sep = sep->se_next)
5021553Srgrimes	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
5031553Srgrimes		    n--;
5041553Srgrimes		    if (debug)
50529602Scharnier			    warnx("someone wants %s", sep->se_service);
50619618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
5071553Srgrimes			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
5081553Srgrimes				(int *)0);
5091553Srgrimes			    if (debug)
51029602Scharnier				    warnx("accept, ctrl %d", ctrl);
5111553Srgrimes			    if (ctrl < 0) {
5121553Srgrimes				    if (errno != EINTR)
5131553Srgrimes					    syslog(LOG_WARNING,
5141553Srgrimes						"accept (for %s): %m",
51537844Sphk						sep->se_service);
51637816Sphk                                      if (sep->se_accept &&
51737816Sphk                                          sep->se_socktype == SOCK_STREAM)
51837816Sphk                                              close(ctrl);
5191553Srgrimes				    continue;
5201553Srgrimes			    }
52130847Sdima			    if (cpmip(sep, ctrl) < 0) {
52230847Sdima				close(ctrl);
52330847Sdima				continue;
52430847Sdima			    }
52519617Sjulian			    if (log) {
5262659Scsgr				i = sizeof peer;
52730847Sdima				if (getpeername(ctrl, (struct sockaddr *)
5282659Scsgr						&peer, &i)) {
5292659Scsgr					syslog(LOG_WARNING,
5302659Scsgr						"getpeername(for %s): %m",
5312659Scsgr						sep->se_service);
53230847Sdima					close(ctrl);
5332659Scsgr					continue;
5342659Scsgr				}
5352659Scsgr				syslog(LOG_INFO,"%s from %s",
5362659Scsgr					sep->se_service,
5372659Scsgr					inet_ntoa(peer.sin_addr));
5382659Scsgr			    }
5391553Srgrimes		    } else
5401553Srgrimes			    ctrl = sep->se_fd;
54142122Sdes		    (void) sigblock(SIGBLOCK);
5421553Srgrimes		    pid = 0;
5431553Srgrimes		    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
5441553Srgrimes		    if (dofork) {
5451553Srgrimes			    if (sep->se_count++ == 0)
54637856Sache				(void)gettimeofday(&sep->se_time, (struct timezone *)NULL);
5471553Srgrimes			    else if (sep->se_count >= toomany) {
5481553Srgrimes				struct timeval now;
5491553Srgrimes
55037856Sache				(void)gettimeofday(&now, (struct timezone *)NULL);
5511553Srgrimes				if (now.tv_sec - sep->se_time.tv_sec >
5521553Srgrimes				    CNT_INTVL) {
5531553Srgrimes					sep->se_time = now;
5541553Srgrimes					sep->se_count = 1;
5551553Srgrimes				} else {
5561553Srgrimes					syslog(LOG_ERR,
5571553Srgrimes			"%s/%s server failing (looping), service terminated",
5581553Srgrimes					    sep->se_service, sep->se_proto);
5591553Srgrimes					close_sep(sep);
56042122Sdes					sigsetmask(0L);
5611553Srgrimes					if (!timingout) {
5621553Srgrimes						timingout = 1;
5631553Srgrimes						alarm(RETRYTIME);
5641553Srgrimes					}
5651553Srgrimes					continue;
5661553Srgrimes				}
5671553Srgrimes			    }
5681553Srgrimes			    pid = fork();
5691553Srgrimes		    }
5701553Srgrimes		    if (pid < 0) {
5711553Srgrimes			    syslog(LOG_ERR, "fork: %m");
57219618Sjulian			    if (sep->se_accept &&
5731553Srgrimes				sep->se_socktype == SOCK_STREAM)
5741553Srgrimes				    close(ctrl);
57542122Sdes			    sigsetmask(0L);
5761553Srgrimes			    sleep(1);
5771553Srgrimes			    continue;
5781553Srgrimes		    }
57919618Sjulian		    if (pid)
58019618Sjulian			addchild(sep, pid);
58142122Sdes		    sigsetmask(0L);
5821553Srgrimes		    if (pid == 0) {
5831553Srgrimes			    if (dofork) {
5841553Srgrimes				if (debug)
58529602Scharnier					warnx("+ closing from %d", maxsock);
5861553Srgrimes				for (tmpint = maxsock; tmpint > 2; tmpint--)
5871553Srgrimes					if (tmpint != ctrl)
58819617Sjulian						(void) close(tmpint);
5891553Srgrimes			    }
59035829Sguido			    /*
59135829Sguido			     * Call tcpmux to find the real service to exec.
59235829Sguido			     */
59335829Sguido			    if (sep->se_bi &&
59435829Sguido				sep->se_bi->bi_fn == (void (*)()) tcpmux) {
59535829Sguido				    sep = tcpmux(ctrl);
59635829Sguido				    if (sep == NULL) {
59735829Sguido					    close(ctrl);
59835829Sguido					    _exit(0);
59935829Sguido				    }
60035829Sguido			    }
60119617Sjulian			    if (sep->se_bi) {
6021553Srgrimes				(*sep->se_bi->bi_fn)(ctrl, sep);
60319617Sjulian				/* NOTREACHED */
60419617Sjulian			    } else {
6051553Srgrimes				if (debug)
60629602Scharnier					warnx("%d execl %s",
60729602Scharnier						getpid(), sep->se_server);
6081553Srgrimes				dup2(ctrl, 0);
6091553Srgrimes				close(ctrl);
6101553Srgrimes				dup2(0, 1);
6111553Srgrimes				dup2(0, 2);
6121553Srgrimes				if ((pwd = getpwnam(sep->se_user)) == NULL) {
6131553Srgrimes					syslog(LOG_ERR,
6141553Srgrimes					    "%s/%s: %s: No such user",
6151553Srgrimes						sep->se_service, sep->se_proto,
6161553Srgrimes						sep->se_user);
6171553Srgrimes					if (sep->se_socktype != SOCK_STREAM)
6181553Srgrimes						recv(0, buf, sizeof (buf), 0);
61919617Sjulian					_exit(EX_NOUSER);
6201553Srgrimes				}
62130807Sache				grp = NULL;
62230807Sache				if (   sep->se_group != NULL
62330807Sache				    && (grp = getgrnam(sep->se_group)) == NULL
62430807Sache				   ) {
62530807Sache					syslog(LOG_ERR,
62630807Sache					    "%s/%s: %s: No such group",
62730807Sache						sep->se_service, sep->se_proto,
62830807Sache						sep->se_group);
62930807Sache					if (sep->se_socktype != SOCK_STREAM)
63030807Sache						recv(0, buf, sizeof (buf), 0);
63130807Sache					_exit(EX_NOUSER);
63230807Sache				}
63330807Sache				if (grp != NULL)
63430807Sache					pwd->pw_gid = grp->gr_gid;
63521640Speter#ifdef LOGIN_CAP
63630792Sache				if ((lc = login_getclass(sep->se_class)) == NULL) {
63730792Sache					/* error syslogged by getclass */
63830792Sache					syslog(LOG_ERR,
63930792Sache					    "%s/%s: %s: login class error",
64037850Sache						sep->se_service, sep->se_proto,
64137850Sache						sep->se_class);
64230792Sache					if (sep->se_socktype != SOCK_STREAM)
64330792Sache						recv(0, buf, sizeof (buf), 0);
64430792Sache					_exit(EX_NOUSER);
64530792Sache				}
64621640Speter#endif
64712024Speter				if (setsid() < 0) {
64812024Speter					syslog(LOG_ERR,
64912024Speter						"%s: can't setsid(): %m",
65012024Speter						 sep->se_service);
65119617Sjulian					/* _exit(EX_OSERR); not fatal yet */
65212024Speter				}
65321640Speter#ifdef LOGIN_CAP
65421640Speter				if (setusercontext(lc, pwd, pwd->pw_uid,
65521640Speter				    LOGIN_SETALL) != 0) {
65621640Speter					syslog(LOG_ERR,
65721640Speter					 "%s: can't setusercontext(..%s..): %m",
65821640Speter					 sep->se_service, sep->se_user);
65921640Speter					_exit(EX_OSERR);
66021640Speter				}
66121640Speter#else
6621553Srgrimes				if (pwd->pw_uid) {
66312024Speter					if (setlogin(sep->se_user) < 0) {
66412024Speter						syslog(LOG_ERR,
66512024Speter						 "%s: can't setlogin(%s): %m",
66612024Speter						 sep->se_service, sep->se_user);
66719617Sjulian						/* _exit(EX_OSERR); not yet */
66812024Speter					}
6691553Srgrimes					if (setgid(pwd->pw_gid) < 0) {
6701553Srgrimes						syslog(LOG_ERR,
6718857Srgrimes						  "%s: can't set gid %d: %m",
6721553Srgrimes						  sep->se_service, pwd->pw_gid);
67319617Sjulian						_exit(EX_OSERR);
6741553Srgrimes					}
6751553Srgrimes					(void) initgroups(pwd->pw_name,
6761553Srgrimes							pwd->pw_gid);
6771553Srgrimes					if (setuid(pwd->pw_uid) < 0) {
6781553Srgrimes						syslog(LOG_ERR,
6798857Srgrimes						  "%s: can't set uid %d: %m",
6801553Srgrimes						  sep->se_service, pwd->pw_uid);
68119617Sjulian						_exit(EX_OSERR);
6821553Srgrimes					}
6831553Srgrimes				}
68421640Speter#endif
68535948Sbde				sigaction(SIGPIPE, &sapipe,
68635948Sbde				    (struct sigaction *)0);
6871553Srgrimes				execv(sep->se_server, sep->se_argv);
6881553Srgrimes				if (sep->se_socktype != SOCK_STREAM)
6891553Srgrimes					recv(0, buf, sizeof (buf), 0);
6901553Srgrimes				syslog(LOG_ERR,
6911553Srgrimes				    "cannot execute %s: %m", sep->se_server);
69219617Sjulian				_exit(EX_OSERR);
6931553Srgrimes			    }
6941553Srgrimes		    }
69519618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
6961553Srgrimes			    close(ctrl);
6971553Srgrimes		}
6981553Srgrimes	}
6991553Srgrimes}
7001553Srgrimes
70119618Sjulian/*
70242122Sdes * Add a signal flag to the signal flag queue for later handling
70342122Sdes */
70442122Sdes
70542122Sdesvoid flag_signal(c)
70642122Sdes    char c;
70742122Sdes{
70842122Sdes    if (write(signalpipe[1], &c, 1) != 1)
70942122Sdes    {
71042122Sdes	syslog(LOG_ERR, "write: %m");
71142122Sdes	exit(EX_OSERR);
71242122Sdes    }
71342122Sdes}
71442122Sdes
71542122Sdes/*
71619618Sjulian * Record a new child pid for this service. If we've reached the
71719618Sjulian * limit on children, then stop accepting incoming requests.
71819618Sjulian */
71919618Sjulian
7201553Srgrimesvoid
72119618Sjulianaddchild(struct servtab *sep, pid_t pid)
72219618Sjulian{
72319618Sjulian#ifdef SANITY_CHECK
72419618Sjulian	if (sep->se_numchild >= sep->se_maxchild) {
72519618Sjulian		syslog(LOG_ERR, "%s: %d >= %d",
72619618Sjulian		    __FUNCTION__, sep->se_numchild, sep->se_maxchild);
72719618Sjulian		exit(EX_SOFTWARE);
72819618Sjulian	}
72919618Sjulian#endif
73019618Sjulian	if (sep->se_maxchild == 0)
73119618Sjulian		return;
73219618Sjulian	sep->se_pids[sep->se_numchild++] = pid;
73319618Sjulian	if (sep->se_numchild == sep->se_maxchild)
73419618Sjulian		disable(sep);
73519618Sjulian}
73619618Sjulian
73719618Sjulian/*
73819618Sjulian * Some child process has exited. See if it's on somebody's list.
73919618Sjulian */
74019618Sjulian
74119618Sjulianvoid
74242122Sdesflag_reapchild(signo)
7431553Srgrimes	int signo;
7441553Srgrimes{
74542122Sdes    flag_signal('C');
74642122Sdes}
74742122Sdes
74842122Sdesvoid
74942122Sdesreapchild()
75042122Sdes{
75119618Sjulian	int k, status;
7521553Srgrimes	pid_t pid;
7531553Srgrimes	struct servtab *sep;
7541553Srgrimes
7551553Srgrimes	for (;;) {
7561553Srgrimes		pid = wait3(&status, WNOHANG, (struct rusage *)0);
7571553Srgrimes		if (pid <= 0)
7581553Srgrimes			break;
7591553Srgrimes		if (debug)
76029602Scharnier			warnx("%d reaped, status %#x", pid, status);
76119618Sjulian		for (sep = servtab; sep; sep = sep->se_next) {
76219618Sjulian			for (k = 0; k < sep->se_numchild; k++)
76319618Sjulian				if (sep->se_pids[k] == pid)
76419618Sjulian					break;
76519618Sjulian			if (k == sep->se_numchild)
76619618Sjulian				continue;
76719618Sjulian			if (sep->se_numchild == sep->se_maxchild)
76819618Sjulian				enable(sep);
76919618Sjulian			sep->se_pids[k] = sep->se_pids[--sep->se_numchild];
77019618Sjulian			if (status)
77119618Sjulian				syslog(LOG_WARNING,
77219618Sjulian				    "%s[%d]: exit status 0x%x",
77319618Sjulian				    sep->se_server, pid, status);
77419618Sjulian			break;
77519618Sjulian		}
7761553Srgrimes	}
7771553Srgrimes}
7781553Srgrimes
7791553Srgrimesvoid
78042122Sdesflag_config(signo)
7811553Srgrimes	int signo;
7821553Srgrimes{
78342122Sdes    flag_signal('H');
78442122Sdes}
78542122Sdes
78642122Sdesvoid config()
78742122Sdes{
78819618Sjulian	struct servtab *sep, *new, **sepp;
78942122Sdes	long omask;
7901553Srgrimes
7911553Srgrimes	if (!setconfig()) {
7921553Srgrimes		syslog(LOG_ERR, "%s: %m", CONFIG);
7931553Srgrimes		return;
7941553Srgrimes	}
7951553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
7961553Srgrimes		sep->se_checked = 0;
79719618Sjulian	while ((new = getconfigent())) {
79830807Sache		if (getpwnam(new->se_user) == NULL) {
7991553Srgrimes			syslog(LOG_ERR,
8001553Srgrimes				"%s/%s: No such user '%s', service ignored",
80119618Sjulian				new->se_service, new->se_proto, new->se_user);
8021553Srgrimes			continue;
8031553Srgrimes		}
80430807Sache		if (new->se_group && getgrnam(new->se_group) == NULL) {
80530807Sache			syslog(LOG_ERR,
80630807Sache				"%s/%s: No such group '%s', service ignored",
80730807Sache				new->se_service, new->se_proto, new->se_group);
80830807Sache			continue;
80930807Sache		}
81030792Sache#ifdef LOGIN_CAP
81130792Sache		if (login_getclass(new->se_class) == NULL) {
81230792Sache			/* error syslogged by getclass */
81330792Sache			syslog(LOG_ERR,
81437850Sache				"%s/%s: %s: login class error, service ignored",
81537850Sache				new->se_service, new->se_proto, new->se_class);
81630792Sache			continue;
81730792Sache		}
81830792Sache#endif
8191553Srgrimes		for (sep = servtab; sep; sep = sep->se_next)
82019618Sjulian			if (strcmp(sep->se_service, new->se_service) == 0 &&
82119618Sjulian			    strcmp(sep->se_proto, new->se_proto) == 0)
8221553Srgrimes				break;
8231553Srgrimes		if (sep != 0) {
8241553Srgrimes			int i;
8251553Srgrimes
82619618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; }
82742122Sdes			omask = sigblock(SIGBLOCK);
82819618Sjulian			/* copy over outstanding child pids */
82919618Sjulian			if (sep->se_maxchild && new->se_maxchild) {
83019618Sjulian				new->se_numchild = sep->se_numchild;
83119618Sjulian				if (new->se_numchild > new->se_maxchild)
83219618Sjulian					new->se_numchild = new->se_maxchild;
83319618Sjulian				memcpy(new->se_pids, sep->se_pids,
83419618Sjulian				    new->se_numchild * sizeof(*new->se_pids));
83519618Sjulian			}
83619618Sjulian			SWAP(sep->se_pids, new->se_pids);
83719618Sjulian			sep->se_maxchild = new->se_maxchild;
83819618Sjulian			sep->se_numchild = new->se_numchild;
83930847Sdima			sep->se_maxcpm = new->se_maxcpm;
84019618Sjulian			/* might need to turn on or off service now */
84119618Sjulian			if (sep->se_fd >= 0) {
84219618Sjulian			      if (sep->se_maxchild
84319618Sjulian				  && sep->se_numchild == sep->se_maxchild) {
84419618Sjulian				      if (FD_ISSET(sep->se_fd, &allsock))
84519618Sjulian					  disable(sep);
84619618Sjulian			      } else {
84719618Sjulian				      if (!FD_ISSET(sep->se_fd, &allsock))
84819618Sjulian					  enable(sep);
84919618Sjulian			      }
85019618Sjulian			}
85119618Sjulian			sep->se_accept = new->se_accept;
85230807Sache			SWAP(sep->se_user, new->se_user);
85330807Sache			SWAP(sep->se_group, new->se_group);
85430792Sache#ifdef LOGIN_CAP
85530807Sache			SWAP(sep->se_class, new->se_class);
85630792Sache#endif
85730807Sache			SWAP(sep->se_server, new->se_server);
8581553Srgrimes			for (i = 0; i < MAXARGV; i++)
85919618Sjulian				SWAP(sep->se_argv[i], new->se_argv[i]);
86042122Sdes			sigsetmask(omask);
86119618Sjulian			freeconfig(new);
8621553Srgrimes			if (debug)
8631553Srgrimes				print_service("REDO", sep);
8641553Srgrimes		} else {
86519618Sjulian			sep = enter(new);
8661553Srgrimes			if (debug)
8671553Srgrimes				print_service("ADD ", sep);
8681553Srgrimes		}
8691553Srgrimes		sep->se_checked = 1;
8701553Srgrimes		if (ISMUX(sep)) {
8711553Srgrimes			sep->se_fd = -1;
8721553Srgrimes			continue;
8731553Srgrimes		}
8742657Scsgr		if (!sep->se_rpc) {
8752657Scsgr			sp = getservbyname(sep->se_service, sep->se_proto);
8762657Scsgr			if (sp == 0) {
8772657Scsgr				syslog(LOG_ERR, "%s/%s: unknown service",
8782657Scsgr			    	sep->se_service, sep->se_proto);
8792657Scsgr				sep->se_checked = 0;
8802657Scsgr				continue;
8812657Scsgr			}
8822657Scsgr			if (sp->s_port != sep->se_ctrladdr.sin_port) {
8832657Scsgr				sep->se_ctrladdr.sin_family = AF_INET;
88422306Sjulian				sep->se_ctrladdr.sin_addr = bind_address;
8852657Scsgr				sep->se_ctrladdr.sin_port = sp->s_port;
8862657Scsgr				if (sep->se_fd >= 0)
8872657Scsgr					close_sep(sep);
8882657Scsgr			}
8892657Scsgr		} else {
8902657Scsgr			rpc = getrpcbyname(sep->se_service);
8912657Scsgr			if (rpc == 0) {
8922657Scsgr				syslog(LOG_ERR, "%s/%s unknown RPC service.",
8932657Scsgr					sep->se_service, sep->se_proto);
8942657Scsgr				if (sep->se_fd != -1)
8952657Scsgr					(void) close(sep->se_fd);
8962657Scsgr				sep->se_fd = -1;
8972657Scsgr					continue;
8982657Scsgr			}
8992657Scsgr			if (rpc->r_number != sep->se_rpc_prog) {
9002657Scsgr				if (sep->se_rpc_prog)
9012657Scsgr					unregisterrpc(sep);
9022657Scsgr				sep->se_rpc_prog = rpc->r_number;
9032657Scsgr				if (sep->se_fd != -1)
9042657Scsgr					(void) close(sep->se_fd);
9052657Scsgr				sep->se_fd = -1;
9062657Scsgr			}
9071553Srgrimes		}
9081553Srgrimes		if (sep->se_fd == -1)
9091553Srgrimes			setup(sep);
9101553Srgrimes	}
9111553Srgrimes	endconfig();
9121553Srgrimes	/*
9131553Srgrimes	 * Purge anything not looked at above.
9141553Srgrimes	 */
91542122Sdes	omask = sigblock(SIGBLOCK);
9161553Srgrimes	sepp = &servtab;
91719617Sjulian	while ((sep = *sepp)) {
9181553Srgrimes		if (sep->se_checked) {
9191553Srgrimes			sepp = &sep->se_next;
9201553Srgrimes			continue;
9211553Srgrimes		}
9221553Srgrimes		*sepp = sep->se_next;
9231553Srgrimes		if (sep->se_fd >= 0)
9241553Srgrimes			close_sep(sep);
9251553Srgrimes		if (debug)
9261553Srgrimes			print_service("FREE", sep);
9272657Scsgr		if (sep->se_rpc && sep->se_rpc_prog > 0)
9282657Scsgr			unregisterrpc(sep);
9291553Srgrimes		freeconfig(sep);
9301553Srgrimes		free((char *)sep);
9311553Srgrimes	}
93242122Sdes	(void) sigsetmask(omask);
9331553Srgrimes}
9341553Srgrimes
9351553Srgrimesvoid
9362657Scsgrunregisterrpc(sep)
9372657Scsgr	struct servtab *sep;
9382657Scsgr{
9392657Scsgr        int i;
9402657Scsgr        struct servtab *sepp;
94142122Sdes	long omask;
9422657Scsgr
94342122Sdes	omask = sigblock(SIGBLOCK);
9442657Scsgr        for (sepp = servtab; sepp; sepp = sepp->se_next) {
9452657Scsgr                if (sepp == sep)
9462657Scsgr                        continue;
9472657Scsgr		if (sep->se_checked == 0 ||
9482657Scsgr                    !sepp->se_rpc ||
9492657Scsgr                    sep->se_rpc_prog != sepp->se_rpc_prog)
9502657Scsgr			continue;
9512657Scsgr                return;
9522657Scsgr        }
9532657Scsgr        if (debug)
9542657Scsgr                print_service("UNREG", sep);
9552657Scsgr        for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++)
9562657Scsgr                pmap_unset(sep->se_rpc_prog, i);
9572657Scsgr        if (sep->se_fd != -1)
9582657Scsgr                (void) close(sep->se_fd);
9592657Scsgr        sep->se_fd = -1;
96042122Sdes	(void) sigsetmask(omask);
9612657Scsgr}
9622657Scsgr
9632657Scsgrvoid
96442122Sdesflag_retry(signo)
9651553Srgrimes	int signo;
9661553Srgrimes{
96742122Sdes    flag_signal('A');
96842122Sdes}
96942122Sdes
97042122Sdesvoid
97142122Sdesretry()
97242122Sdes{
9731553Srgrimes	struct servtab *sep;
9741553Srgrimes
9751553Srgrimes	timingout = 0;
9761553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
97719617Sjulian		if (sep->se_fd == -1 && !ISMUX(sep))
9781553Srgrimes			setup(sep);
9791553Srgrimes}
9801553Srgrimes
9811553Srgrimesvoid
9821553Srgrimessetup(sep)
9831553Srgrimes	struct servtab *sep;
9841553Srgrimes{
9851553Srgrimes	int on = 1;
9861553Srgrimes
9871553Srgrimes	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
9881553Srgrimes		if (debug)
98929602Scharnier			warn("socket failed on %s/%s",
99029602Scharnier				sep->se_service, sep->se_proto);
9911553Srgrimes		syslog(LOG_ERR, "%s/%s: socket: %m",
9921553Srgrimes		    sep->se_service, sep->se_proto);
9931553Srgrimes		return;
9941553Srgrimes	}
9951553Srgrimes#define	turnon(fd, opt) \
9961553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
9971553Srgrimes	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
9981553Srgrimes	    turnon(sep->se_fd, SO_DEBUG) < 0)
9991553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
10001553Srgrimes	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
10011553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
100225253Swollman#ifdef SO_PRIVSTATE
100313956Swollman	if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
100413956Swollman		syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
100525253Swollman#endif
10061553Srgrimes#undef turnon
100736042Sguido	if (sep->se_type == TTCP_TYPE)
100836042Sguido		if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH,
100936042Sguido		    (char *)&on, sizeof (on)) < 0)
101036042Sguido			syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m");
10111553Srgrimes	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
10121553Srgrimes	    sizeof (sep->se_ctrladdr)) < 0) {
10131553Srgrimes		if (debug)
101429602Scharnier			warn("bind failed on %s/%s",
101529602Scharnier				sep->se_service, sep->se_proto);
10161553Srgrimes		syslog(LOG_ERR, "%s/%s: bind: %m",
10171553Srgrimes		    sep->se_service, sep->se_proto);
10181553Srgrimes		(void) close(sep->se_fd);
10191553Srgrimes		sep->se_fd = -1;
10201553Srgrimes		if (!timingout) {
10211553Srgrimes			timingout = 1;
10221553Srgrimes			alarm(RETRYTIME);
10231553Srgrimes		}
10241553Srgrimes		return;
10251553Srgrimes	}
10262657Scsgr        if (sep->se_rpc) {
10272657Scsgr                int i, len = sizeof(struct sockaddr);
10282657Scsgr
10298857Srgrimes                if (getsockname(sep->se_fd,
10302657Scsgr				(struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
10312657Scsgr                        syslog(LOG_ERR, "%s/%s: getsockname: %m",
10322657Scsgr                               sep->se_service, sep->se_proto);
10332657Scsgr                        (void) close(sep->se_fd);
10342657Scsgr                        sep->se_fd = -1;
10358857Srgrimes                        return;
10362657Scsgr                }
10372657Scsgr                if (debug)
10382657Scsgr                        print_service("REG ", sep);
10392657Scsgr                for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
10402657Scsgr                        pmap_unset(sep->se_rpc_prog, i);
10412657Scsgr                        pmap_set(sep->se_rpc_prog, i,
10422657Scsgr                                 (sep->se_socktype == SOCK_DGRAM)
10432657Scsgr                                 ? IPPROTO_UDP : IPPROTO_TCP,
10442657Scsgr                                 ntohs(sep->se_ctrladdr.sin_port));
10452657Scsgr                }
10468857Srgrimes
10472657Scsgr        }
10481553Srgrimes	if (sep->se_socktype == SOCK_STREAM)
104917197Sdg		listen(sep->se_fd, 64);
105019618Sjulian	enable(sep);
10511553Srgrimes	if (debug) {
105229602Scharnier		warnx("registered %s on %d",
10531553Srgrimes			sep->se_server, sep->se_fd);
10541553Srgrimes	}
10551553Srgrimes}
10561553Srgrimes
10571553Srgrimes/*
10581553Srgrimes * Finish with a service and its socket.
10591553Srgrimes */
10601553Srgrimesvoid
10611553Srgrimesclose_sep(sep)
10621553Srgrimes	struct servtab *sep;
10631553Srgrimes{
10641553Srgrimes	if (sep->se_fd >= 0) {
106519618Sjulian		if (FD_ISSET(sep->se_fd, &allsock))
106619618Sjulian			disable(sep);
10671553Srgrimes		(void) close(sep->se_fd);
10681553Srgrimes		sep->se_fd = -1;
10691553Srgrimes	}
10701553Srgrimes	sep->se_count = 0;
107119618Sjulian	sep->se_numchild = 0;	/* forget about any existing children */
10721553Srgrimes}
10731553Srgrimes
10741553Srgrimesstruct servtab *
10751553Srgrimesenter(cp)
10761553Srgrimes	struct servtab *cp;
10771553Srgrimes{
10781553Srgrimes	struct servtab *sep;
107942122Sdes	long omask;
10801553Srgrimes
10811553Srgrimes	sep = (struct servtab *)malloc(sizeof (*sep));
10821553Srgrimes	if (sep == (struct servtab *)0) {
10831553Srgrimes		syslog(LOG_ERR, "Out of memory.");
108419617Sjulian		exit(EX_OSERR);
10851553Srgrimes	}
10861553Srgrimes	*sep = *cp;
10871553Srgrimes	sep->se_fd = -1;
108842122Sdes	omask = sigblock(SIGBLOCK);
10891553Srgrimes	sep->se_next = servtab;
10901553Srgrimes	servtab = sep;
109142122Sdes	sigsetmask(omask);
10921553Srgrimes	return (sep);
10931553Srgrimes}
10941553Srgrimes
109519618Sjulianvoid
109619618Sjulianenable(struct servtab *sep)
109719618Sjulian{
109819618Sjulian	if (debug)
109929602Scharnier		warnx(
110019618Sjulian		    "enabling %s, fd %d", sep->se_service, sep->se_fd);
110119618Sjulian#ifdef SANITY_CHECK
110219618Sjulian	if (sep->se_fd < 0) {
110319618Sjulian		syslog(LOG_ERR,
110419618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
110519618Sjulian		exit(EX_SOFTWARE);
110619618Sjulian	}
110719618Sjulian	if (ISMUX(sep)) {
110819618Sjulian		syslog(LOG_ERR,
110919618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
111019618Sjulian		exit(EX_SOFTWARE);
111119618Sjulian	}
111219618Sjulian	if (FD_ISSET(sep->se_fd, &allsock)) {
111319618Sjulian		syslog(LOG_ERR,
111419618Sjulian		    "%s: %s: not off", __FUNCTION__, sep->se_service);
111519618Sjulian		exit(EX_SOFTWARE);
111619618Sjulian	}
111719618Sjulian#endif
111819618Sjulian	FD_SET(sep->se_fd, &allsock);
111919618Sjulian	nsock++;
112019618Sjulian	if (sep->se_fd > maxsock)
112119618Sjulian		maxsock = sep->se_fd;
112219618Sjulian}
112319618Sjulian
112419618Sjulianvoid
112519618Sjuliandisable(struct servtab *sep)
112619618Sjulian{
112719618Sjulian	if (debug)
112829602Scharnier		warnx(
112919618Sjulian		    "disabling %s, fd %d", sep->se_service, sep->se_fd);
113019618Sjulian#ifdef SANITY_CHECK
113119618Sjulian	if (sep->se_fd < 0) {
113219618Sjulian		syslog(LOG_ERR,
113319618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
113419618Sjulian		exit(EX_SOFTWARE);
113519618Sjulian	}
113619618Sjulian	if (ISMUX(sep)) {
113719618Sjulian		syslog(LOG_ERR,
113819618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
113919618Sjulian		exit(EX_SOFTWARE);
114019618Sjulian	}
114119618Sjulian	if (!FD_ISSET(sep->se_fd, &allsock)) {
114219618Sjulian		syslog(LOG_ERR,
114319618Sjulian		    "%s: %s: not on", __FUNCTION__, sep->se_service);
114419618Sjulian		exit(EX_SOFTWARE);
114519618Sjulian	}
114619618Sjulian	if (nsock == 0) {
114719618Sjulian		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
114819618Sjulian		exit(EX_SOFTWARE);
114919618Sjulian	}
115019618Sjulian#endif
115119618Sjulian	FD_CLR(sep->se_fd, &allsock);
115219618Sjulian	nsock--;
115319618Sjulian	if (sep->se_fd == maxsock)
115419618Sjulian		maxsock--;
115519618Sjulian}
115619618Sjulian
11571553SrgrimesFILE	*fconfig = NULL;
11581553Srgrimesstruct	servtab serv;
11591553Srgrimeschar	line[LINE_MAX];
11601553Srgrimes
11611553Srgrimesint
11621553Srgrimessetconfig()
11631553Srgrimes{
11641553Srgrimes
11651553Srgrimes	if (fconfig != NULL) {
11661553Srgrimes		fseek(fconfig, 0L, SEEK_SET);
11671553Srgrimes		return (1);
11681553Srgrimes	}
11691553Srgrimes	fconfig = fopen(CONFIG, "r");
11701553Srgrimes	return (fconfig != NULL);
11711553Srgrimes}
11721553Srgrimes
11731553Srgrimesvoid
11741553Srgrimesendconfig()
11751553Srgrimes{
11761553Srgrimes	if (fconfig) {
11771553Srgrimes		(void) fclose(fconfig);
11781553Srgrimes		fconfig = NULL;
11791553Srgrimes	}
11801553Srgrimes}
11811553Srgrimes
11821553Srgrimesstruct servtab *
11831553Srgrimesgetconfigent()
11841553Srgrimes{
11851553Srgrimes	struct servtab *sep = &serv;
11861553Srgrimes	int argc;
118719618Sjulian	char *cp, *arg, *s;
11882657Scsgr	char *versp;
11891553Srgrimes	static char TCPMUX_TOKEN[] = "tcpmux/";
11901553Srgrimes#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
11911553Srgrimes
11921553Srgrimesmore:
11931553Srgrimes	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
11941553Srgrimes		;
11951553Srgrimes	if (cp == NULL)
11961553Srgrimes		return ((struct servtab *)0);
11971553Srgrimes	/*
11981553Srgrimes	 * clear the static buffer, since some fields (se_ctrladdr,
11991553Srgrimes	 * for example) don't get initialized here.
12001553Srgrimes	 */
12011553Srgrimes	memset((caddr_t)sep, 0, sizeof *sep);
12021553Srgrimes	arg = skip(&cp);
12031553Srgrimes	if (cp == NULL) {
12041553Srgrimes		/* got an empty line containing just blanks/tabs. */
12051553Srgrimes		goto more;
12061553Srgrimes	}
12071553Srgrimes	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
12081553Srgrimes		char *c = arg + MUX_LEN;
12091553Srgrimes		if (*c == '+') {
12101553Srgrimes			sep->se_type = MUXPLUS_TYPE;
12111553Srgrimes			c++;
12121553Srgrimes		} else
12131553Srgrimes			sep->se_type = MUX_TYPE;
12141553Srgrimes		sep->se_service = newstr(c);
12151553Srgrimes	} else {
12161553Srgrimes		sep->se_service = newstr(arg);
12171553Srgrimes		sep->se_type = NORM_TYPE;
12181553Srgrimes	}
12191553Srgrimes	arg = sskip(&cp);
12201553Srgrimes	if (strcmp(arg, "stream") == 0)
12211553Srgrimes		sep->se_socktype = SOCK_STREAM;
12221553Srgrimes	else if (strcmp(arg, "dgram") == 0)
12231553Srgrimes		sep->se_socktype = SOCK_DGRAM;
12241553Srgrimes	else if (strcmp(arg, "rdm") == 0)
12251553Srgrimes		sep->se_socktype = SOCK_RDM;
12261553Srgrimes	else if (strcmp(arg, "seqpacket") == 0)
12271553Srgrimes		sep->se_socktype = SOCK_SEQPACKET;
12281553Srgrimes	else if (strcmp(arg, "raw") == 0)
12291553Srgrimes		sep->se_socktype = SOCK_RAW;
12301553Srgrimes	else
12311553Srgrimes		sep->se_socktype = -1;
123236042Sguido
123336042Sguido	arg = sskip(&cp);
123436042Sguido	if (strcmp(arg, "tcp/ttcp") == 0) {
123536042Sguido		sep->se_type = TTCP_TYPE;
123636042Sguido		sep->se_proto = newstr("tcp");
123736042Sguido	} else {
123836042Sguido		sep->se_proto = newstr(arg);
123936042Sguido	}
12402657Scsgr        if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
124119237Sjoerg                memmove(sep->se_proto, sep->se_proto + 4,
124219237Sjoerg                    strlen(sep->se_proto) + 1 - 4);
12432657Scsgr                sep->se_rpc = 1;
12442657Scsgr                sep->se_rpc_prog = sep->se_rpc_lowvers =
12452657Scsgr			sep->se_rpc_lowvers = 0;
12462657Scsgr                sep->se_ctrladdr.sin_family = AF_INET;
12472657Scsgr                sep->se_ctrladdr.sin_port = 0;
124817482Sjulian                sep->se_ctrladdr.sin_addr = bind_address;
12492657Scsgr                if ((versp = rindex(sep->se_service, '/'))) {
12502657Scsgr                        *versp++ = '\0';
12512657Scsgr                        switch (sscanf(versp, "%d-%d",
12522657Scsgr                                       &sep->se_rpc_lowvers,
12532657Scsgr                                       &sep->se_rpc_highvers)) {
12542657Scsgr                        case 2:
12552657Scsgr                                break;
12562657Scsgr                        case 1:
12572657Scsgr                                sep->se_rpc_highvers =
12582657Scsgr                                        sep->se_rpc_lowvers;
12592657Scsgr                                break;
12602657Scsgr                        default:
12618857Srgrimes                                syslog(LOG_ERR,
12628857Srgrimes					"bad RPC version specifier; %s\n",
12632657Scsgr					sep->se_service);
12642657Scsgr                                freeconfig(sep);
12652657Scsgr                                goto more;
12662657Scsgr                        }
12672657Scsgr                }
12682657Scsgr                else {
12692657Scsgr                        sep->se_rpc_lowvers =
12702657Scsgr                                sep->se_rpc_highvers = 1;
12712657Scsgr                }
12722657Scsgr        }
12731553Srgrimes	arg = sskip(&cp);
127419618Sjulian	if (!strncmp(arg, "wait", 4))
127519618Sjulian		sep->se_accept = 0;
127619618Sjulian	else if (!strncmp(arg, "nowait", 6))
127719618Sjulian		sep->se_accept = 1;
127819618Sjulian	else {
127919618Sjulian		syslog(LOG_ERR,
128019618Sjulian			"%s: bad wait/nowait for service %s",
128119618Sjulian			CONFIG, sep->se_service);
128219618Sjulian		goto more;
128319618Sjulian	}
128433794Spst	sep->se_maxchild = maxchild;
128533794Spst	sep->se_maxcpm = maxcpm;
128619618Sjulian	if ((s = strchr(arg, '/')) != NULL) {
128719618Sjulian		char *eptr;
128819618Sjulian		u_long val;
128919618Sjulian
129019618Sjulian		val = strtoul(s + 1, &eptr, 10);
129130847Sdima		if (eptr == s + 1 || val > MAX_MAXCHLD) {
129219618Sjulian			syslog(LOG_ERR,
129319618Sjulian				"%s: bad max-child for service %s",
129419618Sjulian				CONFIG, sep->se_service);
129519618Sjulian			goto more;
129619618Sjulian		}
129719618Sjulian		sep->se_maxchild = val;
129830847Sdima		if (*eptr == '/')
129930847Sdima			sep->se_maxcpm = strtol(eptr + 1, &eptr, 10);
130030847Sdima		/*
130130847Sdima		 * explicitly do not check for \0 for future expansion /
130230847Sdima		 * backwards compatibility
130330847Sdima		 */
130419618Sjulian	}
13051553Srgrimes	if (ISMUX(sep)) {
13061553Srgrimes		/*
130719618Sjulian		 * Silently enforce "nowait" mode for TCPMUX services
130819618Sjulian		 * since they don't have an assigned port to listen on.
13091553Srgrimes		 */
131019618Sjulian		sep->se_accept = 1;
13111553Srgrimes		if (strcmp(sep->se_proto, "tcp")) {
13128857Srgrimes			syslog(LOG_ERR,
13131553Srgrimes				"%s: bad protocol for tcpmux service %s",
13141553Srgrimes				CONFIG, sep->se_service);
13151553Srgrimes			goto more;
13161553Srgrimes		}
13171553Srgrimes		if (sep->se_socktype != SOCK_STREAM) {
13188857Srgrimes			syslog(LOG_ERR,
13191553Srgrimes				"%s: bad socket type for tcpmux service %s",
13201553Srgrimes				CONFIG, sep->se_service);
13211553Srgrimes			goto more;
13221553Srgrimes		}
13231553Srgrimes	}
13241553Srgrimes	sep->se_user = newstr(sskip(&cp));
132530792Sache#ifdef LOGIN_CAP
132630792Sache	if ((s = strrchr(sep->se_user, '/')) != NULL) {
132730792Sache		*s = '\0';
132830792Sache		sep->se_class = newstr(s + 1);
132930792Sache	} else
133030792Sache		sep->se_class = newstr(RESOURCE_RC);
133130792Sache#endif
133230807Sache	if ((s = strrchr(sep->se_user, ':')) != NULL) {
133330807Sache		*s = '\0';
133430807Sache		sep->se_group = newstr(s + 1);
133530807Sache	} else
133630807Sache		sep->se_group = NULL;
13371553Srgrimes	sep->se_server = newstr(sskip(&cp));
13381553Srgrimes	if (strcmp(sep->se_server, "internal") == 0) {
13391553Srgrimes		struct biltin *bi;
13401553Srgrimes
13411553Srgrimes		for (bi = biltins; bi->bi_service; bi++)
13421553Srgrimes			if (bi->bi_socktype == sep->se_socktype &&
13431553Srgrimes			    strcmp(bi->bi_service, sep->se_service) == 0)
13441553Srgrimes				break;
13451553Srgrimes		if (bi->bi_service == 0) {
13461553Srgrimes			syslog(LOG_ERR, "internal service %s unknown",
13471553Srgrimes				sep->se_service);
13481553Srgrimes			goto more;
13491553Srgrimes		}
135019618Sjulian		sep->se_accept = 1;	/* force accept mode for built-ins */
13511553Srgrimes		sep->se_bi = bi;
13521553Srgrimes	} else
13531553Srgrimes		sep->se_bi = NULL;
135419618Sjulian	if (sep->se_maxchild < 0)	/* apply default max-children */
135519618Sjulian		if (sep->se_bi)
135619618Sjulian			sep->se_maxchild = sep->se_bi->bi_maxchild;
135719618Sjulian		else
135819618Sjulian			sep->se_maxchild = sep->se_accept ? 0 : 1;
135919618Sjulian	if (sep->se_maxchild) {
136019618Sjulian		sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
136119618Sjulian		if (sep->se_pids == NULL) {
136219618Sjulian			syslog(LOG_ERR, "Out of memory.");
136319618Sjulian			exit(EX_OSERR);
136419618Sjulian		}
136519618Sjulian	}
13661553Srgrimes	argc = 0;
13671553Srgrimes	for (arg = skip(&cp); cp; arg = skip(&cp))
136819618Sjulian		if (argc < MAXARGV) {
13691553Srgrimes			sep->se_argv[argc++] = newstr(arg);
137019618Sjulian		} else {
137119618Sjulian			syslog(LOG_ERR,
137219618Sjulian				"%s: too many arguments for service %s",
137319618Sjulian				CONFIG, sep->se_service);
137419618Sjulian			goto more;
137519618Sjulian		}
13761553Srgrimes	while (argc <= MAXARGV)
13771553Srgrimes		sep->se_argv[argc++] = NULL;
13781553Srgrimes	return (sep);
13791553Srgrimes}
13801553Srgrimes
13811553Srgrimesvoid
13821553Srgrimesfreeconfig(cp)
13831553Srgrimes	struct servtab *cp;
13841553Srgrimes{
13851553Srgrimes	int i;
13861553Srgrimes
13871553Srgrimes	if (cp->se_service)
13881553Srgrimes		free(cp->se_service);
13891553Srgrimes	if (cp->se_proto)
13901553Srgrimes		free(cp->se_proto);
13911553Srgrimes	if (cp->se_user)
13921553Srgrimes		free(cp->se_user);
139330807Sache	if (cp->se_group)
139430807Sache		free(cp->se_group);
139530792Sache#ifdef LOGIN_CAP
139630792Sache	if (cp->se_class)
139730792Sache		free(cp->se_class);
139830792Sache#endif
13991553Srgrimes	if (cp->se_server)
14001553Srgrimes		free(cp->se_server);
140119618Sjulian	if (cp->se_pids)
140219618Sjulian		free(cp->se_pids);
14031553Srgrimes	for (i = 0; i < MAXARGV; i++)
14041553Srgrimes		if (cp->se_argv[i])
14051553Srgrimes			free(cp->se_argv[i]);
14061553Srgrimes}
14071553Srgrimes
14081553Srgrimes
14091553Srgrimes/*
14101553Srgrimes * Safe skip - if skip returns null, log a syntax error in the
14111553Srgrimes * configuration file and exit.
14121553Srgrimes */
14131553Srgrimeschar *
14141553Srgrimessskip(cpp)
14151553Srgrimes	char **cpp;
14161553Srgrimes{
14171553Srgrimes	char *cp;
14181553Srgrimes
14191553Srgrimes	cp = skip(cpp);
14201553Srgrimes	if (cp == NULL) {
14211553Srgrimes		syslog(LOG_ERR, "%s: syntax error", CONFIG);
142219617Sjulian		exit(EX_DATAERR);
14231553Srgrimes	}
14241553Srgrimes	return (cp);
14251553Srgrimes}
14261553Srgrimes
14271553Srgrimeschar *
14281553Srgrimesskip(cpp)
14291553Srgrimes	char **cpp;
14301553Srgrimes{
14311553Srgrimes	char *cp = *cpp;
14321553Srgrimes	char *start;
143311933Sadam	char quote = '\0';
14341553Srgrimes
14351553Srgrimesagain:
14361553Srgrimes	while (*cp == ' ' || *cp == '\t')
14371553Srgrimes		cp++;
14381553Srgrimes	if (*cp == '\0') {
14391553Srgrimes		int c;
14401553Srgrimes
14411553Srgrimes		c = getc(fconfig);
14421553Srgrimes		(void) ungetc(c, fconfig);
14431553Srgrimes		if (c == ' ' || c == '\t')
144419617Sjulian			if ((cp = nextline(fconfig)))
14451553Srgrimes				goto again;
14461553Srgrimes		*cpp = (char *)0;
14471553Srgrimes		return ((char *)0);
14481553Srgrimes	}
144911933Sadam	if (*cp == '"' || *cp == '\'')
145011933Sadam		quote = *cp++;
14511553Srgrimes	start = cp;
145211933Sadam	if (quote)
145311933Sadam		while (*cp && *cp != quote)
145411933Sadam			cp++;
145511933Sadam	else
145611933Sadam		while (*cp && *cp != ' ' && *cp != '\t')
145711933Sadam			cp++;
14581553Srgrimes	if (*cp != '\0')
14591553Srgrimes		*cp++ = '\0';
14601553Srgrimes	*cpp = cp;
14611553Srgrimes	return (start);
14621553Srgrimes}
14631553Srgrimes
14641553Srgrimeschar *
14651553Srgrimesnextline(fd)
14661553Srgrimes	FILE *fd;
14671553Srgrimes{
14681553Srgrimes	char *cp;
14691553Srgrimes
14701553Srgrimes	if (fgets(line, sizeof (line), fd) == NULL)
14711553Srgrimes		return ((char *)0);
14721553Srgrimes	cp = strchr(line, '\n');
14731553Srgrimes	if (cp)
14741553Srgrimes		*cp = '\0';
14751553Srgrimes	return (line);
14761553Srgrimes}
14771553Srgrimes
14781553Srgrimeschar *
14791553Srgrimesnewstr(cp)
14801553Srgrimes	char *cp;
14811553Srgrimes{
148219617Sjulian	if ((cp = strdup(cp ? cp : "")))
14831553Srgrimes		return (cp);
14841553Srgrimes	syslog(LOG_ERR, "strdup: %m");
148519617Sjulian	exit(EX_OSERR);
14861553Srgrimes}
14871553Srgrimes
148813142Speter#ifdef OLD_SETPROCTITLE
14891553Srgrimesvoid
149013142Speterinetd_setproctitle(a, s)
14911553Srgrimes	char *a;
14921553Srgrimes	int s;
14931553Srgrimes{
14941553Srgrimes	int size;
14951553Srgrimes	char *cp;
14961553Srgrimes	struct sockaddr_in sin;
14971553Srgrimes	char buf[80];
14981553Srgrimes
14991553Srgrimes	cp = Argv[0];
15001553Srgrimes	size = sizeof(sin);
15011553Srgrimes	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
15028857Srgrimes		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
15031553Srgrimes	else
15048857Srgrimes		(void) sprintf(buf, "-%s", a);
15051553Srgrimes	strncpy(cp, buf, LastArg - cp);
15061553Srgrimes	cp += strlen(cp);
15071553Srgrimes	while (cp < LastArg)
15081553Srgrimes		*cp++ = ' ';
15091553Srgrimes}
151013142Speter#else
151113142Spetervoid
151213142Speterinetd_setproctitle(a, s)
151313142Speter	char *a;
151413142Speter	int s;
151513142Speter{
151613142Speter	int size;
151713142Speter	struct sockaddr_in sin;
151813142Speter	char buf[80];
15191553Srgrimes
152013142Speter	size = sizeof(sin);
152113142Speter	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
152213142Speter		(void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr));
152313142Speter	else
152413142Speter		(void) sprintf(buf, "%s", a);
152513142Speter	setproctitle("%s", buf);
152613142Speter}
152713142Speter#endif
152813142Speter
152913142Speter
15301553Srgrimes/*
15311553Srgrimes * Internet services provided internally by inetd:
15321553Srgrimes */
15331553Srgrimes#define	BUFSIZE	8192
15341553Srgrimes
153540910Sphk#define IDENT_RESPONSE ":ERROR:HIDDEN-USER\r\n"
153640910Sphk
15371553Srgrimes/* ARGSUSED */
15381553Srgrimesvoid
153940910Sphkident_stream(s, sep)		/* Ident service */
154040910Sphk	int s;
154140910Sphk	struct servtab *sep;
154240910Sphk{
154340910Sphk	char buffer[BUFSIZE];
154440910Sphk	int i, j;
154540910Sphk
154640910Sphk	inetd_setproctitle(sep->se_service, s);
154740910Sphk	j = 0;
154840910Sphk	while ((i = read(s, buffer + j, sizeof(buffer) - j)) > 0) {
154940910Sphk		j += i;
155040910Sphk		buffer[j] = '\0';
155140910Sphk		if (strchr(buffer, '\n'))
155240910Sphk			break;
155340910Sphk		if (strchr(buffer, '\r'))
155440910Sphk			break;
155540910Sphk	}
155640910Sphk	while (j > 0 && (buffer[j-1] == '\n' || buffer[j-1] == '\r'))
155740910Sphk		j--;
155840910Sphk	write(s, buffer, j);
155940910Sphk	write(s, IDENT_RESPONSE, strlen(IDENT_RESPONSE));
156040910Sphk	exit(0);
156140910Sphk}
156240910Sphk/* ARGSUSED */
156340910Sphkvoid
15641553Srgrimesecho_stream(s, sep)		/* Echo service -- echo data back */
15651553Srgrimes	int s;
15661553Srgrimes	struct servtab *sep;
15671553Srgrimes{
15681553Srgrimes	char buffer[BUFSIZE];
15691553Srgrimes	int i;
15701553Srgrimes
157113142Speter	inetd_setproctitle(sep->se_service, s);
15721553Srgrimes	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
15731553Srgrimes	    write(s, buffer, i) > 0)
15741553Srgrimes		;
15751553Srgrimes	exit(0);
15761553Srgrimes}
15771553Srgrimes
15785182Swollmanint check_loop(sin, sep)
15795182Swollman	struct sockaddr_in *sin;
15805182Swollman	struct servtab *sep;
15815182Swollman{
15825182Swollman	struct servtab *se2;
15835182Swollman
15845182Swollman	for (se2 = servtab; se2; se2 = se2->se_next) {
15855182Swollman		if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
15865182Swollman			continue;
15875182Swollman
15885182Swollman		if (sin->sin_port == se2->se_ctrladdr.sin_port) {
15895182Swollman			syslog(LOG_WARNING,
15905182Swollman			       "%s/%s:%s/%s loop request REFUSED from %s",
15918857Srgrimes			       sep->se_service, sep->se_proto,
15925182Swollman			       se2->se_service, se2->se_proto,
15935182Swollman			       inet_ntoa(sin->sin_addr));
15945182Swollman			return 1;
15955182Swollman		}
15965182Swollman	}
15975182Swollman	return 0;
15985182Swollman}
15995182Swollman
16001553Srgrimes/* ARGSUSED */
16011553Srgrimesvoid
16021553Srgrimesecho_dg(s, sep)			/* Echo service -- echo data back */
16031553Srgrimes	int s;
16041553Srgrimes	struct servtab *sep;
16051553Srgrimes{
16061553Srgrimes	char buffer[BUFSIZE];
16071553Srgrimes	int i, size;
16085182Swollman	struct sockaddr_in sin;
16091553Srgrimes
16105182Swollman	size = sizeof(sin);
16118857Srgrimes	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
16125182Swollman			  (struct sockaddr *)&sin, &size)) < 0)
16131553Srgrimes		return;
16145182Swollman
16155182Swollman	if (check_loop(&sin, sep))
16165182Swollman		return;
16175182Swollman
16185182Swollman	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin,
16195182Swollman		      sizeof(sin));
16201553Srgrimes}
16211553Srgrimes
16221553Srgrimes/* ARGSUSED */
16231553Srgrimesvoid
16241553Srgrimesdiscard_stream(s, sep)		/* Discard service -- ignore data */
16251553Srgrimes	int s;
16261553Srgrimes	struct servtab *sep;
16271553Srgrimes{
16281553Srgrimes	int ret;
16291553Srgrimes	char buffer[BUFSIZE];
16301553Srgrimes
163113142Speter	inetd_setproctitle(sep->se_service, s);
16321553Srgrimes	while (1) {
16331553Srgrimes		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
16341553Srgrimes			;
16351553Srgrimes		if (ret == 0 || errno != EINTR)
16361553Srgrimes			break;
16371553Srgrimes	}
16381553Srgrimes	exit(0);
16391553Srgrimes}
16401553Srgrimes
16411553Srgrimes/* ARGSUSED */
16421553Srgrimesvoid
16431553Srgrimesdiscard_dg(s, sep)		/* Discard service -- ignore data */
16441553Srgrimes	int s;
16451553Srgrimes	struct servtab *sep;
16461553Srgrimes{
16471553Srgrimes	char buffer[BUFSIZE];
16481553Srgrimes
16491553Srgrimes	(void) read(s, buffer, sizeof(buffer));
16501553Srgrimes}
16511553Srgrimes
16521553Srgrimes#include <ctype.h>
16531553Srgrimes#define LINESIZ 72
16541553Srgrimeschar ring[128];
16551553Srgrimeschar *endring;
16561553Srgrimes
16571553Srgrimesvoid
16581553Srgrimesinitring()
16591553Srgrimes{
16601553Srgrimes	int i;
16611553Srgrimes
16621553Srgrimes	endring = ring;
16631553Srgrimes
16641553Srgrimes	for (i = 0; i <= 128; ++i)
16651553Srgrimes		if (isprint(i))
16661553Srgrimes			*endring++ = i;
16671553Srgrimes}
16681553Srgrimes
16691553Srgrimes/* ARGSUSED */
16701553Srgrimesvoid
16711553Srgrimeschargen_stream(s, sep)		/* Character generator */
16721553Srgrimes	int s;
16731553Srgrimes	struct servtab *sep;
16741553Srgrimes{
16751553Srgrimes	int len;
16761553Srgrimes	char *rs, text[LINESIZ+2];
16771553Srgrimes
167813142Speter	inetd_setproctitle(sep->se_service, s);
16791553Srgrimes
16801553Srgrimes	if (!endring) {
16811553Srgrimes		initring();
16821553Srgrimes		rs = ring;
16831553Srgrimes	}
16841553Srgrimes
16851553Srgrimes	text[LINESIZ] = '\r';
16861553Srgrimes	text[LINESIZ + 1] = '\n';
16871553Srgrimes	for (rs = ring;;) {
16881553Srgrimes		if ((len = endring - rs) >= LINESIZ)
16891553Srgrimes			memmove(text, rs, LINESIZ);
16901553Srgrimes		else {
16911553Srgrimes			memmove(text, rs, len);
16921553Srgrimes			memmove(text + len, ring, LINESIZ - len);
16931553Srgrimes		}
16941553Srgrimes		if (++rs == endring)
16951553Srgrimes			rs = ring;
16961553Srgrimes		if (write(s, text, sizeof(text)) != sizeof(text))
16971553Srgrimes			break;
16981553Srgrimes	}
16991553Srgrimes	exit(0);
17001553Srgrimes}
17011553Srgrimes
17021553Srgrimes/* ARGSUSED */
17031553Srgrimesvoid
17041553Srgrimeschargen_dg(s, sep)		/* Character generator */
17051553Srgrimes	int s;
17061553Srgrimes	struct servtab *sep;
17071553Srgrimes{
17085182Swollman	struct sockaddr_in sin;
17091553Srgrimes	static char *rs;
17101553Srgrimes	int len, size;
17111553Srgrimes	char text[LINESIZ+2];
17121553Srgrimes
17131553Srgrimes	if (endring == 0) {
17141553Srgrimes		initring();
17151553Srgrimes		rs = ring;
17161553Srgrimes	}
17171553Srgrimes
17185182Swollman	size = sizeof(sin);
17198857Srgrimes	if (recvfrom(s, text, sizeof(text), 0,
17205182Swollman		     (struct sockaddr *)&sin, &size) < 0)
17211553Srgrimes		return;
17221553Srgrimes
17235182Swollman	if (check_loop(&sin, sep))
17245182Swollman		return;
17255182Swollman
17261553Srgrimes	if ((len = endring - rs) >= LINESIZ)
17271553Srgrimes		memmove(text, rs, LINESIZ);
17281553Srgrimes	else {
17291553Srgrimes		memmove(text, rs, len);
17301553Srgrimes		memmove(text + len, ring, LINESIZ - len);
17311553Srgrimes	}
17321553Srgrimes	if (++rs == endring)
17331553Srgrimes		rs = ring;
17341553Srgrimes	text[LINESIZ] = '\r';
17351553Srgrimes	text[LINESIZ + 1] = '\n';
17368857Srgrimes	(void) sendto(s, text, sizeof(text), 0,
17375182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
17381553Srgrimes}
17391553Srgrimes
17401553Srgrimes/*
17411553Srgrimes * Return a machine readable date and time, in the form of the
17421553Srgrimes * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
17431553Srgrimes * returns the number of seconds since midnight, Jan 1, 1970,
17441553Srgrimes * we must add 2208988800 seconds to this figure to make up for
17451553Srgrimes * some seventy years Bell Labs was asleep.
17461553Srgrimes */
17471553Srgrimes
17481553Srgrimeslong
17491553Srgrimesmachtime()
17501553Srgrimes{
17511553Srgrimes	struct timeval tv;
17521553Srgrimes
175337856Sache	if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
17541553Srgrimes		if (debug)
175529602Scharnier			warnx("unable to get time of day");
17561553Srgrimes		return (0L);
17571553Srgrimes	}
17581553Srgrimes#define	OFFSET ((u_long)25567 * 24*60*60)
17591553Srgrimes	return (htonl((long)(tv.tv_sec + OFFSET)));
17601553Srgrimes#undef OFFSET
17611553Srgrimes}
17621553Srgrimes
17631553Srgrimes/* ARGSUSED */
17641553Srgrimesvoid
17651553Srgrimesmachtime_stream(s, sep)
17661553Srgrimes	int s;
17671553Srgrimes	struct servtab *sep;
17681553Srgrimes{
17691553Srgrimes	long result;
17701553Srgrimes
17711553Srgrimes	result = machtime();
17721553Srgrimes	(void) write(s, (char *) &result, sizeof(result));
17731553Srgrimes}
17741553Srgrimes
17751553Srgrimes/* ARGSUSED */
17761553Srgrimesvoid
17771553Srgrimesmachtime_dg(s, sep)
17781553Srgrimes	int s;
17791553Srgrimes	struct servtab *sep;
17801553Srgrimes{
17811553Srgrimes	long result;
17825182Swollman	struct sockaddr_in sin;
17831553Srgrimes	int size;
17841553Srgrimes
17855182Swollman	size = sizeof(sin);
17868857Srgrimes	if (recvfrom(s, (char *)&result, sizeof(result), 0,
17875182Swollman		     (struct sockaddr *)&sin, &size) < 0)
17881553Srgrimes		return;
17895182Swollman
17905182Swollman	if (check_loop(&sin, sep))
17915182Swollman		return;
17925182Swollman
17931553Srgrimes	result = machtime();
17948857Srgrimes	(void) sendto(s, (char *) &result, sizeof(result), 0,
17955182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
17961553Srgrimes}
17971553Srgrimes
17981553Srgrimes/* ARGSUSED */
17991553Srgrimesvoid
18001553Srgrimesdaytime_stream(s, sep)		/* Return human-readable time of day */
18011553Srgrimes	int s;
18021553Srgrimes	struct servtab *sep;
18031553Srgrimes{
18041553Srgrimes	char buffer[256];
18051553Srgrimes	time_t clock;
18061553Srgrimes
18071553Srgrimes	clock = time((time_t *) 0);
18081553Srgrimes
18091553Srgrimes	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
18101553Srgrimes	(void) write(s, buffer, strlen(buffer));
18111553Srgrimes}
18121553Srgrimes
18131553Srgrimes/* ARGSUSED */
18141553Srgrimesvoid
18151553Srgrimesdaytime_dg(s, sep)		/* Return human-readable time of day */
18161553Srgrimes	int s;
18171553Srgrimes	struct servtab *sep;
18181553Srgrimes{
18191553Srgrimes	char buffer[256];
18201553Srgrimes	time_t clock;
18215182Swollman	struct sockaddr_in sin;
18221553Srgrimes	int size;
18231553Srgrimes
18241553Srgrimes	clock = time((time_t *) 0);
18251553Srgrimes
18265182Swollman	size = sizeof(sin);
18278857Srgrimes	if (recvfrom(s, buffer, sizeof(buffer), 0,
18285182Swollman		     (struct sockaddr *)&sin, &size) < 0)
18291553Srgrimes		return;
18305182Swollman
18315182Swollman	if (check_loop(&sin, sep))
18325182Swollman		return;
18335182Swollman
18341553Srgrimes	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
18358857Srgrimes	(void) sendto(s, buffer, strlen(buffer), 0,
18365182Swollman		      (struct sockaddr *)&sin, sizeof(sin));
18371553Srgrimes}
18381553Srgrimes
18391553Srgrimes/*
18401553Srgrimes * print_service:
18411553Srgrimes *	Dump relevant information to stderr
18421553Srgrimes */
18431553Srgrimesvoid
18441553Srgrimesprint_service(action, sep)
18451553Srgrimes	char *action;
18461553Srgrimes	struct servtab *sep;
18471553Srgrimes{
184819617Sjulian	fprintf(stderr,
184930792Sache#ifdef LOGIN_CAP
185038380Sjb	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%p server=%s\n",
185130792Sache#else
185238380Sjb	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%p server=%s\n",
185330792Sache#endif
185419617Sjulian	    action, sep->se_service, sep->se_proto,
185530807Sache	    sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group,
185630792Sache#ifdef LOGIN_CAP
185730792Sache	    sep->se_class,
185830792Sache#endif
185938417Sjb	    (void *) sep->se_bi, sep->se_server);
18601553Srgrimes}
18611553Srgrimes
18621553Srgrimes/*
18631553Srgrimes *  Based on TCPMUX.C by Mark K. Lottor November 1988
18641553Srgrimes *  sri-nic::ps:<mkl>tcpmux.c
18651553Srgrimes */
18661553Srgrimes
18671553Srgrimes
18681553Srgrimesstatic int		/* # of characters upto \r,\n or \0 */
18691553Srgrimesgetline(fd, buf, len)
18701553Srgrimes	int fd;
18711553Srgrimes	char *buf;
18721553Srgrimes	int len;
18731553Srgrimes{
18741553Srgrimes	int count = 0, n;
187535848Sguido	struct sigaction sa;
18761553Srgrimes
187735948Sbde	sa.sa_flags = 0;
187835948Sbde	sigemptyset(&sa.sa_mask);
187935848Sguido	sa.sa_handler = SIG_DFL;
188035848Sguido	sigaction(SIGALRM, &sa, (struct sigaction *)0);
18811553Srgrimes	do {
188235829Sguido		alarm(10);
18831553Srgrimes		n = read(fd, buf, len-count);
188435829Sguido		alarm(0);
18851553Srgrimes		if (n == 0)
188642122Sdes			return (count);
188742122Sdes		if (n < 0)
188842122Sdes			return (-1);
18891553Srgrimes		while (--n >= 0) {
189042122Sdes			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
189142122Sdes				return (count);
18921553Srgrimes			count++;
18931553Srgrimes			buf++;
18941553Srgrimes		}
189542122Sdes	} while (count < len);
18961553Srgrimes	return (count);
18971553Srgrimes}
18981553Srgrimes
18991553Srgrimes#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
19001553Srgrimes
19011553Srgrimes#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
19021553Srgrimes
19031553Srgrimesstruct servtab *
19041553Srgrimestcpmux(s)
19051553Srgrimes	int s;
19061553Srgrimes{
19071553Srgrimes	struct servtab *sep;
19081553Srgrimes	char service[MAX_SERV_LEN+1];
19091553Srgrimes	int len;
19101553Srgrimes
19111553Srgrimes	/* Get requested service name */
19121553Srgrimes	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
19131553Srgrimes		strwrite(s, "-Error reading service name\r\n");
19141553Srgrimes		return (NULL);
19151553Srgrimes	}
19161553Srgrimes	service[len] = '\0';
19171553Srgrimes
19181553Srgrimes	if (debug)
191929602Scharnier		warnx("tcpmux: someone wants %s", service);
19201553Srgrimes
19211553Srgrimes	/*
19221553Srgrimes	 * Help is a required command, and lists available services,
19231553Srgrimes	 * one per line.
19241553Srgrimes	 */
19251553Srgrimes	if (!strcasecmp(service, "help")) {
19261553Srgrimes		for (sep = servtab; sep; sep = sep->se_next) {
19271553Srgrimes			if (!ISMUX(sep))
19281553Srgrimes				continue;
19291553Srgrimes			(void)write(s,sep->se_service,strlen(sep->se_service));
19301553Srgrimes			strwrite(s, "\r\n");
19311553Srgrimes		}
19321553Srgrimes		return (NULL);
19331553Srgrimes	}
19341553Srgrimes
19351553Srgrimes	/* Try matching a service in inetd.conf with the request */
19361553Srgrimes	for (sep = servtab; sep; sep = sep->se_next) {
19371553Srgrimes		if (!ISMUX(sep))
19381553Srgrimes			continue;
19391553Srgrimes		if (!strcasecmp(service, sep->se_service)) {
19401553Srgrimes			if (ISMUXPLUS(sep)) {
19411553Srgrimes				strwrite(s, "+Go\r\n");
19421553Srgrimes			}
19431553Srgrimes			return (sep);
19441553Srgrimes		}
19451553Srgrimes	}
19461553Srgrimes	strwrite(s, "-Service not available\r\n");
19471553Srgrimes	return (NULL);
19481553Srgrimes}
194930847Sdima
195030847Sdima#define CPMHSIZE	256
195130847Sdima#define CPMHMASK	(CPMHSIZE-1)
195230847Sdima#define CHTGRAN		10
195330847Sdima#define CHTSIZE		6
195430847Sdima
195530847Sdimatypedef struct CTime {
195630847Sdima	unsigned long 	ct_Ticks;
195730847Sdima	int		ct_Count;
195830847Sdima} CTime;
195930847Sdima
196030847Sdimatypedef struct CHash {
196130847Sdima	struct in_addr	ch_Addr;
196230847Sdima	time_t		ch_LTime;
196330847Sdima	char		*ch_Service;
196430847Sdima	CTime		ch_Times[CHTSIZE];
196530847Sdima} CHash;
196630847Sdima
196730847SdimaCHash	CHashAry[CPMHSIZE];
196830847Sdima
196930847Sdimaint
197030847Sdimacpmip(sep, ctrl)
197130847Sdima	struct servtab *sep;
197230847Sdima	int ctrl;
197330847Sdima{
197430847Sdima	struct sockaddr_in rsin;
197530847Sdima	int rsinLen = sizeof(rsin);
197630847Sdima	int r = 0;
197730847Sdima
197830847Sdima	/*
197930847Sdima	 * If getpeername() fails, just let it through (if logging is
198030847Sdima	 * enabled the condition is caught elsewhere)
198130847Sdima	 */
198230847Sdima
198330847Sdima	if (sep->se_maxcpm > 0 &&
198430847Sdima	    getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 0 ) {
198530847Sdima		time_t t = time(NULL);
198630847Sdima		int hv = 0xABC3D20F;
198730847Sdima		int i;
198830847Sdima		int cnt = 0;
198930847Sdima		CHash *chBest = NULL;
199030847Sdima		unsigned int ticks = t / CHTGRAN;
199130847Sdima
199230847Sdima		{
199330847Sdima			char *p;
199430847Sdima			int i;
199530847Sdima
199630847Sdima			for (i = 0, p = (char *)&rsin.sin_addr;
199730847Sdima			    i < sizeof(rsin.sin_addr);
199830847Sdima			    ++i, ++p) {
199930847Sdima				hv = (hv << 5) ^ (hv >> 23) ^ *p;
200030847Sdima			}
200130847Sdima			hv = (hv ^ (hv >> 16));
200230847Sdima		}
200330847Sdima		for (i = 0; i < 5; ++i) {
200430847Sdima			CHash *ch = &CHashAry[(hv + i) & CPMHMASK];
200530847Sdima
200630847Sdima			if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr &&
200730847Sdima			    ch->ch_Service && strcmp(sep->se_service,
200830847Sdima			    ch->ch_Service) == 0) {
200930847Sdima				chBest = ch;
201030847Sdima				break;
201130847Sdima			}
201230847Sdima			if (chBest == NULL || ch->ch_LTime == 0 ||
201330847Sdima			    ch->ch_LTime < chBest->ch_LTime) {
201430847Sdima				chBest = ch;
201530847Sdima			}
201630847Sdima		}
201730847Sdima		if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr ||
201830847Sdima		    chBest->ch_Service == NULL ||
201930847Sdima		    strcmp(sep->se_service, chBest->ch_Service) != 0) {
202030847Sdima			chBest->ch_Addr = rsin.sin_addr;
202130847Sdima			if (chBest->ch_Service)
202230847Sdima				free(chBest->ch_Service);
202330847Sdima			chBest->ch_Service = strdup(sep->se_service);
202430847Sdima			bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
202530847Sdima		}
202630847Sdima		chBest->ch_LTime = t;
202730847Sdima		{
202830847Sdima			CTime *ct = &chBest->ch_Times[ticks % CHTSIZE];
202930847Sdima			if (ct->ct_Ticks != ticks) {
203030847Sdima				ct->ct_Ticks = ticks;
203130847Sdima				ct->ct_Count = 0;
203230847Sdima			}
203330847Sdima			++ct->ct_Count;
203430847Sdima		}
203530847Sdima		for (i = 0; i < CHTSIZE; ++i) {
203630847Sdima			CTime *ct = &chBest->ch_Times[i];
203730847Sdima			if (ct->ct_Ticks <= ticks &&
203830847Sdima			    ct->ct_Ticks >= ticks - CHTSIZE) {
203930847Sdima				cnt += ct->ct_Count;
204030847Sdima			}
204130847Sdima		}
204230847Sdima		if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) {
204330847Sdima			r = -1;
204430847Sdima			syslog(LOG_ERR,
204533794Spst			    "%s from %s exceeded counts/min (limit %d/min)",
204633794Spst			    sep->se_service, inet_ntoa(rsin.sin_addr),
204733794Spst			    sep->se_maxcpm);
204830847Sdima		}
204930847Sdima	}
205030847Sdima	return(r);
205130847Sdima}
2052