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