inetd.c revision 21640
1/*
2 * Copyright (c) 1983, 1991, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char copyright[] __attribute__ ((unused)) =
36"@(#) Copyright (c) 1983, 1991, 1993, 1994\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41/* from: @(#)inetd.c	8.4 (Berkeley) 4/13/94"; */
42static char inetd_c_rcsid[] __attribute__ ((unused)) =
43	"$Id: inetd.c,v 1.17 1996/11/10 21:12:44 julian Exp $";
44#endif /* not lint */
45
46/*
47 * Inetd - Internet super-server
48 *
49 * This program invokes all internet services as needed.  Connection-oriented
50 * services are invoked each time a connection is made, by creating a process.
51 * This process is passed the connection as file descriptor 0 and is expected
52 * to do a getpeername to find out the source host and port.
53 *
54 * Datagram oriented services are invoked when a datagram
55 * arrives; a process is created and passed a pending message
56 * on file descriptor 0.  Datagram servers may either connect
57 * to their peer, freeing up the original socket for inetd
58 * to receive further messages on, or ``take over the socket'',
59 * processing all arriving datagrams and, eventually, timing
60 * out.	 The first type of server is said to be ``multi-threaded'';
61 * the second type of server ``single-threaded''.
62 *
63 * Inetd uses a configuration file which is read at startup
64 * and, possibly, at some later time in response to a hangup signal.
65 * The configuration file is ``free format'' with fields given in the
66 * order shown below.  Continuation lines for an entry must being with
67 * a space or tab.  All fields must be present in each entry.
68 *
69 *	service name			must be in /etc/services or must
70 *					name a tcpmux service
71 *	socket type			stream/dgram/raw/rdm/seqpacket
72 *	protocol			must be in /etc/protocols
73 *	wait/nowait			single-threaded/multi-threaded
74 *	user				user to run daemon as
75 *	server program			full path name
76 *	server program arguments	maximum of MAXARGS (20)
77 *
78 * TCP services without official port numbers are handled with the
79 * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
80 * requests. When a connection is made from a foreign host, the service
81 * requested is passed to tcpmux, which looks it up in the servtab list
82 * and returns the proper entry for the service. Tcpmux returns a
83 * negative reply if the service doesn't exist, otherwise the invoked
84 * server is expected to return the positive reply if the service type in
85 * inetd.conf file has the prefix "tcpmux/". If the service type has the
86 * prefix "tcpmux/+", tcpmux will return the positive reply for the
87 * process; this is for compatibility with older server code, and also
88 * allows you to invoke programs that use stdin/stdout without putting any
89 * special server code in them. Services that use tcpmux are "nowait"
90 * because they do not have a well-known port and hence cannot listen
91 * for new requests.
92 *
93 * For RPC services
94 *	service name/version		must be in /etc/rpc
95 *	socket type			stream/dgram/raw/rdm/seqpacket
96 *	protocol			must be in /etc/protocols
97 *	wait/nowait			single-threaded/multi-threaded
98 *	user				user to run daemon as
99 *	server program			full path name
100 *	server program arguments	maximum of MAXARGS
101 *
102 * Comment lines are indicated by a `#' in column 1.
103 */
104#include <sys/param.h>
105#include <sys/stat.h>
106#include <sys/ioctl.h>
107#include <sys/socket.h>
108#include <sys/wait.h>
109#include <sys/time.h>
110#include <sys/resource.h>
111
112#include <netinet/in.h>
113#include <arpa/inet.h>
114#include <rpc/rpc.h>
115#include <rpc/pmap_clnt.h>
116
117#include <errno.h>
118#include <fcntl.h>
119#include <netdb.h>
120#include <pwd.h>
121#include <signal.h>
122#include <stdio.h>
123#include <stdlib.h>
124#include <string.h>
125#include <syslog.h>
126#include <unistd.h>
127#include <libutil.h>
128#include <sysexits.h>
129
130#ifdef LOGIN_CAP
131#undef AUTH_NONE	/* conflicts with rpc stuff */
132#include <login_cap.h>
133#endif
134
135#include "pathnames.h"
136
137#define	TOOMANY		256		/* don't start more than TOOMANY */
138#define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
139#define	RETRYTIME	(60*10)		/* retry after bind or server fail */
140#define MAX_MAXCHLD	32767		/* max allowable max children */
141
142#define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
143
144int	debug = 0;
145int	log = 0;
146int	nsock, maxsock;
147fd_set	allsock;
148int	options;
149int	timingout;
150int	toomany = TOOMANY;
151struct	servent *sp;
152struct	rpcent *rpc;
153struct	in_addr bind_address;
154
155struct	servtab {
156	char	*se_service;		/* name of service */
157	int	se_socktype;		/* type of socket to use */
158	char	*se_proto;		/* protocol used */
159	short	se_maxchild;		/* max number of children */
160	short	se_numchild;		/* current number of children */
161	pid_t	*se_pids;		/* array of child pids */
162	char	*se_user;		/* user name to run as */
163	struct	biltin *se_bi;		/* if built-in, description */
164	char	*se_server;		/* server program */
165#define	MAXARGV 20
166	char	*se_argv[MAXARGV+1];	/* program arguments */
167	int	se_fd;			/* open descriptor */
168	struct	sockaddr_in se_ctrladdr;/* bound address */
169	u_char	se_type;		/* type: normal, mux, or mux+ */
170	u_char	se_checked;		/* looked at during merge */
171	u_char	se_accept;		/* i.e., wait/nowait mode */
172	u_char	se_rpc;			/* ==1 if RPC service */
173	int	se_rpc_prog;		/* RPC program number */
174	u_int	se_rpc_lowvers;		/* RPC low version */
175	u_int	se_rpc_highvers;	/* RPC high version */
176	int	se_count;		/* number started since se_time */
177	struct	timeval se_time;	/* start of se_count */
178	struct	servtab *se_next;
179} *servtab;
180
181#define NORM_TYPE	0
182#define MUX_TYPE	1
183#define MUXPLUS_TYPE	2
184#define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
185			 ((sep)->se_type == MUXPLUS_TYPE))
186#define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
187
188
189void		chargen_dg __P((int, struct servtab *));
190void		chargen_stream __P((int, struct servtab *));
191void		close_sep __P((struct servtab *));
192void		config __P((int));
193void		daytime_dg __P((int, struct servtab *));
194void		daytime_stream __P((int, struct servtab *));
195void		discard_dg __P((int, struct servtab *));
196void		discard_stream __P((int, struct servtab *));
197void		echo_dg __P((int, struct servtab *));
198void		echo_stream __P((int, struct servtab *));
199void		endconfig __P((void));
200struct servtab *enter __P((struct servtab *));
201void		freeconfig __P((struct servtab *));
202struct servtab *getconfigent __P((void));
203void		machtime_dg __P((int, struct servtab *));
204void		machtime_stream __P((int, struct servtab *));
205char	       *newstr __P((char *));
206char	       *nextline __P((FILE *));
207void		print_service __P((char *, struct servtab *));
208void		addchild __P((struct servtab *, int));
209void		reapchild __P((int));
210void		enable __P((struct servtab *));
211void		disable __P((struct servtab *));
212void		retry __P((int));
213int		setconfig __P((void));
214void		setup __P((struct servtab *));
215char	       *sskip __P((char **));
216char	       *skip __P((char **));
217struct servtab *tcpmux __P((int));
218
219void		unregisterrpc __P((register struct servtab *sep));
220
221struct biltin {
222	char	*bi_service;		/* internally provided service name */
223	int	bi_socktype;		/* type of socket supported */
224	short	bi_fork;		/* 1 if should fork before call */
225	short	bi_maxchild;		/* max number of children (default) */
226	void	(*bi_fn)();		/* function which performs it */
227} biltins[] = {
228	/* Echo received data */
229	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream },
230	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg },
231
232	/* Internet /dev/null */
233	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream },
234	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg },
235
236	/* Return 32 bit time since 1970 */
237	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream },
238	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg },
239
240	/* Return human-readable time */
241	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream },
242	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg },
243
244	/* Familiar character generator */
245	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream },
246	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg },
247
248	{ "tcpmux",	SOCK_STREAM,	1, 0,	(void (*)())tcpmux },
249
250	{ NULL }
251};
252
253#define NUMINT	(sizeof(intab) / sizeof(struct inent))
254char	*CONFIG = _PATH_INETDCONF;
255char	*pid_file = _PATH_INETDPID;
256
257#ifdef OLD_SETPROCTITLE
258char	**Argv;
259char 	*LastArg;
260#endif
261
262int
263main(argc, argv, envp)
264	int argc;
265	char *argv[], *envp[];
266{
267	struct servtab *sep;
268	struct passwd *pwd;
269	struct sigvec sv;
270	int tmpint, ch, dofork;
271	pid_t pid;
272	char buf[50];
273	struct  sockaddr_in peer;
274	int i;
275#ifdef LOGIN_CAP
276	login_cap_t *lc = NULL;
277#endif
278
279
280#ifdef OLD_SETPROCTITLE
281	Argv = argv;
282	if (envp == 0 || *envp == 0)
283		envp = argv;
284	while (*envp)
285		envp++;
286	LastArg = envp[-1] + strlen(envp[-1]);
287#endif
288
289	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
290
291	bind_address.s_addr = htonl(INADDR_ANY);
292	while ((ch = getopt(argc, argv, "dlR:a:p:")) != EOF)
293		switch(ch) {
294		case 'd':
295			debug = 1;
296			options |= SO_DEBUG;
297			break;
298		case 'l':
299			log = 1;
300			break;
301		case 'R': {	/* invocation rate */
302			char *p;
303
304			tmpint = strtol(optarg, &p, 0);
305			if (tmpint < 1 || *p)
306				syslog(LOG_ERR,
307			         "-R %s: bad value for service invocation rate",
308					optarg);
309			else
310				toomany = tmpint;
311			break;
312		}
313		case 'a':
314			if (!inet_aton(optarg, &bind_address)) {
315				syslog(LOG_ERR,
316			         "-a %s: invalid IP address", optarg);
317				exit(EX_USAGE);
318			}
319			break;
320		case 'p':
321			pid_file = optarg;
322			break;
323		case '?':
324		default:
325			syslog(LOG_ERR,
326				"usage: inetd [-dl] [-a address] [-R rate]"
327				" [-p pidfile] [conf-file]");
328			exit(EX_USAGE);
329		}
330	argc -= optind;
331	argv += optind;
332
333	if (argc > 0)
334		CONFIG = argv[0];
335	if (debug == 0) {
336		FILE *fp;
337		if (daemon(0, 0) < 0) {
338			syslog(LOG_WARNING, "daemon(0,0) failed: %m");
339		}
340		/*
341		 * In case somebody has started inetd manually, we need to
342		 * clear the logname, so that old servers run as root do not
343		 * get the user's logname..
344		 */
345		if (setlogin("") < 0) {
346			syslog(LOG_WARNING, "cannot clear logname: %m");
347			/* no big deal if it fails.. */
348		}
349		pid = getpid();
350		fp = fopen(pid_file, "w");
351		if (fp) {
352			fprintf(fp, "%ld\n", (long)pid);
353			fclose(fp);
354		} else {
355			syslog(LOG_WARNING, "%s: %m", pid_file);
356		}
357	}
358	memset(&sv, 0, sizeof(sv));
359	sv.sv_mask = SIGBLOCK;
360	sv.sv_handler = retry;
361	sigvec(SIGALRM, &sv, (struct sigvec *)0);
362	config(SIGHUP);
363	sv.sv_handler = config;
364	sigvec(SIGHUP, &sv, (struct sigvec *)0);
365	sv.sv_handler = reapchild;
366	sigvec(SIGCHLD, &sv, (struct sigvec *)0);
367
368	{
369		/* space for daemons to overwrite environment for ps */
370#define	DUMMYSIZE	100
371		char dummy[DUMMYSIZE];
372
373		(void)memset(dummy, 'x', DUMMYSIZE - 1);
374		dummy[DUMMYSIZE - 1] = '\0';
375		(void)setenv("inetd_dummy", dummy, 1);
376	}
377
378	for (;;) {
379	    int n, ctrl;
380	    fd_set readable;
381
382	    if (nsock == 0) {
383		(void) sigblock(SIGBLOCK);
384		while (nsock == 0)
385		    sigpause(0L);
386		(void) sigsetmask(0L);
387	    }
388	    readable = allsock;
389	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
390		(fd_set *)0, (struct timeval *)0)) <= 0) {
391		    if (n < 0 && errno != EINTR)
392			syslog(LOG_WARNING, "select: %m");
393		    sleep(1);
394		    continue;
395	    }
396	    for (sep = servtab; n && sep; sep = sep->se_next)
397	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
398		    n--;
399		    if (debug)
400			    fprintf(stderr, "someone wants %s\n",
401				sep->se_service);
402		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
403			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
404				(int *)0);
405			    if (debug)
406				    fprintf(stderr, "accept, ctrl %d\n", ctrl);
407			    if (ctrl < 0) {
408				    if (errno != EINTR)
409					    syslog(LOG_WARNING,
410						"accept (for %s): %m",
411						sep->se_service);
412				    continue;
413			    }
414			    if (log) {
415				i = sizeof peer;
416				if(getpeername(ctrl, (struct sockaddr *)
417						&peer, &i)) {
418					syslog(LOG_WARNING,
419						"getpeername(for %s): %m",
420						sep->se_service);
421					continue;
422				}
423				syslog(LOG_INFO,"%s from %s",
424					sep->se_service,
425					inet_ntoa(peer.sin_addr));
426			    }
427			    /*
428			     * Call tcpmux to find the real service to exec.
429			     */
430			    if (sep->se_bi &&
431				sep->se_bi->bi_fn == (void (*)()) tcpmux) {
432				    struct servtab *tsep;
433
434				    tsep = tcpmux(ctrl);
435				    if (tsep == NULL) {
436					    close(ctrl);
437					    continue;
438				    }
439				    sep = tsep;
440			    }
441		    } else
442			    ctrl = sep->se_fd;
443		    (void) sigblock(SIGBLOCK);
444		    pid = 0;
445		    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
446		    if (dofork) {
447			    if (sep->se_count++ == 0)
448				(void)gettimeofday(&sep->se_time,
449				    (struct timezone *)0);
450			    else if (sep->se_count >= toomany) {
451				struct timeval now;
452
453				(void)gettimeofday(&now, (struct timezone *)0);
454				if (now.tv_sec - sep->se_time.tv_sec >
455				    CNT_INTVL) {
456					sep->se_time = now;
457					sep->se_count = 1;
458				} else {
459					syslog(LOG_ERR,
460			"%s/%s server failing (looping), service terminated",
461					    sep->se_service, sep->se_proto);
462					close_sep(sep);
463					sigsetmask(0L);
464					if (!timingout) {
465						timingout = 1;
466						alarm(RETRYTIME);
467					}
468					continue;
469				}
470			    }
471			    pid = fork();
472		    }
473		    if (pid < 0) {
474			    syslog(LOG_ERR, "fork: %m");
475			    if (sep->se_accept &&
476				sep->se_socktype == SOCK_STREAM)
477				    close(ctrl);
478			    sigsetmask(0L);
479			    sleep(1);
480			    continue;
481		    }
482		    if (pid)
483			addchild(sep, pid);
484		    sigsetmask(0L);
485		    if (pid == 0) {
486			    if (dofork) {
487				if (debug)
488					fprintf(stderr, "+ Closing from %d\n",
489						maxsock);
490				for (tmpint = maxsock; tmpint > 2; tmpint--)
491					if (tmpint != ctrl)
492						(void) close(tmpint);
493			    }
494			    if (sep->se_bi) {
495				(*sep->se_bi->bi_fn)(ctrl, sep);
496				/* NOTREACHED */
497			    } else {
498				if (debug)
499					fprintf(stderr, "%d execl %s\n",
500					    getpid(), sep->se_server);
501				dup2(ctrl, 0);
502				close(ctrl);
503				dup2(0, 1);
504				dup2(0, 2);
505				if ((pwd = getpwnam(sep->se_user)) == NULL) {
506					syslog(LOG_ERR,
507					    "%s/%s: %s: No such user",
508						sep->se_service, sep->se_proto,
509						sep->se_user);
510					if (sep->se_socktype != SOCK_STREAM)
511						recv(0, buf, sizeof (buf), 0);
512					_exit(EX_NOUSER);
513				}
514#ifdef LOGIN_CAP
515				/*
516				 * Establish the class now, falls back to
517				 * the "default" if unavailable.
518				 */
519				lc = login_getclass(pwd);
520#endif
521				if (setsid() < 0) {
522					syslog(LOG_ERR,
523						"%s: can't setsid(): %m",
524						 sep->se_service);
525					/* _exit(EX_OSERR); not fatal yet */
526				}
527#ifdef LOGIN_CAP
528				if (setusercontext(lc, pwd, pwd->pw_uid,
529				    LOGIN_SETALL) != 0) {
530					syslog(LOG_ERR,
531					 "%s: can't setusercontext(..%s..): %m",
532					 sep->se_service, sep->se_user);
533					_exit(EX_OSERR);
534				}
535#else
536				if (pwd->pw_uid) {
537					if (setlogin(sep->se_user) < 0) {
538						syslog(LOG_ERR,
539						 "%s: can't setlogin(%s): %m",
540						 sep->se_service, sep->se_user);
541						/* _exit(EX_OSERR); not yet */
542					}
543					if (setgid(pwd->pw_gid) < 0) {
544						syslog(LOG_ERR,
545						  "%s: can't set gid %d: %m",
546						  sep->se_service, pwd->pw_gid);
547						_exit(EX_OSERR);
548					}
549					(void) initgroups(pwd->pw_name,
550							pwd->pw_gid);
551					if (setuid(pwd->pw_uid) < 0) {
552						syslog(LOG_ERR,
553						  "%s: can't set uid %d: %m",
554						  sep->se_service, pwd->pw_uid);
555						_exit(EX_OSERR);
556					}
557				}
558#endif
559				execv(sep->se_server, sep->se_argv);
560				if (sep->se_socktype != SOCK_STREAM)
561					recv(0, buf, sizeof (buf), 0);
562				syslog(LOG_ERR,
563				    "cannot execute %s: %m", sep->se_server);
564				_exit(EX_OSERR);
565			    }
566		    }
567		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
568			    close(ctrl);
569		}
570	}
571}
572
573/*
574 * Record a new child pid for this service. If we've reached the
575 * limit on children, then stop accepting incoming requests.
576 */
577
578void
579addchild(struct servtab *sep, pid_t pid)
580{
581#ifdef SANITY_CHECK
582	if (sep->se_numchild >= sep->se_maxchild) {
583		syslog(LOG_ERR, "%s: %d >= %d",
584		    __FUNCTION__, sep->se_numchild, sep->se_maxchild);
585		exit(EX_SOFTWARE);
586	}
587#endif
588	if (sep->se_maxchild == 0)
589		return;
590	sep->se_pids[sep->se_numchild++] = pid;
591	if (sep->se_numchild == sep->se_maxchild)
592		disable(sep);
593}
594
595/*
596 * Some child process has exited. See if it's on somebody's list.
597 */
598
599void
600reapchild(signo)
601	int signo;
602{
603	int k, status;
604	pid_t pid;
605	struct servtab *sep;
606
607	for (;;) {
608		pid = wait3(&status, WNOHANG, (struct rusage *)0);
609		if (pid <= 0)
610			break;
611		if (debug)
612			fprintf(stderr, "%d reaped, status %#x\n",
613				pid, status);
614		for (sep = servtab; sep; sep = sep->se_next) {
615			for (k = 0; k < sep->se_numchild; k++)
616				if (sep->se_pids[k] == pid)
617					break;
618			if (k == sep->se_numchild)
619				continue;
620			if (sep->se_numchild == sep->se_maxchild)
621				enable(sep);
622			sep->se_pids[k] = sep->se_pids[--sep->se_numchild];
623			if (status)
624				syslog(LOG_WARNING,
625				    "%s[%d]: exit status 0x%x",
626				    sep->se_server, pid, status);
627			break;
628		}
629	}
630}
631
632void
633config(signo)
634	int signo;
635{
636	struct servtab *sep, *new, **sepp;
637	struct passwd *pwd;
638	long omask;
639
640	if (!setconfig()) {
641		syslog(LOG_ERR, "%s: %m", CONFIG);
642		return;
643	}
644	for (sep = servtab; sep; sep = sep->se_next)
645		sep->se_checked = 0;
646	while ((new = getconfigent())) {
647		if ((pwd = getpwnam(new->se_user)) == NULL) {
648			syslog(LOG_ERR,
649				"%s/%s: No such user '%s', service ignored",
650				new->se_service, new->se_proto, new->se_user);
651			continue;
652		}
653		for (sep = servtab; sep; sep = sep->se_next)
654			if (strcmp(sep->se_service, new->se_service) == 0 &&
655			    strcmp(sep->se_proto, new->se_proto) == 0)
656				break;
657		if (sep != 0) {
658			int i;
659
660#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; }
661			omask = sigblock(SIGBLOCK);
662			/* copy over outstanding child pids */
663			if (sep->se_maxchild && new->se_maxchild) {
664				new->se_numchild = sep->se_numchild;
665				if (new->se_numchild > new->se_maxchild)
666					new->se_numchild = new->se_maxchild;
667				memcpy(new->se_pids, sep->se_pids,
668				    new->se_numchild * sizeof(*new->se_pids));
669			}
670			SWAP(sep->se_pids, new->se_pids);
671			sep->se_maxchild = new->se_maxchild;
672			sep->se_numchild = new->se_numchild;
673			/* might need to turn on or off service now */
674			if (sep->se_fd >= 0) {
675			      if (sep->se_maxchild
676				  && sep->se_numchild == sep->se_maxchild) {
677				      if (FD_ISSET(sep->se_fd, &allsock))
678					  disable(sep);
679			      } else {
680				      if (!FD_ISSET(sep->se_fd, &allsock))
681					  enable(sep);
682			      }
683			}
684			sep->se_accept = new->se_accept;
685			if (new->se_user)
686				SWAP(sep->se_user, new->se_user);
687			if (new->se_server)
688				SWAP(sep->se_server, new->se_server);
689			for (i = 0; i < MAXARGV; i++)
690				SWAP(sep->se_argv[i], new->se_argv[i]);
691			sigsetmask(omask);
692			freeconfig(new);
693			if (debug)
694				print_service("REDO", sep);
695		} else {
696			sep = enter(new);
697			if (debug)
698				print_service("ADD ", sep);
699		}
700		sep->se_checked = 1;
701		if (ISMUX(sep)) {
702			sep->se_fd = -1;
703			continue;
704		}
705		if (!sep->se_rpc) {
706			sp = getservbyname(sep->se_service, sep->se_proto);
707			if (sp == 0) {
708				syslog(LOG_ERR, "%s/%s: unknown service",
709			    	sep->se_service, sep->se_proto);
710				sep->se_checked = 0;
711				continue;
712			}
713			if (sp->s_port != sep->se_ctrladdr.sin_port) {
714				sep->se_ctrladdr.sin_family = AF_INET;
715				sep->se_ctrladdr.sin_port = sp->s_port;
716				if (sep->se_fd >= 0)
717					close_sep(sep);
718			}
719		} else {
720			rpc = getrpcbyname(sep->se_service);
721			if (rpc == 0) {
722				syslog(LOG_ERR, "%s/%s unknown RPC service.",
723					sep->se_service, sep->se_proto);
724				if (sep->se_fd != -1)
725					(void) close(sep->se_fd);
726				sep->se_fd = -1;
727					continue;
728			}
729			if (rpc->r_number != sep->se_rpc_prog) {
730				if (sep->se_rpc_prog)
731					unregisterrpc(sep);
732				sep->se_rpc_prog = rpc->r_number;
733				if (sep->se_fd != -1)
734					(void) close(sep->se_fd);
735				sep->se_fd = -1;
736			}
737		}
738		if (sep->se_fd == -1)
739			setup(sep);
740	}
741	endconfig();
742	/*
743	 * Purge anything not looked at above.
744	 */
745	omask = sigblock(SIGBLOCK);
746	sepp = &servtab;
747	while ((sep = *sepp)) {
748		if (sep->se_checked) {
749			sepp = &sep->se_next;
750			continue;
751		}
752		*sepp = sep->se_next;
753		if (sep->se_fd >= 0)
754			close_sep(sep);
755		if (debug)
756			print_service("FREE", sep);
757		if (sep->se_rpc && sep->se_rpc_prog > 0)
758			unregisterrpc(sep);
759		freeconfig(sep);
760		free((char *)sep);
761	}
762	(void) sigsetmask(omask);
763}
764
765void
766unregisterrpc(sep)
767	struct servtab *sep;
768{
769        int i;
770        struct servtab *sepp;
771	long omask;
772
773	omask = sigblock(SIGBLOCK);
774        for (sepp = servtab; sepp; sepp = sepp->se_next) {
775                if (sepp == sep)
776                        continue;
777		if (sep->se_checked == 0 ||
778                    !sepp->se_rpc ||
779                    sep->se_rpc_prog != sepp->se_rpc_prog)
780			continue;
781                return;
782        }
783        if (debug)
784                print_service("UNREG", sep);
785        for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++)
786                pmap_unset(sep->se_rpc_prog, i);
787        if (sep->se_fd != -1)
788                (void) close(sep->se_fd);
789        sep->se_fd = -1;
790	(void) sigsetmask(omask);
791}
792
793void
794retry(signo)
795	int signo;
796{
797	struct servtab *sep;
798
799	timingout = 0;
800	for (sep = servtab; sep; sep = sep->se_next)
801		if (sep->se_fd == -1 && !ISMUX(sep))
802			setup(sep);
803}
804
805void
806setup(sep)
807	struct servtab *sep;
808{
809	int on = 1;
810
811	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
812		if (debug)
813			fprintf(stderr, "socket failed on %s/%s: %s\n",
814				sep->se_service, sep->se_proto,
815				strerror(errno));
816		syslog(LOG_ERR, "%s/%s: socket: %m",
817		    sep->se_service, sep->se_proto);
818		return;
819	}
820#define	turnon(fd, opt) \
821setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
822	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
823	    turnon(sep->se_fd, SO_DEBUG) < 0)
824		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
825	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
826		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
827	if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
828		syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
829#undef turnon
830	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
831	    sizeof (sep->se_ctrladdr)) < 0) {
832		if (debug)
833			fprintf(stderr, "bind failed on %s/%s: %s\n",
834				sep->se_service, sep->se_proto,
835				strerror(errno));
836		syslog(LOG_ERR, "%s/%s: bind: %m",
837		    sep->se_service, sep->se_proto);
838		(void) close(sep->se_fd);
839		sep->se_fd = -1;
840		if (!timingout) {
841			timingout = 1;
842			alarm(RETRYTIME);
843		}
844		return;
845	}
846        if (sep->se_rpc) {
847                int i, len = sizeof(struct sockaddr);
848
849                if (getsockname(sep->se_fd,
850				(struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
851                        syslog(LOG_ERR, "%s/%s: getsockname: %m",
852                               sep->se_service, sep->se_proto);
853                        (void) close(sep->se_fd);
854                        sep->se_fd = -1;
855                        return;
856                }
857                if (debug)
858                        print_service("REG ", sep);
859                for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
860                        pmap_unset(sep->se_rpc_prog, i);
861                        pmap_set(sep->se_rpc_prog, i,
862                                 (sep->se_socktype == SOCK_DGRAM)
863                                 ? IPPROTO_UDP : IPPROTO_TCP,
864                                 ntohs(sep->se_ctrladdr.sin_port));
865                }
866
867        }
868	if (sep->se_socktype == SOCK_STREAM)
869		listen(sep->se_fd, 64);
870	enable(sep);
871	if (debug) {
872		fprintf(stderr, "registered %s on %d\n",
873			sep->se_server, sep->se_fd);
874	}
875}
876
877/*
878 * Finish with a service and its socket.
879 */
880void
881close_sep(sep)
882	struct servtab *sep;
883{
884	if (sep->se_fd >= 0) {
885		if (FD_ISSET(sep->se_fd, &allsock))
886			disable(sep);
887		(void) close(sep->se_fd);
888		sep->se_fd = -1;
889	}
890	sep->se_count = 0;
891	sep->se_numchild = 0;	/* forget about any existing children */
892}
893
894struct servtab *
895enter(cp)
896	struct servtab *cp;
897{
898	struct servtab *sep;
899	long omask;
900
901	sep = (struct servtab *)malloc(sizeof (*sep));
902	if (sep == (struct servtab *)0) {
903		syslog(LOG_ERR, "Out of memory.");
904		exit(EX_OSERR);
905	}
906	*sep = *cp;
907	sep->se_fd = -1;
908	omask = sigblock(SIGBLOCK);
909	sep->se_next = servtab;
910	servtab = sep;
911	sigsetmask(omask);
912	return (sep);
913}
914
915void
916enable(struct servtab *sep)
917{
918	if (debug)
919		fprintf(stderr,
920		    "enabling %s, fd %d", sep->se_service, sep->se_fd);
921#ifdef SANITY_CHECK
922	if (sep->se_fd < 0) {
923		syslog(LOG_ERR,
924		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
925		exit(EX_SOFTWARE);
926	}
927	if (ISMUX(sep)) {
928		syslog(LOG_ERR,
929		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
930		exit(EX_SOFTWARE);
931	}
932	if (FD_ISSET(sep->se_fd, &allsock)) {
933		syslog(LOG_ERR,
934		    "%s: %s: not off", __FUNCTION__, sep->se_service);
935		exit(EX_SOFTWARE);
936	}
937#endif
938	FD_SET(sep->se_fd, &allsock);
939	nsock++;
940	if (sep->se_fd > maxsock)
941		maxsock = sep->se_fd;
942}
943
944void
945disable(struct servtab *sep)
946{
947	if (debug)
948		fprintf(stderr,
949		    "disabling %s, fd %d", sep->se_service, sep->se_fd);
950#ifdef SANITY_CHECK
951	if (sep->se_fd < 0) {
952		syslog(LOG_ERR,
953		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
954		exit(EX_SOFTWARE);
955	}
956	if (ISMUX(sep)) {
957		syslog(LOG_ERR,
958		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
959		exit(EX_SOFTWARE);
960	}
961	if (!FD_ISSET(sep->se_fd, &allsock)) {
962		syslog(LOG_ERR,
963		    "%s: %s: not on", __FUNCTION__, sep->se_service);
964		exit(EX_SOFTWARE);
965	}
966	if (nsock == 0) {
967		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
968		exit(EX_SOFTWARE);
969	}
970#endif
971	FD_CLR(sep->se_fd, &allsock);
972	nsock--;
973	if (sep->se_fd == maxsock)
974		maxsock--;
975}
976
977FILE	*fconfig = NULL;
978struct	servtab serv;
979char	line[LINE_MAX];
980
981int
982setconfig()
983{
984
985	if (fconfig != NULL) {
986		fseek(fconfig, 0L, SEEK_SET);
987		return (1);
988	}
989	fconfig = fopen(CONFIG, "r");
990	return (fconfig != NULL);
991}
992
993void
994endconfig()
995{
996	if (fconfig) {
997		(void) fclose(fconfig);
998		fconfig = NULL;
999	}
1000}
1001
1002struct servtab *
1003getconfigent()
1004{
1005	struct servtab *sep = &serv;
1006	int argc;
1007	char *cp, *arg, *s;
1008	char *versp;
1009	static char TCPMUX_TOKEN[] = "tcpmux/";
1010#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
1011
1012more:
1013	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
1014		;
1015	if (cp == NULL)
1016		return ((struct servtab *)0);
1017	/*
1018	 * clear the static buffer, since some fields (se_ctrladdr,
1019	 * for example) don't get initialized here.
1020	 */
1021	memset((caddr_t)sep, 0, sizeof *sep);
1022	arg = skip(&cp);
1023	if (cp == NULL) {
1024		/* got an empty line containing just blanks/tabs. */
1025		goto more;
1026	}
1027	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
1028		char *c = arg + MUX_LEN;
1029		if (*c == '+') {
1030			sep->se_type = MUXPLUS_TYPE;
1031			c++;
1032		} else
1033			sep->se_type = MUX_TYPE;
1034		sep->se_service = newstr(c);
1035	} else {
1036		sep->se_service = newstr(arg);
1037		sep->se_type = NORM_TYPE;
1038	}
1039	arg = sskip(&cp);
1040	if (strcmp(arg, "stream") == 0)
1041		sep->se_socktype = SOCK_STREAM;
1042	else if (strcmp(arg, "dgram") == 0)
1043		sep->se_socktype = SOCK_DGRAM;
1044	else if (strcmp(arg, "rdm") == 0)
1045		sep->se_socktype = SOCK_RDM;
1046	else if (strcmp(arg, "seqpacket") == 0)
1047		sep->se_socktype = SOCK_SEQPACKET;
1048	else if (strcmp(arg, "raw") == 0)
1049		sep->se_socktype = SOCK_RAW;
1050	else
1051		sep->se_socktype = -1;
1052	sep->se_proto = newstr(sskip(&cp));
1053        if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
1054                memmove(sep->se_proto, sep->se_proto + 4,
1055                    strlen(sep->se_proto) + 1 - 4);
1056                sep->se_rpc = 1;
1057                sep->se_rpc_prog = sep->se_rpc_lowvers =
1058			sep->se_rpc_lowvers = 0;
1059                sep->se_ctrladdr.sin_family = AF_INET;
1060                sep->se_ctrladdr.sin_port = 0;
1061                sep->se_ctrladdr.sin_addr = bind_address;
1062                if ((versp = rindex(sep->se_service, '/'))) {
1063                        *versp++ = '\0';
1064                        switch (sscanf(versp, "%d-%d",
1065                                       &sep->se_rpc_lowvers,
1066                                       &sep->se_rpc_highvers)) {
1067                        case 2:
1068                                break;
1069                        case 1:
1070                                sep->se_rpc_highvers =
1071                                        sep->se_rpc_lowvers;
1072                                break;
1073                        default:
1074                                syslog(LOG_ERR,
1075					"bad RPC version specifier; %s\n",
1076					sep->se_service);
1077                                freeconfig(sep);
1078                                goto more;
1079                        }
1080                }
1081                else {
1082                        sep->se_rpc_lowvers =
1083                                sep->se_rpc_highvers = 1;
1084                }
1085        }
1086	arg = sskip(&cp);
1087	if (!strncmp(arg, "wait", 4))
1088		sep->se_accept = 0;
1089	else if (!strncmp(arg, "nowait", 6))
1090		sep->se_accept = 1;
1091	else {
1092		syslog(LOG_ERR,
1093			"%s: bad wait/nowait for service %s",
1094			CONFIG, sep->se_service);
1095		goto more;
1096	}
1097	sep->se_maxchild = -1;
1098	if ((s = strchr(arg, '/')) != NULL) {
1099		char *eptr;
1100		u_long val;
1101
1102		val = strtoul(s + 1, &eptr, 10);
1103		if (eptr == s + 1 || *eptr || val > MAX_MAXCHLD) {
1104			syslog(LOG_ERR,
1105				"%s: bad max-child for service %s",
1106				CONFIG, sep->se_service);
1107			goto more;
1108		}
1109		sep->se_maxchild = val;
1110	}
1111	if (ISMUX(sep)) {
1112		/*
1113		 * Silently enforce "nowait" mode for TCPMUX services
1114		 * since they don't have an assigned port to listen on.
1115		 */
1116		sep->se_accept = 1;
1117		if (strcmp(sep->se_proto, "tcp")) {
1118			syslog(LOG_ERR,
1119				"%s: bad protocol for tcpmux service %s",
1120				CONFIG, sep->se_service);
1121			goto more;
1122		}
1123		if (sep->se_socktype != SOCK_STREAM) {
1124			syslog(LOG_ERR,
1125				"%s: bad socket type for tcpmux service %s",
1126				CONFIG, sep->se_service);
1127			goto more;
1128		}
1129	}
1130	sep->se_user = newstr(sskip(&cp));
1131	sep->se_server = newstr(sskip(&cp));
1132	if (strcmp(sep->se_server, "internal") == 0) {
1133		struct biltin *bi;
1134
1135		for (bi = biltins; bi->bi_service; bi++)
1136			if (bi->bi_socktype == sep->se_socktype &&
1137			    strcmp(bi->bi_service, sep->se_service) == 0)
1138				break;
1139		if (bi->bi_service == 0) {
1140			syslog(LOG_ERR, "internal service %s unknown",
1141				sep->se_service);
1142			goto more;
1143		}
1144		sep->se_accept = 1;	/* force accept mode for built-ins */
1145		sep->se_bi = bi;
1146	} else
1147		sep->se_bi = NULL;
1148	if (sep->se_maxchild < 0)	/* apply default max-children */
1149		if (sep->se_bi)
1150			sep->se_maxchild = sep->se_bi->bi_maxchild;
1151		else
1152			sep->se_maxchild = sep->se_accept ? 0 : 1;
1153	if (sep->se_maxchild) {
1154		sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
1155		if (sep->se_pids == NULL) {
1156			syslog(LOG_ERR, "Out of memory.");
1157			exit(EX_OSERR);
1158		}
1159	}
1160	argc = 0;
1161	for (arg = skip(&cp); cp; arg = skip(&cp))
1162		if (argc < MAXARGV) {
1163			sep->se_argv[argc++] = newstr(arg);
1164		} else {
1165			syslog(LOG_ERR,
1166				"%s: too many arguments for service %s",
1167				CONFIG, sep->se_service);
1168			goto more;
1169		}
1170	while (argc <= MAXARGV)
1171		sep->se_argv[argc++] = NULL;
1172	return (sep);
1173}
1174
1175void
1176freeconfig(cp)
1177	struct servtab *cp;
1178{
1179	int i;
1180
1181	if (cp->se_service)
1182		free(cp->se_service);
1183	if (cp->se_proto)
1184		free(cp->se_proto);
1185	if (cp->se_user)
1186		free(cp->se_user);
1187	if (cp->se_server)
1188		free(cp->se_server);
1189	if (cp->se_pids)
1190		free(cp->se_pids);
1191	for (i = 0; i < MAXARGV; i++)
1192		if (cp->se_argv[i])
1193			free(cp->se_argv[i]);
1194}
1195
1196
1197/*
1198 * Safe skip - if skip returns null, log a syntax error in the
1199 * configuration file and exit.
1200 */
1201char *
1202sskip(cpp)
1203	char **cpp;
1204{
1205	char *cp;
1206
1207	cp = skip(cpp);
1208	if (cp == NULL) {
1209		syslog(LOG_ERR, "%s: syntax error", CONFIG);
1210		exit(EX_DATAERR);
1211	}
1212	return (cp);
1213}
1214
1215char *
1216skip(cpp)
1217	char **cpp;
1218{
1219	char *cp = *cpp;
1220	char *start;
1221	char quote = '\0';
1222
1223again:
1224	while (*cp == ' ' || *cp == '\t')
1225		cp++;
1226	if (*cp == '\0') {
1227		int c;
1228
1229		c = getc(fconfig);
1230		(void) ungetc(c, fconfig);
1231		if (c == ' ' || c == '\t')
1232			if ((cp = nextline(fconfig)))
1233				goto again;
1234		*cpp = (char *)0;
1235		return ((char *)0);
1236	}
1237	if (*cp == '"' || *cp == '\'')
1238		quote = *cp++;
1239	start = cp;
1240	if (quote)
1241		while (*cp && *cp != quote)
1242			cp++;
1243	else
1244		while (*cp && *cp != ' ' && *cp != '\t')
1245			cp++;
1246	if (*cp != '\0')
1247		*cp++ = '\0';
1248	*cpp = cp;
1249	return (start);
1250}
1251
1252char *
1253nextline(fd)
1254	FILE *fd;
1255{
1256	char *cp;
1257
1258	if (fgets(line, sizeof (line), fd) == NULL)
1259		return ((char *)0);
1260	cp = strchr(line, '\n');
1261	if (cp)
1262		*cp = '\0';
1263	return (line);
1264}
1265
1266char *
1267newstr(cp)
1268	char *cp;
1269{
1270	if ((cp = strdup(cp ? cp : "")))
1271		return (cp);
1272	syslog(LOG_ERR, "strdup: %m");
1273	exit(EX_OSERR);
1274}
1275
1276#ifdef OLD_SETPROCTITLE
1277void
1278inetd_setproctitle(a, s)
1279	char *a;
1280	int s;
1281{
1282	int size;
1283	char *cp;
1284	struct sockaddr_in sin;
1285	char buf[80];
1286
1287	cp = Argv[0];
1288	size = sizeof(sin);
1289	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
1290		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
1291	else
1292		(void) sprintf(buf, "-%s", a);
1293	strncpy(cp, buf, LastArg - cp);
1294	cp += strlen(cp);
1295	while (cp < LastArg)
1296		*cp++ = ' ';
1297}
1298#else
1299void
1300inetd_setproctitle(a, s)
1301	char *a;
1302	int s;
1303{
1304	int size;
1305	struct sockaddr_in sin;
1306	char buf[80];
1307
1308	size = sizeof(sin);
1309	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
1310		(void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr));
1311	else
1312		(void) sprintf(buf, "%s", a);
1313	setproctitle("%s", buf);
1314}
1315#endif
1316
1317
1318/*
1319 * Internet services provided internally by inetd:
1320 */
1321#define	BUFSIZE	8192
1322
1323/* ARGSUSED */
1324void
1325echo_stream(s, sep)		/* Echo service -- echo data back */
1326	int s;
1327	struct servtab *sep;
1328{
1329	char buffer[BUFSIZE];
1330	int i;
1331
1332	inetd_setproctitle(sep->se_service, s);
1333	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
1334	    write(s, buffer, i) > 0)
1335		;
1336	exit(0);
1337}
1338
1339int check_loop(sin, sep)
1340	struct sockaddr_in *sin;
1341	struct servtab *sep;
1342{
1343	struct servtab *se2;
1344
1345	for (se2 = servtab; se2; se2 = se2->se_next) {
1346		if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
1347			continue;
1348
1349		if (sin->sin_port == se2->se_ctrladdr.sin_port) {
1350			syslog(LOG_WARNING,
1351			       "%s/%s:%s/%s loop request REFUSED from %s",
1352			       sep->se_service, sep->se_proto,
1353			       se2->se_service, se2->se_proto,
1354			       inet_ntoa(sin->sin_addr));
1355			return 1;
1356		}
1357	}
1358	return 0;
1359}
1360
1361/* ARGSUSED */
1362void
1363echo_dg(s, sep)			/* Echo service -- echo data back */
1364	int s;
1365	struct servtab *sep;
1366{
1367	char buffer[BUFSIZE];
1368	int i, size;
1369	struct sockaddr_in sin;
1370
1371	size = sizeof(sin);
1372	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
1373			  (struct sockaddr *)&sin, &size)) < 0)
1374		return;
1375
1376	if (check_loop(&sin, sep))
1377		return;
1378
1379	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin,
1380		      sizeof(sin));
1381}
1382
1383/* ARGSUSED */
1384void
1385discard_stream(s, sep)		/* Discard service -- ignore data */
1386	int s;
1387	struct servtab *sep;
1388{
1389	int ret;
1390	char buffer[BUFSIZE];
1391
1392	inetd_setproctitle(sep->se_service, s);
1393	while (1) {
1394		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
1395			;
1396		if (ret == 0 || errno != EINTR)
1397			break;
1398	}
1399	exit(0);
1400}
1401
1402/* ARGSUSED */
1403void
1404discard_dg(s, sep)		/* Discard service -- ignore data */
1405	int s;
1406	struct servtab *sep;
1407{
1408	char buffer[BUFSIZE];
1409
1410	(void) read(s, buffer, sizeof(buffer));
1411}
1412
1413#include <ctype.h>
1414#define LINESIZ 72
1415char ring[128];
1416char *endring;
1417
1418void
1419initring()
1420{
1421	int i;
1422
1423	endring = ring;
1424
1425	for (i = 0; i <= 128; ++i)
1426		if (isprint(i))
1427			*endring++ = i;
1428}
1429
1430/* ARGSUSED */
1431void
1432chargen_stream(s, sep)		/* Character generator */
1433	int s;
1434	struct servtab *sep;
1435{
1436	int len;
1437	char *rs, text[LINESIZ+2];
1438
1439	inetd_setproctitle(sep->se_service, s);
1440
1441	if (!endring) {
1442		initring();
1443		rs = ring;
1444	}
1445
1446	text[LINESIZ] = '\r';
1447	text[LINESIZ + 1] = '\n';
1448	for (rs = ring;;) {
1449		if ((len = endring - rs) >= LINESIZ)
1450			memmove(text, rs, LINESIZ);
1451		else {
1452			memmove(text, rs, len);
1453			memmove(text + len, ring, LINESIZ - len);
1454		}
1455		if (++rs == endring)
1456			rs = ring;
1457		if (write(s, text, sizeof(text)) != sizeof(text))
1458			break;
1459	}
1460	exit(0);
1461}
1462
1463/* ARGSUSED */
1464void
1465chargen_dg(s, sep)		/* Character generator */
1466	int s;
1467	struct servtab *sep;
1468{
1469	struct sockaddr_in sin;
1470	static char *rs;
1471	int len, size;
1472	char text[LINESIZ+2];
1473
1474	if (endring == 0) {
1475		initring();
1476		rs = ring;
1477	}
1478
1479	size = sizeof(sin);
1480	if (recvfrom(s, text, sizeof(text), 0,
1481		     (struct sockaddr *)&sin, &size) < 0)
1482		return;
1483
1484	if (check_loop(&sin, sep))
1485		return;
1486
1487	if ((len = endring - rs) >= LINESIZ)
1488		memmove(text, rs, LINESIZ);
1489	else {
1490		memmove(text, rs, len);
1491		memmove(text + len, ring, LINESIZ - len);
1492	}
1493	if (++rs == endring)
1494		rs = ring;
1495	text[LINESIZ] = '\r';
1496	text[LINESIZ + 1] = '\n';
1497	(void) sendto(s, text, sizeof(text), 0,
1498		      (struct sockaddr *)&sin, sizeof(sin));
1499}
1500
1501/*
1502 * Return a machine readable date and time, in the form of the
1503 * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
1504 * returns the number of seconds since midnight, Jan 1, 1970,
1505 * we must add 2208988800 seconds to this figure to make up for
1506 * some seventy years Bell Labs was asleep.
1507 */
1508
1509long
1510machtime()
1511{
1512	struct timeval tv;
1513
1514	if (gettimeofday(&tv, (struct timezone *)0) < 0) {
1515		if (debug)
1516			fprintf(stderr, "Unable to get time of day\n");
1517		return (0L);
1518	}
1519#define	OFFSET ((u_long)25567 * 24*60*60)
1520	return (htonl((long)(tv.tv_sec + OFFSET)));
1521#undef OFFSET
1522}
1523
1524/* ARGSUSED */
1525void
1526machtime_stream(s, sep)
1527	int s;
1528	struct servtab *sep;
1529{
1530	long result;
1531
1532	result = machtime();
1533	(void) write(s, (char *) &result, sizeof(result));
1534}
1535
1536/* ARGSUSED */
1537void
1538machtime_dg(s, sep)
1539	int s;
1540	struct servtab *sep;
1541{
1542	long result;
1543	struct sockaddr_in sin;
1544	int size;
1545
1546	size = sizeof(sin);
1547	if (recvfrom(s, (char *)&result, sizeof(result), 0,
1548		     (struct sockaddr *)&sin, &size) < 0)
1549		return;
1550
1551	if (check_loop(&sin, sep))
1552		return;
1553
1554	result = machtime();
1555	(void) sendto(s, (char *) &result, sizeof(result), 0,
1556		      (struct sockaddr *)&sin, sizeof(sin));
1557}
1558
1559/* ARGSUSED */
1560void
1561daytime_stream(s, sep)		/* Return human-readable time of day */
1562	int s;
1563	struct servtab *sep;
1564{
1565	char buffer[256];
1566	time_t clock;
1567
1568	clock = time((time_t *) 0);
1569
1570	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
1571	(void) write(s, buffer, strlen(buffer));
1572}
1573
1574/* ARGSUSED */
1575void
1576daytime_dg(s, sep)		/* Return human-readable time of day */
1577	int s;
1578	struct servtab *sep;
1579{
1580	char buffer[256];
1581	time_t clock;
1582	struct sockaddr_in sin;
1583	int size;
1584
1585	clock = time((time_t *) 0);
1586
1587	size = sizeof(sin);
1588	if (recvfrom(s, buffer, sizeof(buffer), 0,
1589		     (struct sockaddr *)&sin, &size) < 0)
1590		return;
1591
1592	if (check_loop(&sin, sep))
1593		return;
1594
1595	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
1596	(void) sendto(s, buffer, strlen(buffer), 0,
1597		      (struct sockaddr *)&sin, sizeof(sin));
1598}
1599
1600/*
1601 * print_service:
1602 *	Dump relevant information to stderr
1603 */
1604void
1605print_service(action, sep)
1606	char *action;
1607	struct servtab *sep;
1608{
1609	fprintf(stderr,
1610	    "%s: %s proto=%s accept=%d max=%d user=%s builtin=%x server=%s\n",
1611	    action, sep->se_service, sep->se_proto,
1612	    sep->se_accept, sep->se_maxchild, sep->se_user,
1613	    (int)sep->se_bi, sep->se_server);
1614}
1615
1616/*
1617 *  Based on TCPMUX.C by Mark K. Lottor November 1988
1618 *  sri-nic::ps:<mkl>tcpmux.c
1619 */
1620
1621
1622static int		/* # of characters upto \r,\n or \0 */
1623getline(fd, buf, len)
1624	int fd;
1625	char *buf;
1626	int len;
1627{
1628	int count = 0, n;
1629
1630	do {
1631		n = read(fd, buf, len-count);
1632		if (n == 0)
1633			return (count);
1634		if (n < 0)
1635			return (-1);
1636		while (--n >= 0) {
1637			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
1638				return (count);
1639			count++;
1640			buf++;
1641		}
1642	} while (count < len);
1643	return (count);
1644}
1645
1646#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
1647
1648#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
1649
1650struct servtab *
1651tcpmux(s)
1652	int s;
1653{
1654	struct servtab *sep;
1655	char service[MAX_SERV_LEN+1];
1656	int len;
1657
1658	/* Get requested service name */
1659	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
1660		strwrite(s, "-Error reading service name\r\n");
1661		return (NULL);
1662	}
1663	service[len] = '\0';
1664
1665	if (debug)
1666		fprintf(stderr, "tcpmux: someone wants %s\n", service);
1667
1668	/*
1669	 * Help is a required command, and lists available services,
1670	 * one per line.
1671	 */
1672	if (!strcasecmp(service, "help")) {
1673		for (sep = servtab; sep; sep = sep->se_next) {
1674			if (!ISMUX(sep))
1675				continue;
1676			(void)write(s,sep->se_service,strlen(sep->se_service));
1677			strwrite(s, "\r\n");
1678		}
1679		return (NULL);
1680	}
1681
1682	/* Try matching a service in inetd.conf with the request */
1683	for (sep = servtab; sep; sep = sep->se_next) {
1684		if (!ISMUX(sep))
1685			continue;
1686		if (!strcasecmp(service, sep->se_service)) {
1687			if (ISMUXPLUS(sep)) {
1688				strwrite(s, "+Go\r\n");
1689			}
1690			return (sep);
1691		}
1692	}
1693	strwrite(s, "-Service not available\r\n");
1694	return (NULL);
1695}
1696