xref: /csrg-svn/usr.sbin/inetd/inetd.c (revision 16374)
1*16374Skarels #ifndef lint
2*16374Skarels static	char sccsid[] = "@(#)inetd.c	4.1 (Berkeley) 04/11/84";
3*16374Skarels #endif
4*16374Skarels 
5*16374Skarels /*
6*16374Skarels  * Inetd - Internet super-server
7*16374Skarels  *
8*16374Skarels  * This program invokes all internet services as needed.
9*16374Skarels  * connection-oriented services are invoked each time a
10*16374Skarels  * connection is made, by creating a process.  This process
11*16374Skarels  * is passed the connection as file descriptor 0 and is
12*16374Skarels  * expected to do a getpeername to find out the source host
13*16374Skarels  * and port.
14*16374Skarels  *
15*16374Skarels  * Datagram oriented services are invoked when a datagram
16*16374Skarels  * arrives; a process is created and passed a pending message
17*16374Skarels  * on file descriptor 0.  Datagram servers may either connect
18*16374Skarels  * to their peer, freeing up the original socket for inetd
19*16374Skarels  * to receive further messages on, or ``take over the socket'',
20*16374Skarels  * processing all arriving datagrams and, eventually, timing
21*16374Skarels  * out.	 The first type of server is said to be ``multi-threaded'';
22*16374Skarels  * the second type of server ``single-threaded''.
23*16374Skarels  *
24*16374Skarels  * Inetd uses a configuration file which is read at startup
25*16374Skarels  * and, possibly, at some later time in response to a hangup signal.
26*16374Skarels  * The configuration file is ``free format'' with fields given in the
27*16374Skarels  * order shown below.  Continuation lines for an entry must being with
28*16374Skarels  * a space or tab.  All fields must be present in each entry.
29*16374Skarels  *
30*16374Skarels  *	service name			must be in /etc/services
31*16374Skarels  *	socket type			stream/dgram/raw/rdm/seqpacket
32*16374Skarels  *	protocol			must be in /etc/protocols
33*16374Skarels  *	wait/nowait			single-threaded/multi-threaded
34*16374Skarels  *	server program			full path name
35*16374Skarels  *	server program arguments	maximum of MAXARGS (5)
36*16374Skarels  *
37*16374Skarels  * Comment lines are indicated by a `#' in column 1.
38*16374Skarels  */
39*16374Skarels #include <sys/param.h>
40*16374Skarels #include <sys/stat.h>
41*16374Skarels #include <sys/ioctl.h>
42*16374Skarels #include <sys/socket.h>
43*16374Skarels #include <sys/file.h>
44*16374Skarels #include <sys/wait.h>
45*16374Skarels 
46*16374Skarels #include <netinet/in.h>
47*16374Skarels #include <arpa/inet.h>
48*16374Skarels 
49*16374Skarels #include <errno.h>
50*16374Skarels #include <stdio.h>
51*16374Skarels #include <signal.h>
52*16374Skarels #include <netdb.h>
53*16374Skarels 
54*16374Skarels extern	int errno;
55*16374Skarels 
56*16374Skarels int	reapchild();
57*16374Skarels char	*index();
58*16374Skarels char	*malloc();
59*16374Skarels 
60*16374Skarels int	debug = 0;
61*16374Skarels int	allsock;
62*16374Skarels int	options;
63*16374Skarels struct	servent *sp;
64*16374Skarels 
65*16374Skarels struct	servtab {
66*16374Skarels 	char	*se_service;		/* name of service */
67*16374Skarels 	int	se_socktype;		/* type of socket to use */
68*16374Skarels 	char	*se_proto;		/* protocol used */
69*16374Skarels 	short	se_wait;		/* single threaded server */
70*16374Skarels 	short	se_checked;		/* looked at during merge */
71*16374Skarels 	char	*se_server;		/* server program */
72*16374Skarels #define MAXARGV 5
73*16374Skarels 	char	*se_argv[MAXARGV+1];	/* program arguments */
74*16374Skarels 	int	se_fd;			/* open descriptor */
75*16374Skarels 	struct	sockaddr_in se_ctrladdr;/* bound address */
76*16374Skarels 	struct	servtab *se_next;
77*16374Skarels } *servtab;
78*16374Skarels 
79*16374Skarels char	*CONFIG = "/etc/inetd.conf";
80*16374Skarels 
81*16374Skarels main(argc, argv)
82*16374Skarels 	int argc;
83*16374Skarels 	char *argv[];
84*16374Skarels {
85*16374Skarels 	int ctrl;
86*16374Skarels 	register struct servtab *sep;
87*16374Skarels 	char *cp, buf[50];
88*16374Skarels 	int pid, i;
89*16374Skarels 
90*16374Skarels 	argc--, argv++;
91*16374Skarels 	while (argc > 0 && *argv[0] == '-') {
92*16374Skarels 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
93*16374Skarels 
94*16374Skarels 		case 'd':
95*16374Skarels 			debug = 1;
96*16374Skarels 			options |= SO_DEBUG;
97*16374Skarels 			break;
98*16374Skarels 
99*16374Skarels 		default:
100*16374Skarels 			fprintf(stderr,
101*16374Skarels 			    "inetd: Unknown flag -%c ignored.\n", *cp);
102*16374Skarels 			break;
103*16374Skarels 		}
104*16374Skarels nextopt:
105*16374Skarels 		argc--, argv++;
106*16374Skarels 	}
107*16374Skarels 	if (argc > 0)
108*16374Skarels 		CONFIG = argv[0];
109*16374Skarels #ifndef DEBUG
110*16374Skarels 	if (fork())
111*16374Skarels 		exit(0);
112*16374Skarels 	{ int s;
113*16374Skarels 	for (s = 0; s < 10; s++)
114*16374Skarels 		(void) close(s);
115*16374Skarels 	}
116*16374Skarels 	(void) open("/", O_RDONLY);
117*16374Skarels 	(void) dup2(0, 1);
118*16374Skarels 	(void) dup2(0, 2);
119*16374Skarels 	{ int tt = open("/dev/tty", O_RDWR);
120*16374Skarels 	  if (tt > 0) {
121*16374Skarels 		ioctl(tt, TIOCNOTTY, 0);
122*16374Skarels 		close(tt);
123*16374Skarels 	  }
124*16374Skarels 	}
125*16374Skarels #endif
126*16374Skarels 	config();
127*16374Skarels 	signal(SIGHUP, config);
128*16374Skarels 	signal(SIGCHLD, reapchild);
129*16374Skarels 	for (;;) {
130*16374Skarels 		int readable, s, ctrl;
131*16374Skarels 
132*16374Skarels 		while (allsock == 0)
133*16374Skarels 			sigpause(0);
134*16374Skarels 		readable = allsock;
135*16374Skarels 		if (select(32, &readable, 0, 0, 0) <= 0)
136*16374Skarels 			continue;
137*16374Skarels 		s = ffs(readable)-1;
138*16374Skarels 		if (s < 0)
139*16374Skarels 			continue;
140*16374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
141*16374Skarels 			if (s == sep->se_fd)
142*16374Skarels 				goto found;
143*16374Skarels 		abort(1);
144*16374Skarels 	found:
145*16374Skarels 		if (debug)
146*16374Skarels 			fprintf(stderr, "someone wants %s\n", sep->se_service);
147*16374Skarels 		if (sep->se_socktype == SOCK_STREAM) {
148*16374Skarels 			ctrl = accept(s, 0, 0);
149*16374Skarels 		if (debug)
150*16374Skarels 			fprintf(stderr, "accept, ctrl %d\n", ctrl);
151*16374Skarels 			if (ctrl < 0) {
152*16374Skarels 				if (errno == EINTR)
153*16374Skarels 					continue;
154*16374Skarels 				perror("inetd: accept");
155*16374Skarels 				continue;
156*16374Skarels 			}
157*16374Skarels 		} else
158*16374Skarels 			ctrl = sep->se_fd;
159*16374Skarels #define mask(sig)	(1 << (sig - 1))
160*16374Skarels 		sigblock(mask(SIGCHLD)|mask(SIGHUP));
161*16374Skarels 		pid = fork();
162*16374Skarels 		if (pid < 0) {
163*16374Skarels 			if (sep->se_socktype == SOCK_STREAM)
164*16374Skarels 				close(ctrl);
165*16374Skarels 			sleep(1);
166*16374Skarels 			continue;
167*16374Skarels 		}
168*16374Skarels 		if (sep->se_wait) {
169*16374Skarels 			sep->se_wait = pid;
170*16374Skarels 			allsock &= ~(1 << s);
171*16374Skarels 		}
172*16374Skarels 		sigsetmask(0);
173*16374Skarels 		if (pid == 0) {
174*16374Skarels #ifdef	DEBUG
175*16374Skarels 			int tt = open("/dev/tty", O_RDWR);
176*16374Skarels 			if (tt > 0) {
177*16374Skarels 				ioctl(tt, TIOCNOTTY, 0);
178*16374Skarels 				close(tt);
179*16374Skarels 			}
180*16374Skarels #endif
181*16374Skarels 			dup2(ctrl, 0), close(ctrl), dup2(0, 1);
182*16374Skarels 			for (i = getdtablesize(); --i > 2; )
183*16374Skarels 				close(i);
184*16374Skarels 			if (debug)
185*16374Skarels 				fprintf(stderr, "%d execl %s\n",
186*16374Skarels 				    getpid(), sep->se_server);
187*16374Skarels 			execv(sep->se_server, sep->se_argv);
188*16374Skarels 			if (sep->se_socktype != SOCK_STREAM)
189*16374Skarels 				recv(0, buf, sizeof (buf));
190*16374Skarels 			if (debug)
191*16374Skarels 				fprintf(stderr, "execl failed\n");
192*16374Skarels 			_exit(1);
193*16374Skarels 		}
194*16374Skarels 		if (sep->se_socktype == SOCK_STREAM)
195*16374Skarels 			close(ctrl);
196*16374Skarels 	}
197*16374Skarels }
198*16374Skarels 
199*16374Skarels reapchild()
200*16374Skarels {
201*16374Skarels 	union wait status;
202*16374Skarels 	int pid;
203*16374Skarels 	register struct servtab *sep;
204*16374Skarels 
205*16374Skarels 	for (;;) {
206*16374Skarels 		pid = wait3(&status, WNOHANG, 0);
207*16374Skarels 		if (pid <= 0)
208*16374Skarels 			break;
209*16374Skarels 		if (debug)
210*16374Skarels 			fprintf(stderr, "%d reaped\n", pid);
211*16374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
212*16374Skarels 			if (sep->se_wait == pid) {
213*16374Skarels 				if (status.w_status)
214*16374Skarels 					fprintf(stderr,
215*16374Skarels 					    "inetd: %s: exit status %d\n",
216*16374Skarels 					    sep->se_server, status);
217*16374Skarels 				if (debug)
218*16374Skarels 					fprintf(stderr, "restored %s, fd %d\n",
219*16374Skarels 					    sep->se_service, sep->se_fd);
220*16374Skarels 				allsock |= 1 << sep->se_fd;
221*16374Skarels 				sep->se_wait = 1;
222*16374Skarels 			}
223*16374Skarels 	}
224*16374Skarels }
225*16374Skarels 
226*16374Skarels config()
227*16374Skarels {
228*16374Skarels 	register struct servtab *sep, *cp, **sepp;
229*16374Skarels 	struct servtab *getconfigent(), *enter();
230*16374Skarels 	int omask;
231*16374Skarels 
232*16374Skarels 	if (!setconfig()) {
233*16374Skarels 		fprintf(stderr, "inetd: ");
234*16374Skarels 		perror(CONFIG);
235*16374Skarels 		return;
236*16374Skarels 	}
237*16374Skarels 	for (sep = servtab; sep; sep = sep->se_next)
238*16374Skarels 		sep->se_checked = 0;
239*16374Skarels 	while (cp = getconfigent()) {
240*16374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
241*16374Skarels 			if (strcmp(sep->se_service, cp->se_service) == 0 &&
242*16374Skarels 			    strcmp(sep->se_proto, cp->se_proto) == 0)
243*16374Skarels 				break;
244*16374Skarels 		if (sep != 0) {
245*16374Skarels 			int i;
246*16374Skarels 
247*16374Skarels 			omask = sigblock(mask(SIGCHLD));
248*16374Skarels 			sep->se_wait = cp->se_wait;
249*16374Skarels #define SWAP(a, b) { char *c = a; a = b; b = c; }
250*16374Skarels 			if (cp->se_server)
251*16374Skarels 				SWAP(sep->se_server, cp->se_server);
252*16374Skarels 			for (i = 0; i < MAXARGV; i++)
253*16374Skarels 				SWAP(sep->se_argv[i], cp->se_argv[i]);
254*16374Skarels 			sigsetmask(omask);
255*16374Skarels 			freeconfig(cp);
256*16374Skarels 		} else
257*16374Skarels 			sep = enter(cp);
258*16374Skarels 		sep->se_checked = 1;
259*16374Skarels 		if (sep->se_fd != -1)
260*16374Skarels 			continue;
261*16374Skarels 		sp = getservbyname(sep->se_service, sep->se_proto);
262*16374Skarels 		if (sp == 0) {
263*16374Skarels 			fprintf(stderr,
264*16374Skarels 			    "inetd: %s/%s: unknown service\n",
265*16374Skarels 			    sep->se_service, sep->se_proto);
266*16374Skarels 			continue;
267*16374Skarels 		}
268*16374Skarels 		sep->se_ctrladdr.sin_port = sp->s_port;
269*16374Skarels 		if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
270*16374Skarels 			fprintf(stderr, "inetd: %s/%s: ",
271*16374Skarels 			    sep->se_service, sep->se_proto);
272*16374Skarels 			perror("socket");
273*16374Skarels 			continue;
274*16374Skarels 		}
275*16374Skarels 		if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
276*16374Skarels 		    setsockopt(sep->se_fd, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
277*16374Skarels 			perror("inetd: setsockopt (SO_DEBUG)");
278*16374Skarels 		if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
279*16374Skarels 			perror("inetd: setsockopt (SO_REUSEADDR)");
280*16374Skarels 		if (bind(sep->se_fd, &sep->se_ctrladdr,
281*16374Skarels 		    sizeof (sep->se_ctrladdr), 0) < 0) {
282*16374Skarels 			fprintf(stderr, "inetd: %s/%s: ",
283*16374Skarels 			    sep->se_service, sep->se_proto);
284*16374Skarels 			perror("bind");
285*16374Skarels 			continue;
286*16374Skarels 		}
287*16374Skarels 		if (sep->se_socktype == SOCK_STREAM)
288*16374Skarels 			listen(sep->se_fd, 10);
289*16374Skarels 		allsock |= 1 << sep->se_fd;
290*16374Skarels 	}
291*16374Skarels 	endconfig();
292*16374Skarels 	/*
293*16374Skarels 	 * Purge anything not looked at above.
294*16374Skarels 	 */
295*16374Skarels 	omask = sigblock(mask(SIGCHLD));
296*16374Skarels 	sepp = &servtab;
297*16374Skarels 	while (sep = *sepp) {
298*16374Skarels 		if (sep->se_checked) {
299*16374Skarels 			sepp = &sep->se_next;
300*16374Skarels 			continue;
301*16374Skarels 		}
302*16374Skarels 		*sepp = sep->se_next;
303*16374Skarels 		if (sep->se_fd != -1) {
304*16374Skarels 			allsock &= ~(1 << sep->se_fd);
305*16374Skarels 			(void) close(sep->se_fd);
306*16374Skarels 		}
307*16374Skarels 		freeconfig(sep);
308*16374Skarels 		free((char *)sep);
309*16374Skarels 	}
310*16374Skarels 	(void) sigsetmask(omask);
311*16374Skarels }
312*16374Skarels 
313*16374Skarels struct servtab *
314*16374Skarels enter(cp)
315*16374Skarels 	struct servtab *cp;
316*16374Skarels {
317*16374Skarels 	register struct servtab *sep;
318*16374Skarels 	int omask, i;
319*16374Skarels 	char *strdup();
320*16374Skarels 
321*16374Skarels 	sep = (struct servtab *)malloc(sizeof (*sep));
322*16374Skarels 	if (sep == (struct servtab *)0) {
323*16374Skarels 		fprintf(stderr, "Out of memory.\n");
324*16374Skarels 		exit(-1);
325*16374Skarels 	}
326*16374Skarels 	*sep = *cp;
327*16374Skarels 	sep->se_fd = -1;
328*16374Skarels 	omask = sigblock(mask(SIGCHLD));
329*16374Skarels 	sep->se_next = servtab;
330*16374Skarels 	servtab = sep;
331*16374Skarels 	sigsetmask(omask);
332*16374Skarels 	return (sep);
333*16374Skarels }
334*16374Skarels 
335*16374Skarels FILE	*fconfig = NULL;
336*16374Skarels struct	servtab serv;
337*16374Skarels char	line[256];
338*16374Skarels char	*skip(), *nextline();
339*16374Skarels 
340*16374Skarels setconfig()
341*16374Skarels {
342*16374Skarels 
343*16374Skarels 	if (fconfig != NULL) {
344*16374Skarels 		fseek(fconfig, 0, L_SET);
345*16374Skarels 		return (1);
346*16374Skarels 	}
347*16374Skarels 	fconfig = fopen(CONFIG, "r");
348*16374Skarels 	return (fconfig != NULL);
349*16374Skarels }
350*16374Skarels 
351*16374Skarels endconfig()
352*16374Skarels {
353*16374Skarels 
354*16374Skarels 	if (fconfig == NULL)
355*16374Skarels 		return;
356*16374Skarels 	fclose(fconfig);
357*16374Skarels 	fconfig = NULL;
358*16374Skarels }
359*16374Skarels 
360*16374Skarels struct servtab *
361*16374Skarels getconfigent()
362*16374Skarels {
363*16374Skarels 	register struct servtab *sep = &serv;
364*16374Skarels 	char *cp, *arg;
365*16374Skarels 	int argc;
366*16374Skarels 
367*16374Skarels 	while ((cp = nextline(fconfig)) && *cp == '#')
368*16374Skarels 		;
369*16374Skarels 	if (cp == NULL)
370*16374Skarels 		return ((struct servtab *)0);
371*16374Skarels 	sep->se_service = strdup(skip(&cp));
372*16374Skarels 	arg = skip(&cp);
373*16374Skarels 	if (strcmp(arg, "stream") == 0)
374*16374Skarels 		sep->se_socktype = SOCK_STREAM;
375*16374Skarels 	else if (strcmp(arg, "dgram") == 0)
376*16374Skarels 		sep->se_socktype = SOCK_DGRAM;
377*16374Skarels 	else if (strcmp(arg, "rdm") == 0)
378*16374Skarels 		sep->se_socktype = SOCK_RDM;
379*16374Skarels 	else if (strcmp(arg, "seqpacket") == 0)
380*16374Skarels 		sep->se_socktype = SOCK_SEQPACKET;
381*16374Skarels 	else if (strcmp(arg, "raw") == 0)
382*16374Skarels 		sep->se_socktype = SOCK_RAW;
383*16374Skarels 	else
384*16374Skarels 		sep->se_socktype = -1;
385*16374Skarels 	sep->se_proto = strdup(skip(&cp));
386*16374Skarels 	arg = skip(&cp);
387*16374Skarels 	sep->se_wait = strcmp(arg, "wait") == 0;
388*16374Skarels 	sep->se_server = strdup(skip(&cp));
389*16374Skarels 	argc = 0;
390*16374Skarels 	for (arg = skip(&cp); cp; arg = skip(&cp))
391*16374Skarels 		if (argc < MAXARGV)
392*16374Skarels 			sep->se_argv[argc++] = strdup(arg);
393*16374Skarels 	while (argc <= MAXARGV)
394*16374Skarels 		sep->se_argv[argc++] = NULL;
395*16374Skarels 	return (sep);
396*16374Skarels }
397*16374Skarels 
398*16374Skarels freeconfig(cp)
399*16374Skarels 	register struct servtab *cp;
400*16374Skarels {
401*16374Skarels 	int i;
402*16374Skarels 
403*16374Skarels 	if (cp->se_service)
404*16374Skarels 		free(cp->se_service);
405*16374Skarels 	if (cp->se_proto)
406*16374Skarels 		free(cp->se_proto);
407*16374Skarels 	if (cp->se_server)
408*16374Skarels 		free(cp->se_server);
409*16374Skarels 	for (i = 0; i < MAXARGV; i++)
410*16374Skarels 		if (cp->se_argv[i])
411*16374Skarels 			free(cp->se_argv[i]);
412*16374Skarels }
413*16374Skarels 
414*16374Skarels char *
415*16374Skarels skip(cpp)
416*16374Skarels 	char **cpp;
417*16374Skarels {
418*16374Skarels 	register char *cp = *cpp;
419*16374Skarels 	char *start;
420*16374Skarels 
421*16374Skarels again:
422*16374Skarels 	while (*cp == ' ' || *cp == '\t')
423*16374Skarels 		cp++;
424*16374Skarels 	if (*cp == '\0') {
425*16374Skarels 		char c;
426*16374Skarels 
427*16374Skarels 		c = getc(fconfig);
428*16374Skarels 		ungetc(c, fconfig);
429*16374Skarels 		if (c == ' ' || c == '\t')
430*16374Skarels 			if (cp = nextline(fconfig))
431*16374Skarels 				goto again;
432*16374Skarels 		*cpp = (char *)0;
433*16374Skarels 		return ((char *)0);
434*16374Skarels 	}
435*16374Skarels 	start = cp;
436*16374Skarels 	while (*cp && *cp != ' ' && *cp != '\t')
437*16374Skarels 		cp++;
438*16374Skarels 	if (*cp != '\0')
439*16374Skarels 		*cp++ = '\0';
440*16374Skarels 	*cpp = cp;
441*16374Skarels 	return (start);
442*16374Skarels }
443*16374Skarels 
444*16374Skarels char *
445*16374Skarels nextline(fd)
446*16374Skarels 	FILE *fd;
447*16374Skarels {
448*16374Skarels 	char *cp;
449*16374Skarels 
450*16374Skarels 	if (fgets(line, sizeof (line), fconfig) == NULL)
451*16374Skarels 		return ((char *)0);
452*16374Skarels 	cp = index(line, '\n');
453*16374Skarels 	if (cp)
454*16374Skarels 		*cp = '\0';
455*16374Skarels 	return (line);
456*16374Skarels }
457*16374Skarels 
458*16374Skarels char *
459*16374Skarels strdup(cp)
460*16374Skarels 	char *cp;
461*16374Skarels {
462*16374Skarels 	char *new;
463*16374Skarels 
464*16374Skarels 	if (cp == NULL)
465*16374Skarels 		cp = "";
466*16374Skarels 	new = malloc(strlen(cp) + 1);
467*16374Skarels 	if (new == (char *)0) {
468*16374Skarels 		fprintf(stderr, "Out of memory.\n");
469*16374Skarels 		exit(-1);
470*16374Skarels 	}
471*16374Skarels 	strcpy(new, cp);
472*16374Skarels 	return (new);
473*16374Skarels }
474