inetd.c revision 1553
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
351553Srgrimesstatic 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
411553Srgrimesstatic char sccsid[] = "@(#)inetd.c	8.4 (Berkeley) 4/13/94";
421553Srgrimes#endif /* not lint */
431553Srgrimes
441553Srgrimes/*
451553Srgrimes * Inetd - Internet super-server
461553Srgrimes *
471553Srgrimes * This program invokes all internet services as needed.  Connection-oriented
481553Srgrimes * services are invoked each time a connection is made, by creating a process.
491553Srgrimes * This process is passed the connection as file descriptor 0 and is expected
501553Srgrimes * to do a getpeername to find out the source host and port.
511553Srgrimes *
521553Srgrimes * Datagram oriented services are invoked when a datagram
531553Srgrimes * arrives; a process is created and passed a pending message
541553Srgrimes * on file descriptor 0.  Datagram servers may either connect
551553Srgrimes * to their peer, freeing up the original socket for inetd
561553Srgrimes * to receive further messages on, or ``take over the socket'',
571553Srgrimes * processing all arriving datagrams and, eventually, timing
581553Srgrimes * out.	 The first type of server is said to be ``multi-threaded'';
591553Srgrimes * the second type of server ``single-threaded''.
601553Srgrimes *
611553Srgrimes * Inetd uses a configuration file which is read at startup
621553Srgrimes * and, possibly, at some later time in response to a hangup signal.
631553Srgrimes * The configuration file is ``free format'' with fields given in the
641553Srgrimes * order shown below.  Continuation lines for an entry must being with
651553Srgrimes * a space or tab.  All fields must be present in each entry.
661553Srgrimes *
671553Srgrimes *	service name			must be in /etc/services or must
681553Srgrimes *					name a tcpmux service
691553Srgrimes *	socket type			stream/dgram/raw/rdm/seqpacket
701553Srgrimes *	protocol			must be in /etc/protocols
711553Srgrimes *	wait/nowait			single-threaded/multi-threaded
721553Srgrimes *	user				user to run daemon as
731553Srgrimes *	server program			full path name
741553Srgrimes *	server program arguments	maximum of MAXARGS (20)
751553Srgrimes *
761553Srgrimes * TCP services without official port numbers are handled with the
771553Srgrimes * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
781553Srgrimes * requests. When a connection is made from a foreign host, the service
791553Srgrimes * requested is passed to tcpmux, which looks it up in the servtab list
801553Srgrimes * and returns the proper entry for the service. Tcpmux returns a
811553Srgrimes * negative reply if the service doesn't exist, otherwise the invoked
821553Srgrimes * server is expected to return the positive reply if the service type in
831553Srgrimes * inetd.conf file has the prefix "tcpmux/". If the service type has the
841553Srgrimes * prefix "tcpmux/+", tcpmux will return the positive reply for the
851553Srgrimes * process; this is for compatibility with older server code, and also
861553Srgrimes * allows you to invoke programs that use stdin/stdout without putting any
871553Srgrimes * special server code in them. Services that use tcpmux are "nowait"
881553Srgrimes * because they do not have a well-known port and hence cannot listen
891553Srgrimes * for new requests.
901553Srgrimes *
911553Srgrimes * Comment lines are indicated by a `#' in column 1.
921553Srgrimes */
931553Srgrimes#include <sys/param.h>
941553Srgrimes#include <sys/stat.h>
951553Srgrimes#include <sys/ioctl.h>
961553Srgrimes#include <sys/socket.h>
971553Srgrimes#include <sys/wait.h>
981553Srgrimes#include <sys/time.h>
991553Srgrimes#include <sys/resource.h>
1001553Srgrimes
1011553Srgrimes#include <netinet/in.h>
1021553Srgrimes#include <arpa/inet.h>
1031553Srgrimes
1041553Srgrimes#include <errno.h>
1051553Srgrimes#include <fcntl.h>
1061553Srgrimes#include <netdb.h>
1071553Srgrimes#include <pwd.h>
1081553Srgrimes#include <signal.h>
1091553Srgrimes#include <stdio.h>
1101553Srgrimes#include <stdlib.h>
1111553Srgrimes#include <string.h>
1121553Srgrimes#include <syslog.h>
1131553Srgrimes#include <unistd.h>
1141553Srgrimes
1151553Srgrimes#include "pathnames.h"
1161553Srgrimes
1171553Srgrimes#define	TOOMANY		40		/* don't start more than TOOMANY */
1181553Srgrimes#define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
1191553Srgrimes#define	RETRYTIME	(60*10)		/* retry after bind or server fail */
1201553Srgrimes
1211553Srgrimes#define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
1221553Srgrimes
1231553Srgrimes
1241553Srgrimesint	debug = 0;
1251553Srgrimesint	nsock, maxsock;
1261553Srgrimesfd_set	allsock;
1271553Srgrimesint	options;
1281553Srgrimesint	timingout;
1291553Srgrimesint	toomany = TOOMANY;
1301553Srgrimesstruct	servent *sp;
1311553Srgrimes
1321553Srgrimesstruct	servtab {
1331553Srgrimes	char	*se_service;		/* name of service */
1341553Srgrimes	int	se_socktype;		/* type of socket to use */
1351553Srgrimes	char	*se_proto;		/* protocol used */
1361553Srgrimes	short	se_wait;		/* single threaded server */
1371553Srgrimes	short	se_checked;		/* looked at during merge */
1381553Srgrimes	char	*se_user;		/* user name to run as */
1391553Srgrimes	struct	biltin *se_bi;		/* if built-in, description */
1401553Srgrimes	char	*se_server;		/* server program */
1411553Srgrimes#define	MAXARGV 20
1421553Srgrimes	char	*se_argv[MAXARGV+1];	/* program arguments */
1431553Srgrimes	int	se_fd;			/* open descriptor */
1441553Srgrimes	int	se_type;		/* type */
1451553Srgrimes	struct	sockaddr_in se_ctrladdr;/* bound address */
1461553Srgrimes	int	se_count;		/* number started since se_time */
1471553Srgrimes	struct	timeval se_time;	/* start of se_count */
1481553Srgrimes	struct	servtab *se_next;
1491553Srgrimes} *servtab;
1501553Srgrimes
1511553Srgrimes#define NORM_TYPE	0
1521553Srgrimes#define MUX_TYPE	1
1531553Srgrimes#define MUXPLUS_TYPE	2
1541553Srgrimes#define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
1551553Srgrimes			 ((sep)->se_type == MUXPLUS_TYPE))
1561553Srgrimes#define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
1571553Srgrimes
1581553Srgrimes
1591553Srgrimesvoid		chargen_dg __P((int, struct servtab *));
1601553Srgrimesvoid		chargen_stream __P((int, struct servtab *));
1611553Srgrimesvoid		close_sep __P((struct servtab *));
1621553Srgrimesvoid		config __P((int));
1631553Srgrimesvoid		daytime_dg __P((int, struct servtab *));
1641553Srgrimesvoid		daytime_stream __P((int, struct servtab *));
1651553Srgrimesvoid		discard_dg __P((int, struct servtab *));
1661553Srgrimesvoid		discard_stream __P((int, struct servtab *));
1671553Srgrimesvoid		echo_dg __P((int, struct servtab *));
1681553Srgrimesvoid		echo_stream __P((int, struct servtab *));
1691553Srgrimesvoid		endconfig __P((void));
1701553Srgrimesstruct servtab *enter __P((struct servtab *));
1711553Srgrimesvoid		freeconfig __P((struct servtab *));
1721553Srgrimesstruct servtab *getconfigent __P((void));
1731553Srgrimesvoid		machtime_dg __P((int, struct servtab *));
1741553Srgrimesvoid		machtime_stream __P((int, struct servtab *));
1751553Srgrimeschar	       *newstr __P((char *));
1761553Srgrimeschar	       *nextline __P((FILE *));
1771553Srgrimesvoid		print_service __P((char *, struct servtab *));
1781553Srgrimesvoid		reapchild __P((int));
1791553Srgrimesvoid		retry __P((int));
1801553Srgrimesint		setconfig __P((void));
1811553Srgrimesvoid		setup __P((struct servtab *));
1821553Srgrimeschar	       *sskip __P((char **));
1831553Srgrimeschar	       *skip __P((char **));
1841553Srgrimesstruct servtab *tcpmux __P((int));
1851553Srgrimes
1861553Srgrimesstruct biltin {
1871553Srgrimes	char	*bi_service;		/* internally provided service name */
1881553Srgrimes	int	bi_socktype;		/* type of socket supported */
1891553Srgrimes	short	bi_fork;		/* 1 if should fork before call */
1901553Srgrimes	short	bi_wait;		/* 1 if should wait for child */
1911553Srgrimes	void	(*bi_fn)();		/* function which performs it */
1921553Srgrimes} biltins[] = {
1931553Srgrimes	/* Echo received data */
1941553Srgrimes	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream },
1951553Srgrimes	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg },
1961553Srgrimes
1971553Srgrimes	/* Internet /dev/null */
1981553Srgrimes	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream },
1991553Srgrimes	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg },
2001553Srgrimes
2011553Srgrimes	/* Return 32 bit time since 1970 */
2021553Srgrimes	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream },
2031553Srgrimes	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg },
2041553Srgrimes
2051553Srgrimes	/* Return human-readable time */
2061553Srgrimes	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream },
2071553Srgrimes	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg },
2081553Srgrimes
2091553Srgrimes	/* Familiar character generator */
2101553Srgrimes	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream },
2111553Srgrimes	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg },
2121553Srgrimes
2131553Srgrimes	{ "tcpmux",	SOCK_STREAM,	1, 0,	(void (*)())tcpmux },
2141553Srgrimes
2151553Srgrimes	{ NULL }
2161553Srgrimes};
2171553Srgrimes
2181553Srgrimes#define NUMINT	(sizeof(intab) / sizeof(struct inent))
2191553Srgrimeschar	*CONFIG = _PATH_INETDCONF;
2201553Srgrimeschar	**Argv;
2211553Srgrimeschar 	*LastArg;
2221553Srgrimes
2231553Srgrimesint
2241553Srgrimesmain(argc, argv, envp)
2251553Srgrimes	int argc;
2261553Srgrimes	char *argv[], *envp[];
2271553Srgrimes{
2281553Srgrimes	struct servtab *sep;
2291553Srgrimes	struct passwd *pwd;
2301553Srgrimes	struct sigvec sv;
2311553Srgrimes	int tmpint, ch, dofork;
2321553Srgrimes	pid_t pid;
2331553Srgrimes	char buf[50];
2341553Srgrimes
2351553Srgrimes	Argv = argv;
2361553Srgrimes	if (envp == 0 || *envp == 0)
2371553Srgrimes		envp = argv;
2381553Srgrimes	while (*envp)
2391553Srgrimes		envp++;
2401553Srgrimes	LastArg = envp[-1] + strlen(envp[-1]);
2411553Srgrimes
2421553Srgrimes	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
2431553Srgrimes
2441553Srgrimes	while ((ch = getopt(argc, argv, "dR:")) != EOF)
2451553Srgrimes		switch(ch) {
2461553Srgrimes		case 'd':
2471553Srgrimes			debug = 1;
2481553Srgrimes			options |= SO_DEBUG;
2491553Srgrimes			break;
2501553Srgrimes		case 'R': {	/* invocation rate */
2511553Srgrimes			char *p;
2521553Srgrimes
2531553Srgrimes			tmpint = strtol(optarg, &p, 0);
2541553Srgrimes			if (tmpint < 1 || *p)
2551553Srgrimes				syslog(LOG_ERR,
2561553Srgrimes			         "-R %s: bad value for service invocation rate",
2571553Srgrimes					optarg);
2581553Srgrimes			else
2591553Srgrimes				toomany = tmpint;
2601553Srgrimes			break;
2611553Srgrimes		}
2621553Srgrimes		case '?':
2631553Srgrimes		default:
2641553Srgrimes			syslog(LOG_ERR,
2651553Srgrimes				"usage: inetd [-d] [-R rate] [conf-file]");
2661553Srgrimes			exit(1);
2671553Srgrimes		}
2681553Srgrimes	argc -= optind;
2691553Srgrimes	argv += optind;
2701553Srgrimes
2711553Srgrimes	if (argc > 0)
2721553Srgrimes		CONFIG = argv[0];
2731553Srgrimes	if (debug == 0) {
2741553Srgrimes		daemon(0, 0);
2751553Srgrimes	}
2761553Srgrimes	memset(&sv, 0, sizeof(sv));
2771553Srgrimes	sv.sv_mask = SIGBLOCK;
2781553Srgrimes	sv.sv_handler = retry;
2791553Srgrimes	sigvec(SIGALRM, &sv, (struct sigvec *)0);
2801553Srgrimes	config(SIGHUP);
2811553Srgrimes	sv.sv_handler = config;
2821553Srgrimes	sigvec(SIGHUP, &sv, (struct sigvec *)0);
2831553Srgrimes	sv.sv_handler = reapchild;
2841553Srgrimes	sigvec(SIGCHLD, &sv, (struct sigvec *)0);
2851553Srgrimes
2861553Srgrimes	{
2871553Srgrimes		/* space for daemons to overwrite environment for ps */
2881553Srgrimes#define	DUMMYSIZE	100
2891553Srgrimes		char dummy[DUMMYSIZE];
2901553Srgrimes
2911553Srgrimes		(void)memset(dummy, 'x', sizeof(DUMMYSIZE) - 1);
2921553Srgrimes		dummy[DUMMYSIZE - 1] = '\0';
2931553Srgrimes		(void)setenv("inetd_dummy", dummy, 1);
2941553Srgrimes	}
2951553Srgrimes
2961553Srgrimes	for (;;) {
2971553Srgrimes	    int n, ctrl;
2981553Srgrimes	    fd_set readable;
2991553Srgrimes
3001553Srgrimes	    if (nsock == 0) {
3011553Srgrimes		(void) sigblock(SIGBLOCK);
3021553Srgrimes		while (nsock == 0)
3031553Srgrimes		    sigpause(0L);
3041553Srgrimes		(void) sigsetmask(0L);
3051553Srgrimes	    }
3061553Srgrimes	    readable = allsock;
3071553Srgrimes	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
3081553Srgrimes		(fd_set *)0, (struct timeval *)0)) <= 0) {
3091553Srgrimes		    if (n < 0 && errno != EINTR)
3101553Srgrimes			syslog(LOG_WARNING, "select: %m");
3111553Srgrimes		    sleep(1);
3121553Srgrimes		    continue;
3131553Srgrimes	    }
3141553Srgrimes	    for (sep = servtab; n && sep; sep = sep->se_next)
3151553Srgrimes	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
3161553Srgrimes		    n--;
3171553Srgrimes		    if (debug)
3181553Srgrimes			    fprintf(stderr, "someone wants %s\n",
3191553Srgrimes				sep->se_service);
3201553Srgrimes		    if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
3211553Srgrimes			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
3221553Srgrimes				(int *)0);
3231553Srgrimes			    if (debug)
3241553Srgrimes				    fprintf(stderr, "accept, ctrl %d\n", ctrl);
3251553Srgrimes			    if (ctrl < 0) {
3261553Srgrimes				    if (errno != EINTR)
3271553Srgrimes					    syslog(LOG_WARNING,
3281553Srgrimes						"accept (for %s): %m",
3291553Srgrimes						sep->se_service);
3301553Srgrimes				    continue;
3311553Srgrimes			    }
3321553Srgrimes			    /*
3331553Srgrimes			     * Call tcpmux to find the real service to exec.
3341553Srgrimes			     */
3351553Srgrimes			    if (sep->se_bi &&
3361553Srgrimes				sep->se_bi->bi_fn == (void (*)()) tcpmux) {
3371553Srgrimes				    sep = tcpmux(ctrl);
3381553Srgrimes				    if (sep == NULL) {
3391553Srgrimes					    close(ctrl);
3401553Srgrimes					    continue;
3411553Srgrimes				    }
3421553Srgrimes			    }
3431553Srgrimes		    } else
3441553Srgrimes			    ctrl = sep->se_fd;
3451553Srgrimes		    (void) sigblock(SIGBLOCK);
3461553Srgrimes		    pid = 0;
3471553Srgrimes		    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
3481553Srgrimes		    if (dofork) {
3491553Srgrimes			    if (sep->se_count++ == 0)
3501553Srgrimes				(void)gettimeofday(&sep->se_time,
3511553Srgrimes				    (struct timezone *)0);
3521553Srgrimes			    else if (sep->se_count >= toomany) {
3531553Srgrimes				struct timeval now;
3541553Srgrimes
3551553Srgrimes				(void)gettimeofday(&now, (struct timezone *)0);
3561553Srgrimes				if (now.tv_sec - sep->se_time.tv_sec >
3571553Srgrimes				    CNT_INTVL) {
3581553Srgrimes					sep->se_time = now;
3591553Srgrimes					sep->se_count = 1;
3601553Srgrimes				} else {
3611553Srgrimes					syslog(LOG_ERR,
3621553Srgrimes			"%s/%s server failing (looping), service terminated",
3631553Srgrimes					    sep->se_service, sep->se_proto);
3641553Srgrimes					close_sep(sep);
3651553Srgrimes					sigsetmask(0L);
3661553Srgrimes					if (!timingout) {
3671553Srgrimes						timingout = 1;
3681553Srgrimes						alarm(RETRYTIME);
3691553Srgrimes					}
3701553Srgrimes					continue;
3711553Srgrimes				}
3721553Srgrimes			    }
3731553Srgrimes			    pid = fork();
3741553Srgrimes		    }
3751553Srgrimes		    if (pid < 0) {
3761553Srgrimes			    syslog(LOG_ERR, "fork: %m");
3771553Srgrimes			    if (!sep->se_wait &&
3781553Srgrimes				sep->se_socktype == SOCK_STREAM)
3791553Srgrimes				    close(ctrl);
3801553Srgrimes			    sigsetmask(0L);
3811553Srgrimes			    sleep(1);
3821553Srgrimes			    continue;
3831553Srgrimes		    }
3841553Srgrimes		    if (pid && sep->se_wait) {
3851553Srgrimes			    sep->se_wait = pid;
3861553Srgrimes			    if (sep->se_fd >= 0) {
3871553Srgrimes				FD_CLR(sep->se_fd, &allsock);
3881553Srgrimes			        nsock--;
3891553Srgrimes			    }
3901553Srgrimes		    }
3911553Srgrimes		    sigsetmask(0L);
3921553Srgrimes		    if (pid == 0) {
3931553Srgrimes			    if (debug && dofork)
3941553Srgrimes				setsid();
3951553Srgrimes			    if (dofork) {
3961553Srgrimes				if (debug)
3971553Srgrimes					fprintf(stderr, "+ Closing from %d\n",
3981553Srgrimes						maxsock);
3991553Srgrimes				for (tmpint = maxsock; tmpint > 2; tmpint--)
4001553Srgrimes					if (tmpint != ctrl)
4011553Srgrimes						close(tmpint);
4021553Srgrimes			    }
4031553Srgrimes			    if (sep->se_bi)
4041553Srgrimes				(*sep->se_bi->bi_fn)(ctrl, sep);
4051553Srgrimes			    else {
4061553Srgrimes				if (debug)
4071553Srgrimes					fprintf(stderr, "%d execl %s\n",
4081553Srgrimes					    getpid(), sep->se_server);
4091553Srgrimes				dup2(ctrl, 0);
4101553Srgrimes				close(ctrl);
4111553Srgrimes				dup2(0, 1);
4121553Srgrimes				dup2(0, 2);
4131553Srgrimes				if ((pwd = getpwnam(sep->se_user)) == NULL) {
4141553Srgrimes					syslog(LOG_ERR,
4151553Srgrimes					    "%s/%s: %s: No such user",
4161553Srgrimes						sep->se_service, sep->se_proto,
4171553Srgrimes						sep->se_user);
4181553Srgrimes					if (sep->se_socktype != SOCK_STREAM)
4191553Srgrimes						recv(0, buf, sizeof (buf), 0);
4201553Srgrimes					_exit(1);
4211553Srgrimes				}
4221553Srgrimes				if (pwd->pw_uid) {
4231553Srgrimes					if (setgid(pwd->pw_gid) < 0) {
4241553Srgrimes						syslog(LOG_ERR,
4251553Srgrimes						  "%s: can't set gid %d: %m",
4261553Srgrimes						  sep->se_service, pwd->pw_gid);
4271553Srgrimes						_exit(1);
4281553Srgrimes					}
4291553Srgrimes					(void) initgroups(pwd->pw_name,
4301553Srgrimes							pwd->pw_gid);
4311553Srgrimes					if (setuid(pwd->pw_uid) < 0) {
4321553Srgrimes						syslog(LOG_ERR,
4331553Srgrimes						  "%s: can't set uid %d: %m",
4341553Srgrimes						  sep->se_service, pwd->pw_uid);
4351553Srgrimes						_exit(1);
4361553Srgrimes					}
4371553Srgrimes				}
4381553Srgrimes				execv(sep->se_server, sep->se_argv);
4391553Srgrimes				if (sep->se_socktype != SOCK_STREAM)
4401553Srgrimes					recv(0, buf, sizeof (buf), 0);
4411553Srgrimes				syslog(LOG_ERR,
4421553Srgrimes				    "cannot execute %s: %m", sep->se_server);
4431553Srgrimes				_exit(1);
4441553Srgrimes			    }
4451553Srgrimes		    }
4461553Srgrimes		    if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
4471553Srgrimes			    close(ctrl);
4481553Srgrimes		}
4491553Srgrimes	}
4501553Srgrimes}
4511553Srgrimes
4521553Srgrimesvoid
4531553Srgrimesreapchild(signo)
4541553Srgrimes	int signo;
4551553Srgrimes{
4561553Srgrimes	int status;
4571553Srgrimes	pid_t pid;
4581553Srgrimes	struct servtab *sep;
4591553Srgrimes
4601553Srgrimes	for (;;) {
4611553Srgrimes		pid = wait3(&status, WNOHANG, (struct rusage *)0);
4621553Srgrimes		if (pid <= 0)
4631553Srgrimes			break;
4641553Srgrimes		if (debug)
4651553Srgrimes			fprintf(stderr, "%d reaped, status %#x\n",
4661553Srgrimes				pid, status);
4671553Srgrimes		for (sep = servtab; sep; sep = sep->se_next)
4681553Srgrimes			if (sep->se_wait == pid) {
4691553Srgrimes				if (status)
4701553Srgrimes					syslog(LOG_WARNING,
4711553Srgrimes					    "%s: exit status 0x%x",
4721553Srgrimes					    sep->se_server, status);
4731553Srgrimes				if (debug)
4741553Srgrimes					fprintf(stderr, "restored %s, fd %d\n",
4751553Srgrimes					    sep->se_service, sep->se_fd);
4761553Srgrimes				FD_SET(sep->se_fd, &allsock);
4771553Srgrimes				nsock++;
4781553Srgrimes				sep->se_wait = 1;
4791553Srgrimes			}
4801553Srgrimes	}
4811553Srgrimes}
4821553Srgrimes
4831553Srgrimesvoid
4841553Srgrimesconfig(signo)
4851553Srgrimes	int signo;
4861553Srgrimes{
4871553Srgrimes	struct servtab *sep, *cp, **sepp;
4881553Srgrimes	struct passwd *pwd;
4891553Srgrimes	long omask;
4901553Srgrimes
4911553Srgrimes	if (!setconfig()) {
4921553Srgrimes		syslog(LOG_ERR, "%s: %m", CONFIG);
4931553Srgrimes		return;
4941553Srgrimes	}
4951553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
4961553Srgrimes		sep->se_checked = 0;
4971553Srgrimes	while (cp = getconfigent()) {
4981553Srgrimes		if ((pwd = getpwnam(cp->se_user)) == NULL) {
4991553Srgrimes			syslog(LOG_ERR,
5001553Srgrimes				"%s/%s: No such user '%s', service ignored",
5011553Srgrimes				cp->se_service, cp->se_proto, cp->se_user);
5021553Srgrimes			continue;
5031553Srgrimes		}
5041553Srgrimes		for (sep = servtab; sep; sep = sep->se_next)
5051553Srgrimes			if (strcmp(sep->se_service, cp->se_service) == 0 &&
5061553Srgrimes			    strcmp(sep->se_proto, cp->se_proto) == 0)
5071553Srgrimes				break;
5081553Srgrimes		if (sep != 0) {
5091553Srgrimes			int i;
5101553Srgrimes
5111553Srgrimes			omask = sigblock(SIGBLOCK);
5121553Srgrimes			/*
5131553Srgrimes			 * sep->se_wait may be holding the pid of a daemon
5141553Srgrimes			 * that we're waiting for.  If so, don't overwrite
5151553Srgrimes			 * it unless the config file explicitly says don't
5161553Srgrimes			 * wait.
5171553Srgrimes			 */
5181553Srgrimes			if (cp->se_bi == 0 &&
5191553Srgrimes			    (sep->se_wait == 1 || cp->se_wait == 0))
5201553Srgrimes				sep->se_wait = cp->se_wait;
5211553Srgrimes#define SWAP(a, b) { char *c = a; a = b; b = c; }
5221553Srgrimes			if (cp->se_user)
5231553Srgrimes				SWAP(sep->se_user, cp->se_user);
5241553Srgrimes			if (cp->se_server)
5251553Srgrimes				SWAP(sep->se_server, cp->se_server);
5261553Srgrimes			for (i = 0; i < MAXARGV; i++)
5271553Srgrimes				SWAP(sep->se_argv[i], cp->se_argv[i]);
5281553Srgrimes			sigsetmask(omask);
5291553Srgrimes			freeconfig(cp);
5301553Srgrimes			if (debug)
5311553Srgrimes				print_service("REDO", sep);
5321553Srgrimes		} else {
5331553Srgrimes			sep = enter(cp);
5341553Srgrimes			if (debug)
5351553Srgrimes				print_service("ADD ", sep);
5361553Srgrimes		}
5371553Srgrimes		sep->se_checked = 1;
5381553Srgrimes		if (ISMUX(sep)) {
5391553Srgrimes			sep->se_fd = -1;
5401553Srgrimes			continue;
5411553Srgrimes		}
5421553Srgrimes		sp = getservbyname(sep->se_service, sep->se_proto);
5431553Srgrimes		if (sp == 0) {
5441553Srgrimes			syslog(LOG_ERR, "%s/%s: unknown service",
5451553Srgrimes			    sep->se_service, sep->se_proto);
5461553Srgrimes			sep->se_checked = 0;
5471553Srgrimes			continue;
5481553Srgrimes		}
5491553Srgrimes		if (sp->s_port != sep->se_ctrladdr.sin_port) {
5501553Srgrimes			sep->se_ctrladdr.sin_family = AF_INET;
5511553Srgrimes			sep->se_ctrladdr.sin_port = sp->s_port;
5521553Srgrimes			if (sep->se_fd >= 0)
5531553Srgrimes				close_sep(sep);
5541553Srgrimes		}
5551553Srgrimes		if (sep->se_fd == -1)
5561553Srgrimes			setup(sep);
5571553Srgrimes	}
5581553Srgrimes	endconfig();
5591553Srgrimes	/*
5601553Srgrimes	 * Purge anything not looked at above.
5611553Srgrimes	 */
5621553Srgrimes	omask = sigblock(SIGBLOCK);
5631553Srgrimes	sepp = &servtab;
5641553Srgrimes	while (sep = *sepp) {
5651553Srgrimes		if (sep->se_checked) {
5661553Srgrimes			sepp = &sep->se_next;
5671553Srgrimes			continue;
5681553Srgrimes		}
5691553Srgrimes		*sepp = sep->se_next;
5701553Srgrimes		if (sep->se_fd >= 0)
5711553Srgrimes			close_sep(sep);
5721553Srgrimes		if (debug)
5731553Srgrimes			print_service("FREE", sep);
5741553Srgrimes		freeconfig(sep);
5751553Srgrimes		free((char *)sep);
5761553Srgrimes	}
5771553Srgrimes	(void) sigsetmask(omask);
5781553Srgrimes}
5791553Srgrimes
5801553Srgrimesvoid
5811553Srgrimesretry(signo)
5821553Srgrimes	int signo;
5831553Srgrimes{
5841553Srgrimes	struct servtab *sep;
5851553Srgrimes
5861553Srgrimes	timingout = 0;
5871553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
5881553Srgrimes		if (sep->se_fd == -1)
5891553Srgrimes			setup(sep);
5901553Srgrimes}
5911553Srgrimes
5921553Srgrimesvoid
5931553Srgrimessetup(sep)
5941553Srgrimes	struct servtab *sep;
5951553Srgrimes{
5961553Srgrimes	int on = 1;
5971553Srgrimes
5981553Srgrimes	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
5991553Srgrimes		if (debug)
6001553Srgrimes			fprintf(stderr, "socket failed on %s/%s: %s\n",
6011553Srgrimes				sep->se_service, sep->se_proto,
6021553Srgrimes				strerror(errno));
6031553Srgrimes		syslog(LOG_ERR, "%s/%s: socket: %m",
6041553Srgrimes		    sep->se_service, sep->se_proto);
6051553Srgrimes		return;
6061553Srgrimes	}
6071553Srgrimes#define	turnon(fd, opt) \
6081553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
6091553Srgrimes	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
6101553Srgrimes	    turnon(sep->se_fd, SO_DEBUG) < 0)
6111553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
6121553Srgrimes	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
6131553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
6141553Srgrimes#undef turnon
6151553Srgrimes	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
6161553Srgrimes	    sizeof (sep->se_ctrladdr)) < 0) {
6171553Srgrimes		if (debug)
6181553Srgrimes			fprintf(stderr, "bind failed on %s/%s: %s\n",
6191553Srgrimes				sep->se_service, sep->se_proto,
6201553Srgrimes				strerror(errno));
6211553Srgrimes		syslog(LOG_ERR, "%s/%s: bind: %m",
6221553Srgrimes		    sep->se_service, sep->se_proto);
6231553Srgrimes		(void) close(sep->se_fd);
6241553Srgrimes		sep->se_fd = -1;
6251553Srgrimes		if (!timingout) {
6261553Srgrimes			timingout = 1;
6271553Srgrimes			alarm(RETRYTIME);
6281553Srgrimes		}
6291553Srgrimes		return;
6301553Srgrimes	}
6311553Srgrimes	if (sep->se_socktype == SOCK_STREAM)
6321553Srgrimes		listen(sep->se_fd, 10);
6331553Srgrimes	FD_SET(sep->se_fd, &allsock);
6341553Srgrimes	nsock++;
6351553Srgrimes	if (sep->se_fd > maxsock)
6361553Srgrimes		maxsock = sep->se_fd;
6371553Srgrimes	if (debug) {
6381553Srgrimes		fprintf(stderr, "registered %s on %d\n",
6391553Srgrimes			sep->se_server, sep->se_fd);
6401553Srgrimes	}
6411553Srgrimes}
6421553Srgrimes
6431553Srgrimes/*
6441553Srgrimes * Finish with a service and its socket.
6451553Srgrimes */
6461553Srgrimesvoid
6471553Srgrimesclose_sep(sep)
6481553Srgrimes	struct servtab *sep;
6491553Srgrimes{
6501553Srgrimes	if (sep->se_fd >= 0) {
6511553Srgrimes		nsock--;
6521553Srgrimes		FD_CLR(sep->se_fd, &allsock);
6531553Srgrimes		(void) close(sep->se_fd);
6541553Srgrimes		sep->se_fd = -1;
6551553Srgrimes	}
6561553Srgrimes	sep->se_count = 0;
6571553Srgrimes	/*
6581553Srgrimes	 * Don't keep the pid of this running deamon: when reapchild()
6591553Srgrimes	 * reaps this pid, it would erroneously increment nsock.
6601553Srgrimes	 */
6611553Srgrimes	if (sep->se_wait > 1)
6621553Srgrimes		sep->se_wait = 1;
6631553Srgrimes}
6641553Srgrimes
6651553Srgrimesstruct servtab *
6661553Srgrimesenter(cp)
6671553Srgrimes	struct servtab *cp;
6681553Srgrimes{
6691553Srgrimes	struct servtab *sep;
6701553Srgrimes	long omask;
6711553Srgrimes
6721553Srgrimes	sep = (struct servtab *)malloc(sizeof (*sep));
6731553Srgrimes	if (sep == (struct servtab *)0) {
6741553Srgrimes		syslog(LOG_ERR, "Out of memory.");
6751553Srgrimes		exit(-1);
6761553Srgrimes	}
6771553Srgrimes	*sep = *cp;
6781553Srgrimes	sep->se_fd = -1;
6791553Srgrimes	omask = sigblock(SIGBLOCK);
6801553Srgrimes	sep->se_next = servtab;
6811553Srgrimes	servtab = sep;
6821553Srgrimes	sigsetmask(omask);
6831553Srgrimes	return (sep);
6841553Srgrimes}
6851553Srgrimes
6861553SrgrimesFILE	*fconfig = NULL;
6871553Srgrimesstruct	servtab serv;
6881553Srgrimeschar	line[LINE_MAX];
6891553Srgrimes
6901553Srgrimesint
6911553Srgrimessetconfig()
6921553Srgrimes{
6931553Srgrimes
6941553Srgrimes	if (fconfig != NULL) {
6951553Srgrimes		fseek(fconfig, 0L, SEEK_SET);
6961553Srgrimes		return (1);
6971553Srgrimes	}
6981553Srgrimes	fconfig = fopen(CONFIG, "r");
6991553Srgrimes	return (fconfig != NULL);
7001553Srgrimes}
7011553Srgrimes
7021553Srgrimesvoid
7031553Srgrimesendconfig()
7041553Srgrimes{
7051553Srgrimes	if (fconfig) {
7061553Srgrimes		(void) fclose(fconfig);
7071553Srgrimes		fconfig = NULL;
7081553Srgrimes	}
7091553Srgrimes}
7101553Srgrimes
7111553Srgrimesstruct servtab *
7121553Srgrimesgetconfigent()
7131553Srgrimes{
7141553Srgrimes	struct servtab *sep = &serv;
7151553Srgrimes	int argc;
7161553Srgrimes	char *cp, *arg;
7171553Srgrimes	static char TCPMUX_TOKEN[] = "tcpmux/";
7181553Srgrimes#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
7191553Srgrimes
7201553Srgrimesmore:
7211553Srgrimes	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
7221553Srgrimes		;
7231553Srgrimes	if (cp == NULL)
7241553Srgrimes		return ((struct servtab *)0);
7251553Srgrimes	/*
7261553Srgrimes	 * clear the static buffer, since some fields (se_ctrladdr,
7271553Srgrimes	 * for example) don't get initialized here.
7281553Srgrimes	 */
7291553Srgrimes	memset((caddr_t)sep, 0, sizeof *sep);
7301553Srgrimes	arg = skip(&cp);
7311553Srgrimes	if (cp == NULL) {
7321553Srgrimes		/* got an empty line containing just blanks/tabs. */
7331553Srgrimes		goto more;
7341553Srgrimes	}
7351553Srgrimes	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
7361553Srgrimes		char *c = arg + MUX_LEN;
7371553Srgrimes		if (*c == '+') {
7381553Srgrimes			sep->se_type = MUXPLUS_TYPE;
7391553Srgrimes			c++;
7401553Srgrimes		} else
7411553Srgrimes			sep->se_type = MUX_TYPE;
7421553Srgrimes		sep->se_service = newstr(c);
7431553Srgrimes	} else {
7441553Srgrimes		sep->se_service = newstr(arg);
7451553Srgrimes		sep->se_type = NORM_TYPE;
7461553Srgrimes	}
7471553Srgrimes	arg = sskip(&cp);
7481553Srgrimes	if (strcmp(arg, "stream") == 0)
7491553Srgrimes		sep->se_socktype = SOCK_STREAM;
7501553Srgrimes	else if (strcmp(arg, "dgram") == 0)
7511553Srgrimes		sep->se_socktype = SOCK_DGRAM;
7521553Srgrimes	else if (strcmp(arg, "rdm") == 0)
7531553Srgrimes		sep->se_socktype = SOCK_RDM;
7541553Srgrimes	else if (strcmp(arg, "seqpacket") == 0)
7551553Srgrimes		sep->se_socktype = SOCK_SEQPACKET;
7561553Srgrimes	else if (strcmp(arg, "raw") == 0)
7571553Srgrimes		sep->se_socktype = SOCK_RAW;
7581553Srgrimes	else
7591553Srgrimes		sep->se_socktype = -1;
7601553Srgrimes	sep->se_proto = newstr(sskip(&cp));
7611553Srgrimes	arg = sskip(&cp);
7621553Srgrimes	sep->se_wait = strcmp(arg, "wait") == 0;
7631553Srgrimes	if (ISMUX(sep)) {
7641553Srgrimes		/*
7651553Srgrimes		 * Silently enforce "nowait" for TCPMUX services since
7661553Srgrimes		 * they don't have an assigned port to listen on.
7671553Srgrimes		 */
7681553Srgrimes		sep->se_wait = 0;
7691553Srgrimes
7701553Srgrimes		if (strcmp(sep->se_proto, "tcp")) {
7711553Srgrimes			syslog(LOG_ERR,
7721553Srgrimes				"%s: bad protocol for tcpmux service %s",
7731553Srgrimes				CONFIG, sep->se_service);
7741553Srgrimes			goto more;
7751553Srgrimes		}
7761553Srgrimes		if (sep->se_socktype != SOCK_STREAM) {
7771553Srgrimes			syslog(LOG_ERR,
7781553Srgrimes				"%s: bad socket type for tcpmux service %s",
7791553Srgrimes				CONFIG, sep->se_service);
7801553Srgrimes			goto more;
7811553Srgrimes		}
7821553Srgrimes	}
7831553Srgrimes	sep->se_user = newstr(sskip(&cp));
7841553Srgrimes	sep->se_server = newstr(sskip(&cp));
7851553Srgrimes	if (strcmp(sep->se_server, "internal") == 0) {
7861553Srgrimes		struct biltin *bi;
7871553Srgrimes
7881553Srgrimes		for (bi = biltins; bi->bi_service; bi++)
7891553Srgrimes			if (bi->bi_socktype == sep->se_socktype &&
7901553Srgrimes			    strcmp(bi->bi_service, sep->se_service) == 0)
7911553Srgrimes				break;
7921553Srgrimes		if (bi->bi_service == 0) {
7931553Srgrimes			syslog(LOG_ERR, "internal service %s unknown",
7941553Srgrimes				sep->se_service);
7951553Srgrimes			goto more;
7961553Srgrimes		}
7971553Srgrimes		sep->se_bi = bi;
7981553Srgrimes		sep->se_wait = bi->bi_wait;
7991553Srgrimes	} else
8001553Srgrimes		sep->se_bi = NULL;
8011553Srgrimes	argc = 0;
8021553Srgrimes	for (arg = skip(&cp); cp; arg = skip(&cp))
8031553Srgrimes		if (argc < MAXARGV)
8041553Srgrimes			sep->se_argv[argc++] = newstr(arg);
8051553Srgrimes	while (argc <= MAXARGV)
8061553Srgrimes		sep->se_argv[argc++] = NULL;
8071553Srgrimes	return (sep);
8081553Srgrimes}
8091553Srgrimes
8101553Srgrimesvoid
8111553Srgrimesfreeconfig(cp)
8121553Srgrimes	struct servtab *cp;
8131553Srgrimes{
8141553Srgrimes	int i;
8151553Srgrimes
8161553Srgrimes	if (cp->se_service)
8171553Srgrimes		free(cp->se_service);
8181553Srgrimes	if (cp->se_proto)
8191553Srgrimes		free(cp->se_proto);
8201553Srgrimes	if (cp->se_user)
8211553Srgrimes		free(cp->se_user);
8221553Srgrimes	if (cp->se_server)
8231553Srgrimes		free(cp->se_server);
8241553Srgrimes	for (i = 0; i < MAXARGV; i++)
8251553Srgrimes		if (cp->se_argv[i])
8261553Srgrimes			free(cp->se_argv[i]);
8271553Srgrimes}
8281553Srgrimes
8291553Srgrimes
8301553Srgrimes/*
8311553Srgrimes * Safe skip - if skip returns null, log a syntax error in the
8321553Srgrimes * configuration file and exit.
8331553Srgrimes */
8341553Srgrimeschar *
8351553Srgrimessskip(cpp)
8361553Srgrimes	char **cpp;
8371553Srgrimes{
8381553Srgrimes	char *cp;
8391553Srgrimes
8401553Srgrimes	cp = skip(cpp);
8411553Srgrimes	if (cp == NULL) {
8421553Srgrimes		syslog(LOG_ERR, "%s: syntax error", CONFIG);
8431553Srgrimes		exit(-1);
8441553Srgrimes	}
8451553Srgrimes	return (cp);
8461553Srgrimes}
8471553Srgrimes
8481553Srgrimeschar *
8491553Srgrimesskip(cpp)
8501553Srgrimes	char **cpp;
8511553Srgrimes{
8521553Srgrimes	char *cp = *cpp;
8531553Srgrimes	char *start;
8541553Srgrimes
8551553Srgrimesagain:
8561553Srgrimes	while (*cp == ' ' || *cp == '\t')
8571553Srgrimes		cp++;
8581553Srgrimes	if (*cp == '\0') {
8591553Srgrimes		int c;
8601553Srgrimes
8611553Srgrimes		c = getc(fconfig);
8621553Srgrimes		(void) ungetc(c, fconfig);
8631553Srgrimes		if (c == ' ' || c == '\t')
8641553Srgrimes			if (cp = nextline(fconfig))
8651553Srgrimes				goto again;
8661553Srgrimes		*cpp = (char *)0;
8671553Srgrimes		return ((char *)0);
8681553Srgrimes	}
8691553Srgrimes	start = cp;
8701553Srgrimes	while (*cp && *cp != ' ' && *cp != '\t')
8711553Srgrimes		cp++;
8721553Srgrimes	if (*cp != '\0')
8731553Srgrimes		*cp++ = '\0';
8741553Srgrimes	*cpp = cp;
8751553Srgrimes	return (start);
8761553Srgrimes}
8771553Srgrimes
8781553Srgrimeschar *
8791553Srgrimesnextline(fd)
8801553Srgrimes	FILE *fd;
8811553Srgrimes{
8821553Srgrimes	char *cp;
8831553Srgrimes
8841553Srgrimes	if (fgets(line, sizeof (line), fd) == NULL)
8851553Srgrimes		return ((char *)0);
8861553Srgrimes	cp = strchr(line, '\n');
8871553Srgrimes	if (cp)
8881553Srgrimes		*cp = '\0';
8891553Srgrimes	return (line);
8901553Srgrimes}
8911553Srgrimes
8921553Srgrimeschar *
8931553Srgrimesnewstr(cp)
8941553Srgrimes	char *cp;
8951553Srgrimes{
8961553Srgrimes	if (cp = strdup(cp ? cp : ""))
8971553Srgrimes		return (cp);
8981553Srgrimes	syslog(LOG_ERR, "strdup: %m");
8991553Srgrimes	exit(-1);
9001553Srgrimes}
9011553Srgrimes
9021553Srgrimesvoid
9031553Srgrimessetproctitle(a, s)
9041553Srgrimes	char *a;
9051553Srgrimes	int s;
9061553Srgrimes{
9071553Srgrimes	int size;
9081553Srgrimes	char *cp;
9091553Srgrimes	struct sockaddr_in sin;
9101553Srgrimes	char buf[80];
9111553Srgrimes
9121553Srgrimes	cp = Argv[0];
9131553Srgrimes	size = sizeof(sin);
9141553Srgrimes	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
9151553Srgrimes		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
9161553Srgrimes	else
9171553Srgrimes		(void) sprintf(buf, "-%s", a);
9181553Srgrimes	strncpy(cp, buf, LastArg - cp);
9191553Srgrimes	cp += strlen(cp);
9201553Srgrimes	while (cp < LastArg)
9211553Srgrimes		*cp++ = ' ';
9221553Srgrimes}
9231553Srgrimes
9241553Srgrimes/*
9251553Srgrimes * Internet services provided internally by inetd:
9261553Srgrimes */
9271553Srgrimes#define	BUFSIZE	8192
9281553Srgrimes
9291553Srgrimes/* ARGSUSED */
9301553Srgrimesvoid
9311553Srgrimesecho_stream(s, sep)		/* Echo service -- echo data back */
9321553Srgrimes	int s;
9331553Srgrimes	struct servtab *sep;
9341553Srgrimes{
9351553Srgrimes	char buffer[BUFSIZE];
9361553Srgrimes	int i;
9371553Srgrimes
9381553Srgrimes	setproctitle(sep->se_service, s);
9391553Srgrimes	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
9401553Srgrimes	    write(s, buffer, i) > 0)
9411553Srgrimes		;
9421553Srgrimes	exit(0);
9431553Srgrimes}
9441553Srgrimes
9451553Srgrimes/* ARGSUSED */
9461553Srgrimesvoid
9471553Srgrimesecho_dg(s, sep)			/* Echo service -- echo data back */
9481553Srgrimes	int s;
9491553Srgrimes	struct servtab *sep;
9501553Srgrimes{
9511553Srgrimes	char buffer[BUFSIZE];
9521553Srgrimes	int i, size;
9531553Srgrimes	struct sockaddr sa;
9541553Srgrimes
9551553Srgrimes	size = sizeof(sa);
9561553Srgrimes	if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
9571553Srgrimes		return;
9581553Srgrimes	(void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
9591553Srgrimes}
9601553Srgrimes
9611553Srgrimes/* ARGSUSED */
9621553Srgrimesvoid
9631553Srgrimesdiscard_stream(s, sep)		/* Discard service -- ignore data */
9641553Srgrimes	int s;
9651553Srgrimes	struct servtab *sep;
9661553Srgrimes{
9671553Srgrimes	int ret;
9681553Srgrimes	char buffer[BUFSIZE];
9691553Srgrimes
9701553Srgrimes	setproctitle(sep->se_service, s);
9711553Srgrimes	while (1) {
9721553Srgrimes		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
9731553Srgrimes			;
9741553Srgrimes		if (ret == 0 || errno != EINTR)
9751553Srgrimes			break;
9761553Srgrimes	}
9771553Srgrimes	exit(0);
9781553Srgrimes}
9791553Srgrimes
9801553Srgrimes/* ARGSUSED */
9811553Srgrimesvoid
9821553Srgrimesdiscard_dg(s, sep)		/* Discard service -- ignore data */
9831553Srgrimes	int s;
9841553Srgrimes	struct servtab *sep;
9851553Srgrimes{
9861553Srgrimes	char buffer[BUFSIZE];
9871553Srgrimes
9881553Srgrimes	(void) read(s, buffer, sizeof(buffer));
9891553Srgrimes}
9901553Srgrimes
9911553Srgrimes#include <ctype.h>
9921553Srgrimes#define LINESIZ 72
9931553Srgrimeschar ring[128];
9941553Srgrimeschar *endring;
9951553Srgrimes
9961553Srgrimesvoid
9971553Srgrimesinitring()
9981553Srgrimes{
9991553Srgrimes	int i;
10001553Srgrimes
10011553Srgrimes	endring = ring;
10021553Srgrimes
10031553Srgrimes	for (i = 0; i <= 128; ++i)
10041553Srgrimes		if (isprint(i))
10051553Srgrimes			*endring++ = i;
10061553Srgrimes}
10071553Srgrimes
10081553Srgrimes/* ARGSUSED */
10091553Srgrimesvoid
10101553Srgrimeschargen_stream(s, sep)		/* Character generator */
10111553Srgrimes	int s;
10121553Srgrimes	struct servtab *sep;
10131553Srgrimes{
10141553Srgrimes	int len;
10151553Srgrimes	char *rs, text[LINESIZ+2];
10161553Srgrimes
10171553Srgrimes	setproctitle(sep->se_service, s);
10181553Srgrimes
10191553Srgrimes	if (!endring) {
10201553Srgrimes		initring();
10211553Srgrimes		rs = ring;
10221553Srgrimes	}
10231553Srgrimes
10241553Srgrimes	text[LINESIZ] = '\r';
10251553Srgrimes	text[LINESIZ + 1] = '\n';
10261553Srgrimes	for (rs = ring;;) {
10271553Srgrimes		if ((len = endring - rs) >= LINESIZ)
10281553Srgrimes			memmove(text, rs, LINESIZ);
10291553Srgrimes		else {
10301553Srgrimes			memmove(text, rs, len);
10311553Srgrimes			memmove(text + len, ring, LINESIZ - len);
10321553Srgrimes		}
10331553Srgrimes		if (++rs == endring)
10341553Srgrimes			rs = ring;
10351553Srgrimes		if (write(s, text, sizeof(text)) != sizeof(text))
10361553Srgrimes			break;
10371553Srgrimes	}
10381553Srgrimes	exit(0);
10391553Srgrimes}
10401553Srgrimes
10411553Srgrimes/* ARGSUSED */
10421553Srgrimesvoid
10431553Srgrimeschargen_dg(s, sep)		/* Character generator */
10441553Srgrimes	int s;
10451553Srgrimes	struct servtab *sep;
10461553Srgrimes{
10471553Srgrimes	struct sockaddr sa;
10481553Srgrimes	static char *rs;
10491553Srgrimes	int len, size;
10501553Srgrimes	char text[LINESIZ+2];
10511553Srgrimes
10521553Srgrimes	if (endring == 0) {
10531553Srgrimes		initring();
10541553Srgrimes		rs = ring;
10551553Srgrimes	}
10561553Srgrimes
10571553Srgrimes	size = sizeof(sa);
10581553Srgrimes	if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
10591553Srgrimes		return;
10601553Srgrimes
10611553Srgrimes	if ((len = endring - rs) >= LINESIZ)
10621553Srgrimes		memmove(text, rs, LINESIZ);
10631553Srgrimes	else {
10641553Srgrimes		memmove(text, rs, len);
10651553Srgrimes		memmove(text + len, ring, LINESIZ - len);
10661553Srgrimes	}
10671553Srgrimes	if (++rs == endring)
10681553Srgrimes		rs = ring;
10691553Srgrimes	text[LINESIZ] = '\r';
10701553Srgrimes	text[LINESIZ + 1] = '\n';
10711553Srgrimes	(void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
10721553Srgrimes}
10731553Srgrimes
10741553Srgrimes/*
10751553Srgrimes * Return a machine readable date and time, in the form of the
10761553Srgrimes * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
10771553Srgrimes * returns the number of seconds since midnight, Jan 1, 1970,
10781553Srgrimes * we must add 2208988800 seconds to this figure to make up for
10791553Srgrimes * some seventy years Bell Labs was asleep.
10801553Srgrimes */
10811553Srgrimes
10821553Srgrimeslong
10831553Srgrimesmachtime()
10841553Srgrimes{
10851553Srgrimes	struct timeval tv;
10861553Srgrimes
10871553Srgrimes	if (gettimeofday(&tv, (struct timezone *)0) < 0) {
10881553Srgrimes		if (debug)
10891553Srgrimes			fprintf(stderr, "Unable to get time of day\n");
10901553Srgrimes		return (0L);
10911553Srgrimes	}
10921553Srgrimes#define	OFFSET ((u_long)25567 * 24*60*60)
10931553Srgrimes	return (htonl((long)(tv.tv_sec + OFFSET)));
10941553Srgrimes#undef OFFSET
10951553Srgrimes}
10961553Srgrimes
10971553Srgrimes/* ARGSUSED */
10981553Srgrimesvoid
10991553Srgrimesmachtime_stream(s, sep)
11001553Srgrimes	int s;
11011553Srgrimes	struct servtab *sep;
11021553Srgrimes{
11031553Srgrimes	long result;
11041553Srgrimes
11051553Srgrimes	result = machtime();
11061553Srgrimes	(void) write(s, (char *) &result, sizeof(result));
11071553Srgrimes}
11081553Srgrimes
11091553Srgrimes/* ARGSUSED */
11101553Srgrimesvoid
11111553Srgrimesmachtime_dg(s, sep)
11121553Srgrimes	int s;
11131553Srgrimes	struct servtab *sep;
11141553Srgrimes{
11151553Srgrimes	long result;
11161553Srgrimes	struct sockaddr sa;
11171553Srgrimes	int size;
11181553Srgrimes
11191553Srgrimes	size = sizeof(sa);
11201553Srgrimes	if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
11211553Srgrimes		return;
11221553Srgrimes	result = machtime();
11231553Srgrimes	(void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
11241553Srgrimes}
11251553Srgrimes
11261553Srgrimes/* ARGSUSED */
11271553Srgrimesvoid
11281553Srgrimesdaytime_stream(s, sep)		/* Return human-readable time of day */
11291553Srgrimes	int s;
11301553Srgrimes	struct servtab *sep;
11311553Srgrimes{
11321553Srgrimes	char buffer[256];
11331553Srgrimes	time_t clock;
11341553Srgrimes
11351553Srgrimes	clock = time((time_t *) 0);
11361553Srgrimes
11371553Srgrimes	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
11381553Srgrimes	(void) write(s, buffer, strlen(buffer));
11391553Srgrimes}
11401553Srgrimes
11411553Srgrimes/* ARGSUSED */
11421553Srgrimesvoid
11431553Srgrimesdaytime_dg(s, sep)		/* Return human-readable time of day */
11441553Srgrimes	int s;
11451553Srgrimes	struct servtab *sep;
11461553Srgrimes{
11471553Srgrimes	char buffer[256];
11481553Srgrimes	time_t clock;
11491553Srgrimes	struct sockaddr sa;
11501553Srgrimes	int size;
11511553Srgrimes
11521553Srgrimes	clock = time((time_t *) 0);
11531553Srgrimes
11541553Srgrimes	size = sizeof(sa);
11551553Srgrimes	if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
11561553Srgrimes		return;
11571553Srgrimes	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
11581553Srgrimes	(void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
11591553Srgrimes}
11601553Srgrimes
11611553Srgrimes/*
11621553Srgrimes * print_service:
11631553Srgrimes *	Dump relevant information to stderr
11641553Srgrimes */
11651553Srgrimesvoid
11661553Srgrimesprint_service(action, sep)
11671553Srgrimes	char *action;
11681553Srgrimes	struct servtab *sep;
11691553Srgrimes{
11701553Srgrimes	fprintf(stderr,
11711553Srgrimes	    "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
11721553Srgrimes	    action, sep->se_service, sep->se_proto,
11731553Srgrimes	    sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server);
11741553Srgrimes}
11751553Srgrimes
11761553Srgrimes/*
11771553Srgrimes *  Based on TCPMUX.C by Mark K. Lottor November 1988
11781553Srgrimes *  sri-nic::ps:<mkl>tcpmux.c
11791553Srgrimes */
11801553Srgrimes
11811553Srgrimes
11821553Srgrimesstatic int		/* # of characters upto \r,\n or \0 */
11831553Srgrimesgetline(fd, buf, len)
11841553Srgrimes	int fd;
11851553Srgrimes	char *buf;
11861553Srgrimes	int len;
11871553Srgrimes{
11881553Srgrimes	int count = 0, n;
11891553Srgrimes
11901553Srgrimes	do {
11911553Srgrimes		n = read(fd, buf, len-count);
11921553Srgrimes		if (n == 0)
11931553Srgrimes			return (count);
11941553Srgrimes		if (n < 0)
11951553Srgrimes			return (-1);
11961553Srgrimes		while (--n >= 0) {
11971553Srgrimes			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
11981553Srgrimes				return (count);
11991553Srgrimes			count++;
12001553Srgrimes			buf++;
12011553Srgrimes		}
12021553Srgrimes	} while (count < len);
12031553Srgrimes	return (count);
12041553Srgrimes}
12051553Srgrimes
12061553Srgrimes#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
12071553Srgrimes
12081553Srgrimes#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
12091553Srgrimes
12101553Srgrimesstruct servtab *
12111553Srgrimestcpmux(s)
12121553Srgrimes	int s;
12131553Srgrimes{
12141553Srgrimes	struct servtab *sep;
12151553Srgrimes	char service[MAX_SERV_LEN+1];
12161553Srgrimes	int len;
12171553Srgrimes
12181553Srgrimes	/* Get requested service name */
12191553Srgrimes	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
12201553Srgrimes		strwrite(s, "-Error reading service name\r\n");
12211553Srgrimes		return (NULL);
12221553Srgrimes	}
12231553Srgrimes	service[len] = '\0';
12241553Srgrimes
12251553Srgrimes	if (debug)
12261553Srgrimes		fprintf(stderr, "tcpmux: someone wants %s\n", service);
12271553Srgrimes
12281553Srgrimes	/*
12291553Srgrimes	 * Help is a required command, and lists available services,
12301553Srgrimes	 * one per line.
12311553Srgrimes	 */
12321553Srgrimes	if (!strcasecmp(service, "help")) {
12331553Srgrimes		for (sep = servtab; sep; sep = sep->se_next) {
12341553Srgrimes			if (!ISMUX(sep))
12351553Srgrimes				continue;
12361553Srgrimes			(void)write(s,sep->se_service,strlen(sep->se_service));
12371553Srgrimes			strwrite(s, "\r\n");
12381553Srgrimes		}
12391553Srgrimes		return (NULL);
12401553Srgrimes	}
12411553Srgrimes
12421553Srgrimes	/* Try matching a service in inetd.conf with the request */
12431553Srgrimes	for (sep = servtab; sep; sep = sep->se_next) {
12441553Srgrimes		if (!ISMUX(sep))
12451553Srgrimes			continue;
12461553Srgrimes		if (!strcasecmp(service, sep->se_service)) {
12471553Srgrimes			if (ISMUXPLUS(sep)) {
12481553Srgrimes				strwrite(s, "+Go\r\n");
12491553Srgrimes			}
12501553Srgrimes			return (sep);
12511553Srgrimes		}
12521553Srgrimes	}
12531553Srgrimes	strwrite(s, "-Service not available\r\n");
12541553Srgrimes	return (NULL);
12551553Srgrimes}
1256