xref: /csrg-svn/usr.sbin/inetd/inetd.c (revision 21134)
1*21134Sdist /*
2*21134Sdist  * Copyright (c) 1983 Regents of the University of California.
3*21134Sdist  * All rights reserved.  The Berkeley software License Agreement
4*21134Sdist  * specifies the terms and conditions for redistribution.
5*21134Sdist  */
6*21134Sdist 
716374Skarels #ifndef lint
8*21134Sdist char copyright[] =
9*21134Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10*21134Sdist  All rights reserved.\n";
11*21134Sdist #endif not lint
1216374Skarels 
13*21134Sdist #ifndef lint
14*21134Sdist static char sccsid[] = "@(#)inetd.c	5.1 (Berkeley) 05/28/85";
15*21134Sdist #endif not lint
16*21134Sdist 
1716374Skarels /*
1816374Skarels  * Inetd - Internet super-server
1916374Skarels  *
2016374Skarels  * This program invokes all internet services as needed.
2116374Skarels  * connection-oriented services are invoked each time a
2216374Skarels  * connection is made, by creating a process.  This process
2316374Skarels  * is passed the connection as file descriptor 0 and is
2416374Skarels  * expected to do a getpeername to find out the source host
2516374Skarels  * and port.
2616374Skarels  *
2716374Skarels  * Datagram oriented services are invoked when a datagram
2816374Skarels  * arrives; a process is created and passed a pending message
2916374Skarels  * on file descriptor 0.  Datagram servers may either connect
3016374Skarels  * to their peer, freeing up the original socket for inetd
3116374Skarels  * to receive further messages on, or ``take over the socket'',
3216374Skarels  * processing all arriving datagrams and, eventually, timing
3316374Skarels  * out.	 The first type of server is said to be ``multi-threaded'';
3416374Skarels  * the second type of server ``single-threaded''.
3516374Skarels  *
3616374Skarels  * Inetd uses a configuration file which is read at startup
3716374Skarels  * and, possibly, at some later time in response to a hangup signal.
3816374Skarels  * The configuration file is ``free format'' with fields given in the
3916374Skarels  * order shown below.  Continuation lines for an entry must being with
4016374Skarels  * a space or tab.  All fields must be present in each entry.
4116374Skarels  *
4216374Skarels  *	service name			must be in /etc/services
4316374Skarels  *	socket type			stream/dgram/raw/rdm/seqpacket
4416374Skarels  *	protocol			must be in /etc/protocols
4516374Skarels  *	wait/nowait			single-threaded/multi-threaded
4617346Sbloom  *	user				user to run daemon as
4716374Skarels  *	server program			full path name
4816374Skarels  *	server program arguments	maximum of MAXARGS (5)
4916374Skarels  *
5016374Skarels  * Comment lines are indicated by a `#' in column 1.
5116374Skarels  */
5216374Skarels #include <sys/param.h>
5316374Skarels #include <sys/stat.h>
5416374Skarels #include <sys/ioctl.h>
5516374Skarels #include <sys/socket.h>
5616374Skarels #include <sys/file.h>
5716374Skarels #include <sys/wait.h>
5816374Skarels 
5916374Skarels #include <netinet/in.h>
6016374Skarels #include <arpa/inet.h>
6116374Skarels 
6216374Skarels #include <errno.h>
6316374Skarels #include <stdio.h>
6416374Skarels #include <signal.h>
6516374Skarels #include <netdb.h>
6616510Sralph #include <syslog.h>
6717346Sbloom #include <pwd.h>
6816374Skarels 
6916374Skarels extern	int errno;
7016374Skarels 
7116374Skarels int	reapchild();
7216374Skarels char	*index();
7316374Skarels char	*malloc();
7416374Skarels 
7516374Skarels int	debug = 0;
7616374Skarels int	allsock;
7716374Skarels int	options;
7816374Skarels struct	servent *sp;
7916374Skarels 
8016374Skarels struct	servtab {
8116374Skarels 	char	*se_service;		/* name of service */
8216374Skarels 	int	se_socktype;		/* type of socket to use */
8316374Skarels 	char	*se_proto;		/* protocol used */
8416374Skarels 	short	se_wait;		/* single threaded server */
8516374Skarels 	short	se_checked;		/* looked at during merge */
8617346Sbloom 	char	*se_user;		/* user name to run as */
8716374Skarels 	char	*se_server;		/* server program */
8816374Skarels #define MAXARGV 5
8916374Skarels 	char	*se_argv[MAXARGV+1];	/* program arguments */
9016374Skarels 	int	se_fd;			/* open descriptor */
9116374Skarels 	struct	sockaddr_in se_ctrladdr;/* bound address */
9216374Skarels 	struct	servtab *se_next;
9316374Skarels } *servtab;
9416374Skarels 
9516374Skarels char	*CONFIG = "/etc/inetd.conf";
9616374Skarels 
9716374Skarels main(argc, argv)
9816374Skarels 	int argc;
9916374Skarels 	char *argv[];
10016374Skarels {
10116374Skarels 	int ctrl;
10216374Skarels 	register struct servtab *sep;
10317346Sbloom 	register struct passwd *pwd;
10416374Skarels 	char *cp, buf[50];
10516374Skarels 	int pid, i;
10616374Skarels 
10716374Skarels 	argc--, argv++;
10816374Skarels 	while (argc > 0 && *argv[0] == '-') {
10916374Skarels 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
11016374Skarels 
11116374Skarels 		case 'd':
11216374Skarels 			debug = 1;
11316374Skarels 			options |= SO_DEBUG;
11416374Skarels 			break;
11516374Skarels 
11616374Skarels 		default:
11716374Skarels 			fprintf(stderr,
11816374Skarels 			    "inetd: Unknown flag -%c ignored.\n", *cp);
11916374Skarels 			break;
12016374Skarels 		}
12116374Skarels nextopt:
12216374Skarels 		argc--, argv++;
12316374Skarels 	}
12416374Skarels 	if (argc > 0)
12516374Skarels 		CONFIG = argv[0];
12616374Skarels #ifndef DEBUG
12716374Skarels 	if (fork())
12816374Skarels 		exit(0);
12916374Skarels 	{ int s;
13016374Skarels 	for (s = 0; s < 10; s++)
13116374Skarels 		(void) close(s);
13216374Skarels 	}
13316374Skarels 	(void) open("/", O_RDONLY);
13416374Skarels 	(void) dup2(0, 1);
13516374Skarels 	(void) dup2(0, 2);
13616374Skarels 	{ int tt = open("/dev/tty", O_RDWR);
13716374Skarels 	  if (tt > 0) {
13816374Skarels 		ioctl(tt, TIOCNOTTY, 0);
13916374Skarels 		close(tt);
14016374Skarels 	  }
14116374Skarels 	}
14216374Skarels #endif
14316510Sralph 	openlog("inetd", LOG_PID, 0);
14416374Skarels 	config();
14516374Skarels 	signal(SIGHUP, config);
14616374Skarels 	signal(SIGCHLD, reapchild);
14716374Skarels 	for (;;) {
14816374Skarels 		int readable, s, ctrl;
14916374Skarels 
15016374Skarels 		while (allsock == 0)
15116374Skarels 			sigpause(0);
15216374Skarels 		readable = allsock;
15316374Skarels 		if (select(32, &readable, 0, 0, 0) <= 0)
15416374Skarels 			continue;
15516374Skarels 		s = ffs(readable)-1;
15616374Skarels 		if (s < 0)
15716374Skarels 			continue;
15816374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
15916374Skarels 			if (s == sep->se_fd)
16016374Skarels 				goto found;
16116374Skarels 		abort(1);
16216374Skarels 	found:
16316374Skarels 		if (debug)
16416374Skarels 			fprintf(stderr, "someone wants %s\n", sep->se_service);
16516374Skarels 		if (sep->se_socktype == SOCK_STREAM) {
16616374Skarels 			ctrl = accept(s, 0, 0);
16716510Sralph 			if (debug)
16816510Sralph 				fprintf(stderr, "accept, ctrl %d\n", ctrl);
16916374Skarels 			if (ctrl < 0) {
17016374Skarels 				if (errno == EINTR)
17116374Skarels 					continue;
17216510Sralph 				syslog(LOG_WARNING, "accept: %m");
17316374Skarels 				continue;
17416374Skarels 			}
17516374Skarels 		} else
17616374Skarels 			ctrl = sep->se_fd;
17716374Skarels #define mask(sig)	(1 << (sig - 1))
17816374Skarels 		sigblock(mask(SIGCHLD)|mask(SIGHUP));
17916374Skarels 		pid = fork();
18016374Skarels 		if (pid < 0) {
18116374Skarels 			if (sep->se_socktype == SOCK_STREAM)
18216374Skarels 				close(ctrl);
18316374Skarels 			sleep(1);
18416374Skarels 			continue;
18516374Skarels 		}
18616374Skarels 		if (sep->se_wait) {
18716374Skarels 			sep->se_wait = pid;
18816374Skarels 			allsock &= ~(1 << s);
18916374Skarels 		}
19016374Skarels 		sigsetmask(0);
19116374Skarels 		if (pid == 0) {
19216374Skarels #ifdef	DEBUG
19316374Skarels 			int tt = open("/dev/tty", O_RDWR);
19416374Skarels 			if (tt > 0) {
19516374Skarels 				ioctl(tt, TIOCNOTTY, 0);
19616374Skarels 				close(tt);
19716374Skarels 			}
19816374Skarels #endif
19916374Skarels 			dup2(ctrl, 0), close(ctrl), dup2(0, 1);
20016374Skarels 			for (i = getdtablesize(); --i > 2; )
20116374Skarels 				close(i);
20217346Sbloom 			if ((pwd = getpwnam(sep->se_user)) == NULL) {
20317346Sbloom 				syslog(LOG_ERR, "getpwnam: %s: No such user"
20417346Sbloom 					,sep->se_user);
20517346Sbloom 				exit(1);
20617346Sbloom 			}
20717346Sbloom 			(void) setgid(pwd->pw_gid);
20817346Sbloom 			initgroups(pwd->pw_name, pwd->pw_gid);
20917346Sbloom 			(void) setuid(pwd->pw_uid);
21016374Skarels 			if (debug)
21116374Skarels 				fprintf(stderr, "%d execl %s\n",
21216374Skarels 				    getpid(), sep->se_server);
21316374Skarels 			execv(sep->se_server, sep->se_argv);
21416374Skarels 			if (sep->se_socktype != SOCK_STREAM)
21516510Sralph 				recv(0, buf, sizeof (buf), 0);
21616510Sralph 			syslog(LOG_ERR, "execv %s: %m", sep->se_server);
21716374Skarels 			_exit(1);
21816374Skarels 		}
21916374Skarels 		if (sep->se_socktype == SOCK_STREAM)
22016374Skarels 			close(ctrl);
22116374Skarels 	}
22216374Skarels }
22316374Skarels 
22416374Skarels reapchild()
22516374Skarels {
22616374Skarels 	union wait status;
22716374Skarels 	int pid;
22816374Skarels 	register struct servtab *sep;
22916374Skarels 
23016374Skarels 	for (;;) {
23116374Skarels 		pid = wait3(&status, WNOHANG, 0);
23216374Skarels 		if (pid <= 0)
23316374Skarels 			break;
23416374Skarels 		if (debug)
23516374Skarels 			fprintf(stderr, "%d reaped\n", pid);
23616374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
23716374Skarels 			if (sep->se_wait == pid) {
23816374Skarels 				if (status.w_status)
23916510Sralph 					syslog(LOG_WARNING,
24016510Sralph 					    "%s: exit status 0x%x",
24116374Skarels 					    sep->se_server, status);
24216374Skarels 				if (debug)
24316374Skarels 					fprintf(stderr, "restored %s, fd %d\n",
24416374Skarels 					    sep->se_service, sep->se_fd);
24516374Skarels 				allsock |= 1 << sep->se_fd;
24616374Skarels 				sep->se_wait = 1;
24716374Skarels 			}
24816374Skarels 	}
24916374Skarels }
25016374Skarels 
25116374Skarels config()
25216374Skarels {
25316374Skarels 	register struct servtab *sep, *cp, **sepp;
25416374Skarels 	struct servtab *getconfigent(), *enter();
25517156Ssam 	int omask, on = 1;
25616374Skarels 
25716374Skarels 	if (!setconfig()) {
25816510Sralph 		syslog(LOG_ERR, "%s: %m", CONFIG);
25916374Skarels 		return;
26016374Skarels 	}
26116374Skarels 	for (sep = servtab; sep; sep = sep->se_next)
26216374Skarels 		sep->se_checked = 0;
26316374Skarels 	while (cp = getconfigent()) {
26416374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
26516374Skarels 			if (strcmp(sep->se_service, cp->se_service) == 0 &&
26616374Skarels 			    strcmp(sep->se_proto, cp->se_proto) == 0)
26716374Skarels 				break;
26816374Skarels 		if (sep != 0) {
26916374Skarels 			int i;
27016374Skarels 
27116374Skarels 			omask = sigblock(mask(SIGCHLD));
27216374Skarels 			sep->se_wait = cp->se_wait;
27316374Skarels #define SWAP(a, b) { char *c = a; a = b; b = c; }
27416374Skarels 			if (cp->se_server)
27516374Skarels 				SWAP(sep->se_server, cp->se_server);
27616374Skarels 			for (i = 0; i < MAXARGV; i++)
27716374Skarels 				SWAP(sep->se_argv[i], cp->se_argv[i]);
27816374Skarels 			sigsetmask(omask);
27916374Skarels 			freeconfig(cp);
28016374Skarels 		} else
28116374Skarels 			sep = enter(cp);
28216374Skarels 		sep->se_checked = 1;
28316374Skarels 		if (sep->se_fd != -1)
28416374Skarels 			continue;
28516374Skarels 		sp = getservbyname(sep->se_service, sep->se_proto);
28616374Skarels 		if (sp == 0) {
28716510Sralph 			syslog(LOG_ERR, "%s/%s: unknown service",
28816374Skarels 			    sep->se_service, sep->se_proto);
28916374Skarels 			continue;
29016374Skarels 		}
29116374Skarels 		sep->se_ctrladdr.sin_port = sp->s_port;
29216374Skarels 		if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
29316510Sralph 			syslog(LOG_ERR, "%s/%s: socket: %m",
29416374Skarels 			    sep->se_service, sep->se_proto);
29516374Skarels 			continue;
29616374Skarels 		}
29717156Ssam #define	turnon(fd, opt) \
29817156Ssam 	setsockopt(fd, SOL_SOCKET, opt, &on, sizeof (on))
29916374Skarels 		if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
30017156Ssam 		    turnon(sep->se_fd, SO_DEBUG) < 0)
30116510Sralph 			syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
30217156Ssam 		if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
30316510Sralph 			syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
30417156Ssam #undef turnon
30516374Skarels 		if (bind(sep->se_fd, &sep->se_ctrladdr,
30616374Skarels 		    sizeof (sep->se_ctrladdr), 0) < 0) {
30716510Sralph 			syslog(LOG_ERR, "%s/%s: bind: %m",
30816374Skarels 			    sep->se_service, sep->se_proto);
30916374Skarels 			continue;
31016374Skarels 		}
31116374Skarels 		if (sep->se_socktype == SOCK_STREAM)
31216374Skarels 			listen(sep->se_fd, 10);
31316374Skarels 		allsock |= 1 << sep->se_fd;
31416374Skarels 	}
31516374Skarels 	endconfig();
31616374Skarels 	/*
31716374Skarels 	 * Purge anything not looked at above.
31816374Skarels 	 */
31916374Skarels 	omask = sigblock(mask(SIGCHLD));
32016374Skarels 	sepp = &servtab;
32116374Skarels 	while (sep = *sepp) {
32216374Skarels 		if (sep->se_checked) {
32316374Skarels 			sepp = &sep->se_next;
32416374Skarels 			continue;
32516374Skarels 		}
32616374Skarels 		*sepp = sep->se_next;
32716374Skarels 		if (sep->se_fd != -1) {
32816374Skarels 			allsock &= ~(1 << sep->se_fd);
32916374Skarels 			(void) close(sep->se_fd);
33016374Skarels 		}
33116374Skarels 		freeconfig(sep);
33216374Skarels 		free((char *)sep);
33316374Skarels 	}
33416374Skarels 	(void) sigsetmask(omask);
33516374Skarels }
33616374Skarels 
33716374Skarels struct servtab *
33816374Skarels enter(cp)
33916374Skarels 	struct servtab *cp;
34016374Skarels {
34116374Skarels 	register struct servtab *sep;
34216374Skarels 	int omask, i;
34316374Skarels 	char *strdup();
34416374Skarels 
34516374Skarels 	sep = (struct servtab *)malloc(sizeof (*sep));
34616374Skarels 	if (sep == (struct servtab *)0) {
34716510Sralph 		syslog(LOG_ERR, "Out of memory.");
34816374Skarels 		exit(-1);
34916374Skarels 	}
35016374Skarels 	*sep = *cp;
35116374Skarels 	sep->se_fd = -1;
35216374Skarels 	omask = sigblock(mask(SIGCHLD));
35316374Skarels 	sep->se_next = servtab;
35416374Skarels 	servtab = sep;
35516374Skarels 	sigsetmask(omask);
35616374Skarels 	return (sep);
35716374Skarels }
35816374Skarels 
35916374Skarels FILE	*fconfig = NULL;
36016374Skarels struct	servtab serv;
36116374Skarels char	line[256];
36216374Skarels char	*skip(), *nextline();
36316374Skarels 
36416374Skarels setconfig()
36516374Skarels {
36616374Skarels 
36716374Skarels 	if (fconfig != NULL) {
36816374Skarels 		fseek(fconfig, 0, L_SET);
36916374Skarels 		return (1);
37016374Skarels 	}
37116374Skarels 	fconfig = fopen(CONFIG, "r");
37216374Skarels 	return (fconfig != NULL);
37316374Skarels }
37416374Skarels 
37516374Skarels endconfig()
37616374Skarels {
37716374Skarels 
37816374Skarels 	if (fconfig == NULL)
37916374Skarels 		return;
38016374Skarels 	fclose(fconfig);
38116374Skarels 	fconfig = NULL;
38216374Skarels }
38316374Skarels 
38416374Skarels struct servtab *
38516374Skarels getconfigent()
38616374Skarels {
38716374Skarels 	register struct servtab *sep = &serv;
38816374Skarels 	char *cp, *arg;
38916374Skarels 	int argc;
39016374Skarels 
39116374Skarels 	while ((cp = nextline(fconfig)) && *cp == '#')
39216374Skarels 		;
39316374Skarels 	if (cp == NULL)
39416374Skarels 		return ((struct servtab *)0);
39516374Skarels 	sep->se_service = strdup(skip(&cp));
39616374Skarels 	arg = skip(&cp);
39716374Skarels 	if (strcmp(arg, "stream") == 0)
39816374Skarels 		sep->se_socktype = SOCK_STREAM;
39916374Skarels 	else if (strcmp(arg, "dgram") == 0)
40016374Skarels 		sep->se_socktype = SOCK_DGRAM;
40116374Skarels 	else if (strcmp(arg, "rdm") == 0)
40216374Skarels 		sep->se_socktype = SOCK_RDM;
40316374Skarels 	else if (strcmp(arg, "seqpacket") == 0)
40416374Skarels 		sep->se_socktype = SOCK_SEQPACKET;
40516374Skarels 	else if (strcmp(arg, "raw") == 0)
40616374Skarels 		sep->se_socktype = SOCK_RAW;
40716374Skarels 	else
40816374Skarels 		sep->se_socktype = -1;
40916374Skarels 	sep->se_proto = strdup(skip(&cp));
41016374Skarels 	arg = skip(&cp);
41116374Skarels 	sep->se_wait = strcmp(arg, "wait") == 0;
41217346Sbloom 	sep->se_user = strdup(skip(&cp));
41316374Skarels 	sep->se_server = strdup(skip(&cp));
41416374Skarels 	argc = 0;
41516374Skarels 	for (arg = skip(&cp); cp; arg = skip(&cp))
41616374Skarels 		if (argc < MAXARGV)
41716374Skarels 			sep->se_argv[argc++] = strdup(arg);
41816374Skarels 	while (argc <= MAXARGV)
41916374Skarels 		sep->se_argv[argc++] = NULL;
42016374Skarels 	return (sep);
42116374Skarels }
42216374Skarels 
42316374Skarels freeconfig(cp)
42416374Skarels 	register struct servtab *cp;
42516374Skarels {
42616374Skarels 	int i;
42716374Skarels 
42816374Skarels 	if (cp->se_service)
42916374Skarels 		free(cp->se_service);
43016374Skarels 	if (cp->se_proto)
43116374Skarels 		free(cp->se_proto);
43216374Skarels 	if (cp->se_server)
43316374Skarels 		free(cp->se_server);
43416374Skarels 	for (i = 0; i < MAXARGV; i++)
43516374Skarels 		if (cp->se_argv[i])
43616374Skarels 			free(cp->se_argv[i]);
43716374Skarels }
43816374Skarels 
43916374Skarels char *
44016374Skarels skip(cpp)
44116374Skarels 	char **cpp;
44216374Skarels {
44316374Skarels 	register char *cp = *cpp;
44416374Skarels 	char *start;
44516374Skarels 
44616374Skarels again:
44716374Skarels 	while (*cp == ' ' || *cp == '\t')
44816374Skarels 		cp++;
44916374Skarels 	if (*cp == '\0') {
45016374Skarels 		char c;
45116374Skarels 
45216374Skarels 		c = getc(fconfig);
45316374Skarels 		ungetc(c, fconfig);
45416374Skarels 		if (c == ' ' || c == '\t')
45516374Skarels 			if (cp = nextline(fconfig))
45616374Skarels 				goto again;
45716374Skarels 		*cpp = (char *)0;
45816374Skarels 		return ((char *)0);
45916374Skarels 	}
46016374Skarels 	start = cp;
46116374Skarels 	while (*cp && *cp != ' ' && *cp != '\t')
46216374Skarels 		cp++;
46316374Skarels 	if (*cp != '\0')
46416374Skarels 		*cp++ = '\0';
46516374Skarels 	*cpp = cp;
46616374Skarels 	return (start);
46716374Skarels }
46816374Skarels 
46916374Skarels char *
47016374Skarels nextline(fd)
47116374Skarels 	FILE *fd;
47216374Skarels {
47316374Skarels 	char *cp;
47416374Skarels 
47516374Skarels 	if (fgets(line, sizeof (line), fconfig) == NULL)
47616374Skarels 		return ((char *)0);
47716374Skarels 	cp = index(line, '\n');
47816374Skarels 	if (cp)
47916374Skarels 		*cp = '\0';
48016374Skarels 	return (line);
48116374Skarels }
48216374Skarels 
48316374Skarels char *
48416374Skarels strdup(cp)
48516374Skarels 	char *cp;
48616374Skarels {
48716374Skarels 	char *new;
48816374Skarels 
48916374Skarels 	if (cp == NULL)
49016374Skarels 		cp = "";
49116374Skarels 	new = malloc(strlen(cp) + 1);
49216374Skarels 	if (new == (char *)0) {
49316510Sralph 		syslog(LOG_ERR, "Out of memory.");
49416374Skarels 		exit(-1);
49516374Skarels 	}
49616374Skarels 	strcpy(new, cp);
49716374Skarels 	return (new);
49816374Skarels }
499