inetd.c revision 13956
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[] =
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[] =
43	"$Id: inetd.c,v 1.10 1996/01/01 08:42:23 peter 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
116#include <errno.h>
117#include <fcntl.h>
118#include <netdb.h>
119#include <pwd.h>
120#include <signal.h>
121#include <stdio.h>
122#include <stdlib.h>
123#include <string.h>
124#include <syslog.h>
125#include <unistd.h>
126#include <libutil.h>
127
128#include "pathnames.h"
129
130#define	TOOMANY		256		/* don't start more than TOOMANY */
131#define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
132#define	RETRYTIME	(60*10)		/* retry after bind or server fail */
133
134#define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
135
136
137int	debug = 0;
138int	log = 0;
139int	nsock, maxsock;
140fd_set	allsock;
141int	options;
142int	timingout;
143int	toomany = TOOMANY;
144struct	servent *sp;
145struct	rpcent *rpc;
146
147struct	servtab {
148	char	*se_service;		/* name of service */
149	int	se_socktype;		/* type of socket to use */
150	char	*se_proto;		/* protocol used */
151	short	se_wait;		/* single threaded server */
152	short	se_checked;		/* looked at during merge */
153	char	*se_user;		/* user name to run as */
154	struct	biltin *se_bi;		/* if built-in, description */
155	char	*se_server;		/* server program */
156#define	MAXARGV 20
157	char	*se_argv[MAXARGV+1];	/* program arguments */
158	int	se_fd;			/* open descriptor */
159	int	se_type;		/* type */
160	struct	sockaddr_in se_ctrladdr;/* bound address */
161	int	se_rpc;			/* ==1 if RPC service */
162	int	se_rpc_prog;		/* RPC program number */
163	u_int	se_rpc_lowvers;		/* RPC low version */
164	u_int	se_rpc_highvers;	/* RPC high version */
165	int	se_count;		/* number started since se_time */
166	struct	timeval se_time;	/* start of se_count */
167	struct	servtab *se_next;
168} *servtab;
169
170#define NORM_TYPE	0
171#define MUX_TYPE	1
172#define MUXPLUS_TYPE	2
173#define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
174			 ((sep)->se_type == MUXPLUS_TYPE))
175#define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
176
177
178void		chargen_dg __P((int, struct servtab *));
179void		chargen_stream __P((int, struct servtab *));
180void		close_sep __P((struct servtab *));
181void		config __P((int));
182void		daytime_dg __P((int, struct servtab *));
183void		daytime_stream __P((int, struct servtab *));
184void		discard_dg __P((int, struct servtab *));
185void		discard_stream __P((int, struct servtab *));
186void		echo_dg __P((int, struct servtab *));
187void		echo_stream __P((int, struct servtab *));
188void		endconfig __P((void));
189struct servtab *enter __P((struct servtab *));
190void		freeconfig __P((struct servtab *));
191struct servtab *getconfigent __P((void));
192void		machtime_dg __P((int, struct servtab *));
193void		machtime_stream __P((int, struct servtab *));
194char	       *newstr __P((char *));
195char	       *nextline __P((FILE *));
196void		print_service __P((char *, struct servtab *));
197void		reapchild __P((int));
198void		retry __P((int));
199int		setconfig __P((void));
200void		setup __P((struct servtab *));
201char	       *sskip __P((char **));
202char	       *skip __P((char **));
203struct servtab *tcpmux __P((int));
204
205void		unregisterrpc __P((register struct servtab *sep));
206
207struct biltin {
208	char	*bi_service;		/* internally provided service name */
209	int	bi_socktype;		/* type of socket supported */
210	short	bi_fork;		/* 1 if should fork before call */
211	short	bi_wait;		/* 1 if should wait for child */
212	void	(*bi_fn)();		/* function which performs it */
213} biltins[] = {
214	/* Echo received data */
215	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream },
216	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg },
217
218	/* Internet /dev/null */
219	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream },
220	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg },
221
222	/* Return 32 bit time since 1970 */
223	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream },
224	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg },
225
226	/* Return human-readable time */
227	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream },
228	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg },
229
230	/* Familiar character generator */
231	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream },
232	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg },
233
234	{ "tcpmux",	SOCK_STREAM,	1, 0,	(void (*)())tcpmux },
235
236	{ NULL }
237};
238
239#define NUMINT	(sizeof(intab) / sizeof(struct inent))
240char	*CONFIG = _PATH_INETDCONF;
241
242#ifdef OLD_SETPROCTITLE
243char	**Argv;
244char 	*LastArg;
245#endif
246
247int
248main(argc, argv, envp)
249	int argc;
250	char *argv[], *envp[];
251{
252	struct servtab *sep;
253	struct passwd *pwd;
254	struct sigvec sv;
255	int tmpint, ch, dofork;
256	pid_t pid;
257	char buf[50];
258	struct  sockaddr_in peer;
259	int i;
260
261
262#ifdef OLD_SETPROCTITLE
263	Argv = argv;
264	if (envp == 0 || *envp == 0)
265		envp = argv;
266	while (*envp)
267		envp++;
268	LastArg = envp[-1] + strlen(envp[-1]);
269#endif
270
271	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
272
273	while ((ch = getopt(argc, argv, "dlR:")) != EOF)
274		switch(ch) {
275		case 'd':
276			debug = 1;
277			options |= SO_DEBUG;
278			break;
279		case 'l':
280			log = 1;
281			break;
282		case 'R': {	/* invocation rate */
283			char *p;
284
285			tmpint = strtol(optarg, &p, 0);
286			if (tmpint < 1 || *p)
287				syslog(LOG_ERR,
288			         "-R %s: bad value for service invocation rate",
289					optarg);
290			else
291				toomany = tmpint;
292			break;
293		}
294		case '?':
295		default:
296			syslog(LOG_ERR,
297				"usage: inetd [-dl] [-R rate] [conf-file]");
298			exit(1);
299		}
300	argc -= optind;
301	argv += optind;
302
303	if (argc > 0)
304		CONFIG = argv[0];
305	if (debug == 0) {
306		FILE *fp;
307		if (daemon(0, 0) < 0) {
308			syslog(LOG_WARNING, "daemon(0,0) failed: %m");
309		}
310		/*
311		 * In case somebody has started inetd manually, we need to
312		 * clear the logname, so that old servers run as root do not
313		 * get the user's logname..
314		 */
315		if (setlogin("") < 0) {
316			syslog(LOG_WARNING, "cannot clear logname: %m");
317			/* no big deal if it fails.. */
318		}
319		pid = getpid();
320		fp = fopen(_PATH_INETDPID, "w");
321		if (fp) {
322			fprintf(fp, "%ld\n", (long)pid);
323			fclose(fp);
324		} else {
325			syslog(LOG_WARNING, _PATH_INETDPID ": %m");
326		}
327	}
328	memset(&sv, 0, sizeof(sv));
329	sv.sv_mask = SIGBLOCK;
330	sv.sv_handler = retry;
331	sigvec(SIGALRM, &sv, (struct sigvec *)0);
332	config(SIGHUP);
333	sv.sv_handler = config;
334	sigvec(SIGHUP, &sv, (struct sigvec *)0);
335	sv.sv_handler = reapchild;
336	sigvec(SIGCHLD, &sv, (struct sigvec *)0);
337
338	{
339		/* space for daemons to overwrite environment for ps */
340#define	DUMMYSIZE	100
341		char dummy[DUMMYSIZE];
342
343		(void)memset(dummy, 'x', sizeof(DUMMYSIZE) - 1);
344		dummy[DUMMYSIZE - 1] = '\0';
345		(void)setenv("inetd_dummy", dummy, 1);
346	}
347
348	for (;;) {
349	    int n, ctrl;
350	    fd_set readable;
351
352	    if (nsock == 0) {
353		(void) sigblock(SIGBLOCK);
354		while (nsock == 0)
355		    sigpause(0L);
356		(void) sigsetmask(0L);
357	    }
358	    readable = allsock;
359	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
360		(fd_set *)0, (struct timeval *)0)) <= 0) {
361		    if (n < 0 && errno != EINTR)
362			syslog(LOG_WARNING, "select: %m");
363		    sleep(1);
364		    continue;
365	    }
366	    for (sep = servtab; n && sep; sep = sep->se_next)
367	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
368		    n--;
369		    if (debug)
370			    fprintf(stderr, "someone wants %s\n",
371				sep->se_service);
372		    if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
373			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
374				(int *)0);
375			    if (debug)
376				    fprintf(stderr, "accept, ctrl %d\n", ctrl);
377			    if (ctrl < 0) {
378				    if (errno != EINTR)
379					    syslog(LOG_WARNING,
380						"accept (for %s): %m",
381						sep->se_service);
382				    continue;
383			    }
384			    if(log) {
385				i = sizeof peer;
386				if(getpeername(ctrl, (struct sockaddr *)
387						&peer, &i)) {
388					syslog(LOG_WARNING,
389						"getpeername(for %s): %m",
390						sep->se_service);
391					continue;
392				}
393				syslog(LOG_INFO,"%s from %s",
394					sep->se_service,
395					inet_ntoa(peer.sin_addr));
396			    }
397			    /*
398			     * Call tcpmux to find the real service to exec.
399			     */
400			    if (sep->se_bi &&
401				sep->se_bi->bi_fn == (void (*)()) tcpmux) {
402				    struct servtab *tsep;
403
404				    tsep = tcpmux(ctrl);
405				    if (tsep == NULL) {
406					    close(ctrl);
407					    continue;
408				    }
409				    sep = tsep;
410			    }
411		    } else
412			    ctrl = sep->se_fd;
413		    (void) sigblock(SIGBLOCK);
414		    pid = 0;
415		    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
416		    if (dofork) {
417			    if (sep->se_count++ == 0)
418				(void)gettimeofday(&sep->se_time,
419				    (struct timezone *)0);
420			    else if (sep->se_count >= toomany) {
421				struct timeval now;
422
423				(void)gettimeofday(&now, (struct timezone *)0);
424				if (now.tv_sec - sep->se_time.tv_sec >
425				    CNT_INTVL) {
426					sep->se_time = now;
427					sep->se_count = 1;
428				} else {
429					syslog(LOG_ERR,
430			"%s/%s server failing (looping), service terminated",
431					    sep->se_service, sep->se_proto);
432					close_sep(sep);
433					sigsetmask(0L);
434					if (!timingout) {
435						timingout = 1;
436						alarm(RETRYTIME);
437					}
438					continue;
439				}
440			    }
441			    pid = fork();
442		    }
443		    if (pid < 0) {
444			    syslog(LOG_ERR, "fork: %m");
445			    if (!sep->se_wait &&
446				sep->se_socktype == SOCK_STREAM)
447				    close(ctrl);
448			    sigsetmask(0L);
449			    sleep(1);
450			    continue;
451		    }
452		    if (pid && sep->se_wait) {
453			    sep->se_wait = pid;
454			    if (sep->se_fd >= 0) {
455				FD_CLR(sep->se_fd, &allsock);
456			        nsock--;
457			    }
458		    }
459		    sigsetmask(0L);
460		    if (pid == 0) {
461			    if (dofork) {
462				if (debug)
463					fprintf(stderr, "+ Closing from %d\n",
464						maxsock);
465				for (tmpint = maxsock; tmpint > 2; tmpint--)
466					if (tmpint != ctrl)
467						close(tmpint);
468			    }
469			    if (sep->se_bi)
470				(*sep->se_bi->bi_fn)(ctrl, sep);
471			    else {
472				if (debug)
473					fprintf(stderr, "%d execl %s\n",
474					    getpid(), sep->se_server);
475				dup2(ctrl, 0);
476				close(ctrl);
477				dup2(0, 1);
478				dup2(0, 2);
479				if ((pwd = getpwnam(sep->se_user)) == NULL) {
480					syslog(LOG_ERR,
481					    "%s/%s: %s: No such user",
482						sep->se_service, sep->se_proto,
483						sep->se_user);
484					if (sep->se_socktype != SOCK_STREAM)
485						recv(0, buf, sizeof (buf), 0);
486					_exit(1);
487				}
488				if (setsid() < 0) {
489					syslog(LOG_ERR,
490						"%s: can't setsid(): %m",
491						 sep->se_service);
492					/* _exit(1); not fatal yet */
493				}
494				if (pwd->pw_uid) {
495					if (setlogin(sep->se_user) < 0) {
496						syslog(LOG_ERR,
497						 "%s: can't setlogin(%s): %m",
498						 sep->se_service, sep->se_user);
499						/* _exit(1); not fatal yet */
500					}
501					if (setgid(pwd->pw_gid) < 0) {
502						syslog(LOG_ERR,
503						  "%s: can't set gid %d: %m",
504						  sep->se_service, pwd->pw_gid);
505						_exit(1);
506					}
507					(void) initgroups(pwd->pw_name,
508							pwd->pw_gid);
509					if (setuid(pwd->pw_uid) < 0) {
510						syslog(LOG_ERR,
511						  "%s: can't set uid %d: %m",
512						  sep->se_service, pwd->pw_uid);
513						_exit(1);
514					}
515				}
516				execv(sep->se_server, sep->se_argv);
517				if (sep->se_socktype != SOCK_STREAM)
518					recv(0, buf, sizeof (buf), 0);
519				syslog(LOG_ERR,
520				    "cannot execute %s: %m", sep->se_server);
521				_exit(1);
522			    }
523		    }
524		    if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
525			    close(ctrl);
526		}
527	}
528}
529
530void
531reapchild(signo)
532	int signo;
533{
534	int status;
535	pid_t pid;
536	struct servtab *sep;
537
538	for (;;) {
539		pid = wait3(&status, WNOHANG, (struct rusage *)0);
540		if (pid <= 0)
541			break;
542		if (debug)
543			fprintf(stderr, "%d reaped, status %#x\n",
544				pid, status);
545		for (sep = servtab; sep; sep = sep->se_next)
546			if (sep->se_wait == pid) {
547				if (status)
548					syslog(LOG_WARNING,
549					    "%s: exit status 0x%x",
550					    sep->se_server, status);
551				if (debug)
552					fprintf(stderr, "restored %s, fd %d\n",
553					    sep->se_service, sep->se_fd);
554				FD_SET(sep->se_fd, &allsock);
555				nsock++;
556				sep->se_wait = 1;
557			}
558	}
559}
560
561void
562config(signo)
563	int signo;
564{
565	struct servtab *sep, *cp, **sepp;
566	struct passwd *pwd;
567	long omask;
568
569	if (!setconfig()) {
570		syslog(LOG_ERR, "%s: %m", CONFIG);
571		return;
572	}
573	for (sep = servtab; sep; sep = sep->se_next)
574		sep->se_checked = 0;
575	while (cp = getconfigent()) {
576		if ((pwd = getpwnam(cp->se_user)) == NULL) {
577			syslog(LOG_ERR,
578				"%s/%s: No such user '%s', service ignored",
579				cp->se_service, cp->se_proto, cp->se_user);
580			continue;
581		}
582		for (sep = servtab; sep; sep = sep->se_next)
583			if (strcmp(sep->se_service, cp->se_service) == 0 &&
584			    strcmp(sep->se_proto, cp->se_proto) == 0)
585				break;
586		if (sep != 0) {
587			int i;
588
589			omask = sigblock(SIGBLOCK);
590			/*
591			 * sep->se_wait may be holding the pid of a daemon
592			 * that we're waiting for.  If so, don't overwrite
593			 * it unless the config file explicitly says don't
594			 * wait.
595			 */
596			if (cp->se_bi == 0 &&
597			    (sep->se_wait == 1 || cp->se_wait == 0))
598				sep->se_wait = cp->se_wait;
599#define SWAP(a, b) { char *c = a; a = b; b = c; }
600			if (cp->se_user)
601				SWAP(sep->se_user, cp->se_user);
602			if (cp->se_server)
603				SWAP(sep->se_server, cp->se_server);
604			for (i = 0; i < MAXARGV; i++)
605				SWAP(sep->se_argv[i], cp->se_argv[i]);
606			sigsetmask(omask);
607			freeconfig(cp);
608			if (debug)
609				print_service("REDO", sep);
610		} else {
611			sep = enter(cp);
612			if (debug)
613				print_service("ADD ", sep);
614		}
615		sep->se_checked = 1;
616		if (ISMUX(sep)) {
617			sep->se_fd = -1;
618			continue;
619		}
620		if (!sep->se_rpc) {
621			sp = getservbyname(sep->se_service, sep->se_proto);
622			if (sp == 0) {
623				syslog(LOG_ERR, "%s/%s: unknown service",
624			    	sep->se_service, sep->se_proto);
625				sep->se_checked = 0;
626				continue;
627			}
628			if (sp->s_port != sep->se_ctrladdr.sin_port) {
629				sep->se_ctrladdr.sin_family = AF_INET;
630				sep->se_ctrladdr.sin_port = sp->s_port;
631				if (sep->se_fd >= 0)
632					close_sep(sep);
633			}
634		} else {
635			rpc = getrpcbyname(sep->se_service);
636			if (rpc == 0) {
637				syslog(LOG_ERR, "%s/%s unknown RPC service.",
638					sep->se_service, sep->se_proto);
639				if (sep->se_fd != -1)
640					(void) close(sep->se_fd);
641				sep->se_fd = -1;
642					continue;
643			}
644			if (rpc->r_number != sep->se_rpc_prog) {
645				if (sep->se_rpc_prog)
646					unregisterrpc(sep);
647				sep->se_rpc_prog = rpc->r_number;
648				if (sep->se_fd != -1)
649					(void) close(sep->se_fd);
650				sep->se_fd = -1;
651			}
652		}
653		if (sep->se_fd == -1)
654			setup(sep);
655	}
656	endconfig();
657	/*
658	 * Purge anything not looked at above.
659	 */
660	omask = sigblock(SIGBLOCK);
661	sepp = &servtab;
662	while (sep = *sepp) {
663		if (sep->se_checked) {
664			sepp = &sep->se_next;
665			continue;
666		}
667		*sepp = sep->se_next;
668		if (sep->se_fd >= 0)
669			close_sep(sep);
670		if (debug)
671			print_service("FREE", sep);
672		if (sep->se_rpc && sep->se_rpc_prog > 0)
673			unregisterrpc(sep);
674		freeconfig(sep);
675		free((char *)sep);
676	}
677	(void) sigsetmask(omask);
678}
679
680void
681unregisterrpc(sep)
682	struct servtab *sep;
683{
684        int i;
685        struct servtab *sepp;
686	long omask;
687
688	omask = sigblock(SIGBLOCK);
689        for (sepp = servtab; sepp; sepp = sepp->se_next) {
690                if (sepp == sep)
691                        continue;
692		if (sep->se_checked == 0 ||
693                    !sepp->se_rpc ||
694                    sep->se_rpc_prog != sepp->se_rpc_prog)
695			continue;
696                return;
697        }
698        if (debug)
699                print_service("UNREG", sep);
700        for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++)
701                pmap_unset(sep->se_rpc_prog, i);
702        if (sep->se_fd != -1)
703                (void) close(sep->se_fd);
704        sep->se_fd = -1;
705	(void) sigsetmask(omask);
706}
707
708void
709retry(signo)
710	int signo;
711{
712	struct servtab *sep;
713
714	timingout = 0;
715	for (sep = servtab; sep; sep = sep->se_next)
716		if (sep->se_fd == -1)
717			setup(sep);
718}
719
720void
721setup(sep)
722	struct servtab *sep;
723{
724	int on = 1;
725
726	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
727		if (debug)
728			fprintf(stderr, "socket failed on %s/%s: %s\n",
729				sep->se_service, sep->se_proto,
730				strerror(errno));
731		syslog(LOG_ERR, "%s/%s: socket: %m",
732		    sep->se_service, sep->se_proto);
733		return;
734	}
735#define	turnon(fd, opt) \
736setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
737	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
738	    turnon(sep->se_fd, SO_DEBUG) < 0)
739		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
740	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
741		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
742	if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
743		syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
744#undef turnon
745	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
746	    sizeof (sep->se_ctrladdr)) < 0) {
747		if (debug)
748			fprintf(stderr, "bind failed on %s/%s: %s\n",
749				sep->se_service, sep->se_proto,
750				strerror(errno));
751		syslog(LOG_ERR, "%s/%s: bind: %m",
752		    sep->se_service, sep->se_proto);
753		(void) close(sep->se_fd);
754		sep->se_fd = -1;
755		if (!timingout) {
756			timingout = 1;
757			alarm(RETRYTIME);
758		}
759		return;
760	}
761        if (sep->se_rpc) {
762                int i, len = sizeof(struct sockaddr);
763
764                if (getsockname(sep->se_fd,
765				(struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
766                        syslog(LOG_ERR, "%s/%s: getsockname: %m",
767                               sep->se_service, sep->se_proto);
768                        (void) close(sep->se_fd);
769                        sep->se_fd = -1;
770                        return;
771                }
772                if (debug)
773                        print_service("REG ", sep);
774                for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
775                        pmap_unset(sep->se_rpc_prog, i);
776                        pmap_set(sep->se_rpc_prog, i,
777                                 (sep->se_socktype == SOCK_DGRAM)
778                                 ? IPPROTO_UDP : IPPROTO_TCP,
779                                 ntohs(sep->se_ctrladdr.sin_port));
780                }
781
782        }
783	if (sep->se_socktype == SOCK_STREAM)
784		listen(sep->se_fd, 10);
785	FD_SET(sep->se_fd, &allsock);
786	nsock++;
787	if (sep->se_fd > maxsock)
788		maxsock = sep->se_fd;
789	if (debug) {
790		fprintf(stderr, "registered %s on %d\n",
791			sep->se_server, sep->se_fd);
792	}
793}
794
795/*
796 * Finish with a service and its socket.
797 */
798void
799close_sep(sep)
800	struct servtab *sep;
801{
802	if (sep->se_fd >= 0) {
803		nsock--;
804		FD_CLR(sep->se_fd, &allsock);
805		(void) close(sep->se_fd);
806		sep->se_fd = -1;
807	}
808	sep->se_count = 0;
809	/*
810	 * Don't keep the pid of this running deamon: when reapchild()
811	 * reaps this pid, it would erroneously increment nsock.
812	 */
813	if (sep->se_wait > 1)
814		sep->se_wait = 1;
815}
816
817struct servtab *
818enter(cp)
819	struct servtab *cp;
820{
821	struct servtab *sep;
822	long omask;
823
824	sep = (struct servtab *)malloc(sizeof (*sep));
825	if (sep == (struct servtab *)0) {
826		syslog(LOG_ERR, "Out of memory.");
827		exit(-1);
828	}
829	*sep = *cp;
830	sep->se_fd = -1;
831	omask = sigblock(SIGBLOCK);
832	sep->se_next = servtab;
833	servtab = sep;
834	sigsetmask(omask);
835	return (sep);
836}
837
838FILE	*fconfig = NULL;
839struct	servtab serv;
840char	line[LINE_MAX];
841
842int
843setconfig()
844{
845
846	if (fconfig != NULL) {
847		fseek(fconfig, 0L, SEEK_SET);
848		return (1);
849	}
850	fconfig = fopen(CONFIG, "r");
851	return (fconfig != NULL);
852}
853
854void
855endconfig()
856{
857	if (fconfig) {
858		(void) fclose(fconfig);
859		fconfig = NULL;
860	}
861}
862
863struct servtab *
864getconfigent()
865{
866	struct servtab *sep = &serv;
867	int argc;
868	char *cp, *arg;
869	char *versp;
870	static char TCPMUX_TOKEN[] = "tcpmux/";
871#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
872
873more:
874	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
875		;
876	if (cp == NULL)
877		return ((struct servtab *)0);
878	/*
879	 * clear the static buffer, since some fields (se_ctrladdr,
880	 * for example) don't get initialized here.
881	 */
882	memset((caddr_t)sep, 0, sizeof *sep);
883	arg = skip(&cp);
884	if (cp == NULL) {
885		/* got an empty line containing just blanks/tabs. */
886		goto more;
887	}
888	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
889		char *c = arg + MUX_LEN;
890		if (*c == '+') {
891			sep->se_type = MUXPLUS_TYPE;
892			c++;
893		} else
894			sep->se_type = MUX_TYPE;
895		sep->se_service = newstr(c);
896	} else {
897		sep->se_service = newstr(arg);
898		sep->se_type = NORM_TYPE;
899	}
900	arg = sskip(&cp);
901	if (strcmp(arg, "stream") == 0)
902		sep->se_socktype = SOCK_STREAM;
903	else if (strcmp(arg, "dgram") == 0)
904		sep->se_socktype = SOCK_DGRAM;
905	else if (strcmp(arg, "rdm") == 0)
906		sep->se_socktype = SOCK_RDM;
907	else if (strcmp(arg, "seqpacket") == 0)
908		sep->se_socktype = SOCK_SEQPACKET;
909	else if (strcmp(arg, "raw") == 0)
910		sep->se_socktype = SOCK_RAW;
911	else
912		sep->se_socktype = -1;
913	sep->se_proto = newstr(sskip(&cp));
914        if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
915                sep->se_proto += 4;
916                sep->se_rpc = 1;
917                sep->se_rpc_prog = sep->se_rpc_lowvers =
918			sep->se_rpc_lowvers = 0;
919                sep->se_ctrladdr.sin_family = AF_INET;
920                sep->se_ctrladdr.sin_port = 0;
921                sep->se_ctrladdr.sin_addr.s_addr = htonl(INADDR_ANY);
922                if ((versp = rindex(sep->se_service, '/'))) {
923                        *versp++ = '\0';
924                        switch (sscanf(versp, "%d-%d",
925                                       &sep->se_rpc_lowvers,
926                                       &sep->se_rpc_highvers)) {
927                        case 2:
928                                break;
929                        case 1:
930                                sep->se_rpc_highvers =
931                                        sep->se_rpc_lowvers;
932                                break;
933                        default:
934                                syslog(LOG_ERR,
935					"bad RPC version specifier; %s\n",
936					sep->se_service);
937                                freeconfig(sep);
938                                goto more;
939                        }
940                }
941                else {
942                        sep->se_rpc_lowvers =
943                                sep->se_rpc_highvers = 1;
944                }
945        }
946	arg = sskip(&cp);
947	sep->se_wait = strcmp(arg, "wait") == 0;
948	if (ISMUX(sep)) {
949		/*
950		 * Silently enforce "nowait" for TCPMUX services since
951		 * they don't have an assigned port to listen on.
952		 */
953		sep->se_wait = 0;
954
955		if (strcmp(sep->se_proto, "tcp")) {
956			syslog(LOG_ERR,
957				"%s: bad protocol for tcpmux service %s",
958				CONFIG, sep->se_service);
959			goto more;
960		}
961		if (sep->se_socktype != SOCK_STREAM) {
962			syslog(LOG_ERR,
963				"%s: bad socket type for tcpmux service %s",
964				CONFIG, sep->se_service);
965			goto more;
966		}
967	}
968	sep->se_user = newstr(sskip(&cp));
969	sep->se_server = newstr(sskip(&cp));
970	if (strcmp(sep->se_server, "internal") == 0) {
971		struct biltin *bi;
972
973		for (bi = biltins; bi->bi_service; bi++)
974			if (bi->bi_socktype == sep->se_socktype &&
975			    strcmp(bi->bi_service, sep->se_service) == 0)
976				break;
977		if (bi->bi_service == 0) {
978			syslog(LOG_ERR, "internal service %s unknown",
979				sep->se_service);
980			goto more;
981		}
982		sep->se_bi = bi;
983		sep->se_wait = bi->bi_wait;
984	} else
985		sep->se_bi = NULL;
986	argc = 0;
987	for (arg = skip(&cp); cp; arg = skip(&cp))
988		if (argc < MAXARGV)
989			sep->se_argv[argc++] = newstr(arg);
990	while (argc <= MAXARGV)
991		sep->se_argv[argc++] = NULL;
992	return (sep);
993}
994
995void
996freeconfig(cp)
997	struct servtab *cp;
998{
999	int i;
1000
1001	if (cp->se_service)
1002		free(cp->se_service);
1003	if (cp->se_proto)
1004		free(cp->se_proto);
1005	if (cp->se_user)
1006		free(cp->se_user);
1007	if (cp->se_server)
1008		free(cp->se_server);
1009	for (i = 0; i < MAXARGV; i++)
1010		if (cp->se_argv[i])
1011			free(cp->se_argv[i]);
1012}
1013
1014
1015/*
1016 * Safe skip - if skip returns null, log a syntax error in the
1017 * configuration file and exit.
1018 */
1019char *
1020sskip(cpp)
1021	char **cpp;
1022{
1023	char *cp;
1024
1025	cp = skip(cpp);
1026	if (cp == NULL) {
1027		syslog(LOG_ERR, "%s: syntax error", CONFIG);
1028		exit(-1);
1029	}
1030	return (cp);
1031}
1032
1033char *
1034skip(cpp)
1035	char **cpp;
1036{
1037	char *cp = *cpp;
1038	char *start;
1039	char quote = '\0';
1040
1041again:
1042	while (*cp == ' ' || *cp == '\t')
1043		cp++;
1044	if (*cp == '\0') {
1045		int c;
1046
1047		c = getc(fconfig);
1048		(void) ungetc(c, fconfig);
1049		if (c == ' ' || c == '\t')
1050			if (cp = nextline(fconfig))
1051				goto again;
1052		*cpp = (char *)0;
1053		return ((char *)0);
1054	}
1055	if (*cp == '"' || *cp == '\'')
1056		quote = *cp++;
1057	start = cp;
1058	if (quote)
1059		while (*cp && *cp != quote)
1060			cp++;
1061	else
1062		while (*cp && *cp != ' ' && *cp != '\t')
1063			cp++;
1064	if (*cp != '\0')
1065		*cp++ = '\0';
1066	*cpp = cp;
1067	return (start);
1068}
1069
1070char *
1071nextline(fd)
1072	FILE *fd;
1073{
1074	char *cp;
1075
1076	if (fgets(line, sizeof (line), fd) == NULL)
1077		return ((char *)0);
1078	cp = strchr(line, '\n');
1079	if (cp)
1080		*cp = '\0';
1081	return (line);
1082}
1083
1084char *
1085newstr(cp)
1086	char *cp;
1087{
1088	if (cp = strdup(cp ? cp : ""))
1089		return (cp);
1090	syslog(LOG_ERR, "strdup: %m");
1091	exit(-1);
1092}
1093
1094#ifdef OLD_SETPROCTITLE
1095void
1096inetd_setproctitle(a, s)
1097	char *a;
1098	int s;
1099{
1100	int size;
1101	char *cp;
1102	struct sockaddr_in sin;
1103	char buf[80];
1104
1105	cp = Argv[0];
1106	size = sizeof(sin);
1107	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
1108		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
1109	else
1110		(void) sprintf(buf, "-%s", a);
1111	strncpy(cp, buf, LastArg - cp);
1112	cp += strlen(cp);
1113	while (cp < LastArg)
1114		*cp++ = ' ';
1115}
1116#else
1117void
1118inetd_setproctitle(a, s)
1119	char *a;
1120	int s;
1121{
1122	int size;
1123	struct sockaddr_in sin;
1124	char buf[80];
1125
1126	size = sizeof(sin);
1127	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
1128		(void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr));
1129	else
1130		(void) sprintf(buf, "%s", a);
1131	setproctitle("%s", buf);
1132}
1133#endif
1134
1135
1136/*
1137 * Internet services provided internally by inetd:
1138 */
1139#define	BUFSIZE	8192
1140
1141/* ARGSUSED */
1142void
1143echo_stream(s, sep)		/* Echo service -- echo data back */
1144	int s;
1145	struct servtab *sep;
1146{
1147	char buffer[BUFSIZE];
1148	int i;
1149
1150	inetd_setproctitle(sep->se_service, s);
1151	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
1152	    write(s, buffer, i) > 0)
1153		;
1154	exit(0);
1155}
1156
1157int check_loop(sin, sep)
1158	struct sockaddr_in *sin;
1159	struct servtab *sep;
1160{
1161	struct servtab *se2;
1162
1163	for (se2 = servtab; se2; se2 = se2->se_next) {
1164		if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
1165			continue;
1166
1167		if (sin->sin_port == se2->se_ctrladdr.sin_port) {
1168			syslog(LOG_WARNING,
1169			       "%s/%s:%s/%s loop request REFUSED from %s",
1170			       sep->se_service, sep->se_proto,
1171			       se2->se_service, se2->se_proto,
1172			       inet_ntoa(sin->sin_addr));
1173			return 1;
1174		}
1175	}
1176	return 0;
1177}
1178
1179/* ARGSUSED */
1180void
1181echo_dg(s, sep)			/* Echo service -- echo data back */
1182	int s;
1183	struct servtab *sep;
1184{
1185	char buffer[BUFSIZE];
1186	int i, size;
1187	struct sockaddr_in sin;
1188
1189	size = sizeof(sin);
1190	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
1191			  (struct sockaddr *)&sin, &size)) < 0)
1192		return;
1193
1194	if (check_loop(&sin, sep))
1195		return;
1196
1197	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin,
1198		      sizeof(sin));
1199}
1200
1201/* ARGSUSED */
1202void
1203discard_stream(s, sep)		/* Discard service -- ignore data */
1204	int s;
1205	struct servtab *sep;
1206{
1207	int ret;
1208	char buffer[BUFSIZE];
1209
1210	inetd_setproctitle(sep->se_service, s);
1211	while (1) {
1212		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
1213			;
1214		if (ret == 0 || errno != EINTR)
1215			break;
1216	}
1217	exit(0);
1218}
1219
1220/* ARGSUSED */
1221void
1222discard_dg(s, sep)		/* Discard service -- ignore data */
1223	int s;
1224	struct servtab *sep;
1225{
1226	char buffer[BUFSIZE];
1227
1228	(void) read(s, buffer, sizeof(buffer));
1229}
1230
1231#include <ctype.h>
1232#define LINESIZ 72
1233char ring[128];
1234char *endring;
1235
1236void
1237initring()
1238{
1239	int i;
1240
1241	endring = ring;
1242
1243	for (i = 0; i <= 128; ++i)
1244		if (isprint(i))
1245			*endring++ = i;
1246}
1247
1248/* ARGSUSED */
1249void
1250chargen_stream(s, sep)		/* Character generator */
1251	int s;
1252	struct servtab *sep;
1253{
1254	int len;
1255	char *rs, text[LINESIZ+2];
1256
1257	inetd_setproctitle(sep->se_service, s);
1258
1259	if (!endring) {
1260		initring();
1261		rs = ring;
1262	}
1263
1264	text[LINESIZ] = '\r';
1265	text[LINESIZ + 1] = '\n';
1266	for (rs = ring;;) {
1267		if ((len = endring - rs) >= LINESIZ)
1268			memmove(text, rs, LINESIZ);
1269		else {
1270			memmove(text, rs, len);
1271			memmove(text + len, ring, LINESIZ - len);
1272		}
1273		if (++rs == endring)
1274			rs = ring;
1275		if (write(s, text, sizeof(text)) != sizeof(text))
1276			break;
1277	}
1278	exit(0);
1279}
1280
1281/* ARGSUSED */
1282void
1283chargen_dg(s, sep)		/* Character generator */
1284	int s;
1285	struct servtab *sep;
1286{
1287	struct sockaddr_in sin;
1288	static char *rs;
1289	int len, size;
1290	char text[LINESIZ+2];
1291
1292	if (endring == 0) {
1293		initring();
1294		rs = ring;
1295	}
1296
1297	size = sizeof(sin);
1298	if (recvfrom(s, text, sizeof(text), 0,
1299		     (struct sockaddr *)&sin, &size) < 0)
1300		return;
1301
1302	if (check_loop(&sin, sep))
1303		return;
1304
1305	if ((len = endring - rs) >= LINESIZ)
1306		memmove(text, rs, LINESIZ);
1307	else {
1308		memmove(text, rs, len);
1309		memmove(text + len, ring, LINESIZ - len);
1310	}
1311	if (++rs == endring)
1312		rs = ring;
1313	text[LINESIZ] = '\r';
1314	text[LINESIZ + 1] = '\n';
1315	(void) sendto(s, text, sizeof(text), 0,
1316		      (struct sockaddr *)&sin, sizeof(sin));
1317}
1318
1319/*
1320 * Return a machine readable date and time, in the form of the
1321 * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
1322 * returns the number of seconds since midnight, Jan 1, 1970,
1323 * we must add 2208988800 seconds to this figure to make up for
1324 * some seventy years Bell Labs was asleep.
1325 */
1326
1327long
1328machtime()
1329{
1330	struct timeval tv;
1331
1332	if (gettimeofday(&tv, (struct timezone *)0) < 0) {
1333		if (debug)
1334			fprintf(stderr, "Unable to get time of day\n");
1335		return (0L);
1336	}
1337#define	OFFSET ((u_long)25567 * 24*60*60)
1338	return (htonl((long)(tv.tv_sec + OFFSET)));
1339#undef OFFSET
1340}
1341
1342/* ARGSUSED */
1343void
1344machtime_stream(s, sep)
1345	int s;
1346	struct servtab *sep;
1347{
1348	long result;
1349
1350	result = machtime();
1351	(void) write(s, (char *) &result, sizeof(result));
1352}
1353
1354/* ARGSUSED */
1355void
1356machtime_dg(s, sep)
1357	int s;
1358	struct servtab *sep;
1359{
1360	long result;
1361	struct sockaddr_in sin;
1362	int size;
1363
1364	size = sizeof(sin);
1365	if (recvfrom(s, (char *)&result, sizeof(result), 0,
1366		     (struct sockaddr *)&sin, &size) < 0)
1367		return;
1368
1369	if (check_loop(&sin, sep))
1370		return;
1371
1372	result = machtime();
1373	(void) sendto(s, (char *) &result, sizeof(result), 0,
1374		      (struct sockaddr *)&sin, sizeof(sin));
1375}
1376
1377/* ARGSUSED */
1378void
1379daytime_stream(s, sep)		/* Return human-readable time of day */
1380	int s;
1381	struct servtab *sep;
1382{
1383	char buffer[256];
1384	time_t clock;
1385
1386	clock = time((time_t *) 0);
1387
1388	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
1389	(void) write(s, buffer, strlen(buffer));
1390}
1391
1392/* ARGSUSED */
1393void
1394daytime_dg(s, sep)		/* Return human-readable time of day */
1395	int s;
1396	struct servtab *sep;
1397{
1398	char buffer[256];
1399	time_t clock;
1400	struct sockaddr_in sin;
1401	int size;
1402
1403	clock = time((time_t *) 0);
1404
1405	size = sizeof(sin);
1406	if (recvfrom(s, buffer, sizeof(buffer), 0,
1407		     (struct sockaddr *)&sin, &size) < 0)
1408		return;
1409
1410	if (check_loop(&sin, sep))
1411		return;
1412
1413	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
1414	(void) sendto(s, buffer, strlen(buffer), 0,
1415		      (struct sockaddr *)&sin, sizeof(sin));
1416}
1417
1418/*
1419 * print_service:
1420 *	Dump relevant information to stderr
1421 */
1422void
1423print_service(action, sep)
1424	char *action;
1425	struct servtab *sep;
1426{
1427	if(sep->se_rpc)
1428		fprintf(stderr,
1429	    		"%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
1430	    		action, sep->se_service, sep->se_proto,
1431	    		sep->se_wait, sep->se_user, (int)sep->se_bi,
1432			sep->se_server);
1433	else
1434		fprintf(stderr,
1435			"%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
1436			action, sep->se_service, sep->se_proto,
1437			sep->se_wait, sep->se_user, (int)sep->se_bi,
1438			sep->se_server);
1439}
1440
1441/*
1442 *  Based on TCPMUX.C by Mark K. Lottor November 1988
1443 *  sri-nic::ps:<mkl>tcpmux.c
1444 */
1445
1446
1447static int		/* # of characters upto \r,\n or \0 */
1448getline(fd, buf, len)
1449	int fd;
1450	char *buf;
1451	int len;
1452{
1453	int count = 0, n;
1454
1455	do {
1456		n = read(fd, buf, len-count);
1457		if (n == 0)
1458			return (count);
1459		if (n < 0)
1460			return (-1);
1461		while (--n >= 0) {
1462			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
1463				return (count);
1464			count++;
1465			buf++;
1466		}
1467	} while (count < len);
1468	return (count);
1469}
1470
1471#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
1472
1473#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
1474
1475struct servtab *
1476tcpmux(s)
1477	int s;
1478{
1479	struct servtab *sep;
1480	char service[MAX_SERV_LEN+1];
1481	int len;
1482
1483	/* Get requested service name */
1484	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
1485		strwrite(s, "-Error reading service name\r\n");
1486		return (NULL);
1487	}
1488	service[len] = '\0';
1489
1490	if (debug)
1491		fprintf(stderr, "tcpmux: someone wants %s\n", service);
1492
1493	/*
1494	 * Help is a required command, and lists available services,
1495	 * one per line.
1496	 */
1497	if (!strcasecmp(service, "help")) {
1498		for (sep = servtab; sep; sep = sep->se_next) {
1499			if (!ISMUX(sep))
1500				continue;
1501			(void)write(s,sep->se_service,strlen(sep->se_service));
1502			strwrite(s, "\r\n");
1503		}
1504		return (NULL);
1505	}
1506
1507	/* Try matching a service in inetd.conf with the request */
1508	for (sep = servtab; sep; sep = sep->se_next) {
1509		if (!ISMUX(sep))
1510			continue;
1511		if (!strcasecmp(service, sep->se_service)) {
1512			if (ISMUXPLUS(sep)) {
1513				strwrite(s, "+Go\r\n");
1514			}
1515			return (sep);
1516		}
1517	}
1518	strwrite(s, "-Service not available\r\n");
1519	return (NULL);
1520}
1521