xref: /openbsd-src/usr.sbin/inetd/inetd.c (revision 8445c53715e7030056b779e8ab40efb7820981f2)
1 /*	$OpenBSD: inetd.c,v 1.85 2001/09/04 23:35:59 millert Exp $	*/
2 /*	$NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $	*/
3 /*
4  * Copyright (c) 1983,1991 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #ifndef lint
37 char copyright[] =
38 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
39  All rights reserved.\n";
40 #endif /* not lint */
41 
42 #ifndef lint
43 /*static char sccsid[] = "from: @(#)inetd.c	5.30 (Berkeley) 6/3/91";*/
44 static char rcsid[] = "$OpenBSD: inetd.c,v 1.85 2001/09/04 23:35:59 millert Exp $";
45 #endif /* not lint */
46 
47 /*
48  * Inetd - Internet super-server
49  *
50  * This program invokes all internet services as needed.
51  * connection-oriented services are invoked each time a
52  * connection is made, by creating a process.  This process
53  * is passed the connection as file descriptor 0 and is
54  * expected to do a getpeername to find out the source host
55  * and port.
56  *
57  * Datagram oriented services are invoked when a datagram
58  * arrives; a process is created and passed a pending message
59  * on file descriptor 0.  Datagram servers may either connect
60  * to their peer, freeing up the original socket for inetd
61  * to receive further messages on, or ``take over the socket'',
62  * processing all arriving datagrams and, eventually, timing
63  * out.	 The first type of server is said to be ``multi-threaded'';
64  * the second type of server ``single-threaded''.
65  *
66  * Inetd uses a configuration file which is read at startup
67  * and, possibly, at some later time in response to a hangup signal.
68  * The configuration file is ``free format'' with fields given in the
69  * order shown below.  Continuation lines for an entry must begin with
70  * a space or tab.  All fields must be present in each entry.
71  *
72  *	service name			must be in /etc/services
73  *	socket type			stream/dgram/raw/rdm/seqpacket
74  *	protocol			must be in /etc/protocols
75  *	wait/nowait[.max]		single-threaded/multi-threaded, max #
76  *	user[.group] or user[:group]	user/group to run daemon as
77  *	server program			full path name
78  *	server program arguments	maximum of MAXARGS (20)
79  *
80  * For RPC services
81  *      service name/version            must be in /etc/rpc
82  *	socket type			stream/dgram/raw/rdm/seqpacket
83  *	protocol			must be in /etc/protocols
84  *	wait/nowait[.max]		single-threaded/multi-threaded
85  *	user[.group] or user[:group]	user to run daemon as
86  *	server program			full path name
87  *	server program arguments	maximum of MAXARGS (20)
88  *
89  * For non-RPC services, the "service name" can be of the form
90  * hostaddress:servicename, in which case the hostaddress is used
91  * as the host portion of the address to listen on.  If hostaddress
92  * consists of a single `*' character, INADDR_ANY is used.
93  *
94  * A line can also consist of just
95  *      hostaddress:
96  * where hostaddress is as in the preceding paragraph.  Such a line must
97  * have no further fields; the specified hostaddress is remembered and
98  * used for all further lines that have no hostaddress specified,
99  * until the next such line (or EOF).  (This is why * is provided to
100  * allow explicit specification of INADDR_ANY.)  A line
101  *      *:
102  * is implicitly in effect at the beginning of the file.
103  *
104  * The hostaddress specifier may (and often will) contain dots;
105  * the service name must not.
106  *
107  * For RPC services, host-address specifiers are accepted and will
108  * work to some extent; however, because of limitations in the
109  * portmapper interface, it will not work to try to give more than
110  * one line for any given RPC service, even if the host-address
111  * specifiers are different.
112  *
113  * Comment lines are indicated by a `#' in column 1.
114  */
115 
116 /*
117  * Here's the scoop concerning the user[.:]group feature:
118  *
119  * 1) set-group-option off.
120  *
121  * 	a) user = root:	NO setuid() or setgid() is done
122  *
123  * 	b) other:	setgid(primary group as found in passwd)
124  * 			initgroups(name, primary group)
125  * 			setuid()
126  *
127  * 2) set-group-option on.
128  *
129  * 	a) user = root:	setgid(specified group)
130  * 			NO initgroups()
131  * 			NO setuid()
132  *
133  * 	b) other:	setgid(specified group)
134  * 			initgroups(name, specified group)
135  * 			setuid()
136  *
137  */
138 
139 #include <sys/param.h>
140 #include <sys/stat.h>
141 #include <sys/ioctl.h>
142 #include <sys/socket.h>
143 #include <sys/un.h>
144 #include <sys/file.h>
145 #include <sys/wait.h>
146 #include <sys/time.h>
147 #include <sys/resource.h>
148 
149 #ifndef RLIMIT_NOFILE
150 #define RLIMIT_NOFILE	RLIMIT_OFILE
151 #endif
152 
153 #include <netinet/in.h>
154 #include <arpa/inet.h>
155 
156 #include <errno.h>
157 #include <ctype.h>
158 #include <signal.h>
159 #include <netdb.h>
160 #include <syslog.h>
161 #include <pwd.h>
162 #include <grp.h>
163 #include <stdio.h>
164 #include <stdlib.h>
165 #include <unistd.h>
166 #include <string.h>
167 #include <login_cap.h>
168 #include <rpc/rpc.h>
169 #include <rpc/pmap_clnt.h>
170 #include <rpcsvc/nfs_prot.h>
171 #include "pathnames.h"
172 
173 #define	TOOMANY		256		/* don't start more than TOOMANY */
174 #define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
175 #define	RETRYTIME	(60*10)		/* retry after bind or server fail */
176 
177 void	config __P((int));
178 void	doconfig __P((void));
179 void	reap __P((int));
180 void	doreap __P((void));
181 void	retry __P((int));
182 void	doretry __P((void));
183 void	goaway __P((int));
184 
185 int	 debug = 0;
186 int	 nsock, maxsock;
187 fd_set	*allsockp;
188 int	 allsockn;
189 int	 toomany = TOOMANY;
190 int	 options;
191 int	 timingout;
192 struct	 servent *sp;
193 char	*curdom;
194 uid_t	 uid;
195 sigset_t blockmask;
196 sigset_t emptymask;
197 
198 #ifndef OPEN_MAX
199 #define OPEN_MAX	64
200 #endif
201 
202 /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
203 #define FD_MARGIN	(8)
204 typeof(((struct rlimit *)0)->rlim_cur)	rlim_ofile_cur = OPEN_MAX;
205 
206 #ifdef RLIMIT_NOFILE
207 struct rlimit	rlim_ofile;
208 #endif
209 
210 struct	servtab {
211 	char	*se_hostaddr;		/* host address to listen on */
212 	char	*se_service;		/* name of service */
213 	int	se_socktype;		/* type of socket to use */
214 	int	se_family;		/* address family */
215 	char	*se_proto;		/* protocol used */
216 	int	se_rpcprog;		/* rpc program number */
217 	int	se_rpcversl;		/* rpc program lowest version */
218 	int	se_rpcversh;		/* rpc program highest version */
219 #define isrpcservice(sep)	((sep)->se_rpcversl != 0)
220 	pid_t	se_wait;		/* single threaded server */
221 	short	se_checked;		/* looked at during merge */
222 	char	*se_user;		/* user name to run as */
223 	char	*se_group;		/* group name to run as */
224 	struct	biltin *se_bi;		/* if built-in, description */
225 	char	*se_server;		/* server program */
226 #define	MAXARGV 20
227 	char	*se_argv[MAXARGV+1];	/* program arguments */
228 	int	se_fd;			/* open descriptor */
229 	union {
230 		struct	sockaddr se_un_ctrladdr;
231 		struct	sockaddr_in se_un_ctrladdr_in;
232 		struct	sockaddr_in6 se_un_ctrladdr_in6;
233 		struct	sockaddr_un se_un_ctrladdr_un;
234 		struct	sockaddr_storage se_un_ctrladdr_storage;
235 	} se_un;			/* bound address */
236 #define se_ctrladdr	se_un.se_un_ctrladdr
237 #define se_ctrladdr_in	se_un.se_un_ctrladdr_in
238 #define se_ctrladdr_in6	se_un.se_un_ctrladdr_in6
239 #define se_ctrladdr_un	se_un.se_un_ctrladdr_un
240 #define se_ctrladdr_storage	se_un.se_un_ctrladdr_storage
241 	int	se_ctrladdr_size;
242 	int	se_max;			/* max # of instances of this service */
243 	int	se_count;		/* number started since se_time */
244 	struct	timeval se_time;	/* start of se_count */
245 	struct	servtab *se_next;
246 } *servtab;
247 
248 void echo_stream __P((int, struct servtab *));
249 void discard_stream __P((int, struct servtab *));
250 void machtime_stream __P((int, struct servtab *));
251 void daytime_stream __P((int, struct servtab *));
252 void chargen_stream __P((int, struct servtab *));
253 void echo_dg __P((int, struct servtab *));
254 void discard_dg __P((int, struct servtab *));
255 void machtime_dg __P((int, struct servtab *));
256 void daytime_dg __P((int, struct servtab *));
257 void chargen_dg __P((int, struct servtab *));
258 
259 struct biltin {
260 	char	*bi_service;		/* internally provided service name */
261 	int	bi_socktype;		/* type of socket supported */
262 	short	bi_fork;		/* 1 if should fork before call */
263 	short	bi_wait;		/* 1 if should wait for child */
264 	void	(*bi_fn) __P((int, struct servtab *));
265 } biltins[] = {
266 	/* Echo received data */
267 	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream },
268 	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg },
269 
270 	/* Internet /dev/null */
271 	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream },
272 	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg },
273 
274 	/* Return 32 bit time since 1900 */
275 	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream },
276 	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg },
277 
278 	/* Return human-readable time */
279 	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream },
280 	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg },
281 
282 	/* Familiar character generator */
283 	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream },
284 	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg },
285 
286 	{ 0 }
287 };
288 
289 sig_atomic_t wantretry;
290 sig_atomic_t wantconfig;
291 sig_atomic_t wantreap;
292 
293 #define NUMINT	(sizeof(intab) / sizeof(struct inent))
294 char	*CONFIG = _PATH_INETDCONF;
295 char	**Argv;
296 char	*LastArg;
297 char	*progname;
298 
299 void logpid __P((void));
300 
301 void
302 fd_grow(fd_set **fdsp, int *bytes, int fd)
303 {
304 	caddr_t new;
305 	int newbytes;
306 
307 	newbytes = howmany(fd+1, NFDBITS) * sizeof(fd_mask);
308 	if (newbytes > *bytes) {
309 		newbytes *= 2;			/* optimism */
310 		new = realloc(*fdsp, newbytes);
311 		if (new == NULL) {
312 			syslog(LOG_ERR, "Out of memory.");
313 			exit(1);
314 		}
315 		memset(new + *bytes, 0, newbytes - *bytes);
316 		*fdsp = (fd_set *)new;
317 		*bytes = newbytes;
318 	}
319 }
320 
321 int
322 main(argc, argv, envp)
323 	int argc;
324 	char *argv[], *envp[];
325 {
326 	extern char *optarg;
327 	extern int optind;
328 	register struct servtab *sep;
329 	register struct passwd *pwd;
330 	register struct group *grp = NULL;
331 	register int tmpint;
332 	struct sigaction sa, sapipe;
333 	int ch, dofork;
334 	pid_t pid;
335 	char buf[50];
336 	fd_set *readablep = NULL;
337 	int readablen = 0;
338 
339 	Argv = argv;
340 	if (envp == 0 || *envp == 0)
341 		envp = argv;
342 	while (*envp)
343 		envp++;
344 	LastArg = envp[-1] + strlen(envp[-1]);
345 
346 	progname = strrchr(argv[0], '/');
347 	progname = progname ? progname + 1 : argv[0];
348 
349 	while ((ch = getopt(argc, argv, "dR:")) != -1)
350 		switch(ch) {
351 		case 'd':
352 			debug = 1;
353 			options |= SO_DEBUG;
354 			break;
355 		case 'R': {	/* invocation rate */
356 			char *p;
357 			int val;
358 
359 			val = strtoul(optarg, &p, 0);
360 			if (val >= 1 && *p == NULL) {
361 				toomany = val;
362 				break;
363 			}
364 			syslog(LOG_ERR,
365 			    "-R %s: bad value for service invocation rate",
366 			    optarg);
367 			break;
368 		}
369 		case '?':
370 		default:
371 			fprintf(stderr, "usage: %s [-R rate] [-d] [conf]\n",
372 			    progname);
373 			exit(1);
374 		}
375 	argc -= optind;
376 	argv += optind;
377 
378 	uid = getuid();
379 	if (uid != 0)
380 		CONFIG = NULL;
381 	if (argc > 0)
382 		CONFIG = argv[0];
383 	if (CONFIG == NULL) {
384 		fprintf(stderr, "%s: non-root must specify a config file\n",
385 		    progname);
386 		exit(1);
387 	}
388 
389 	if (debug == 0) {
390 		daemon(0, 0);
391 		if (uid == 0)
392 			(void) setlogin("");
393 	}
394 
395 	if (uid == 0) {
396 		gid_t gid = getgid();
397 
398 		/* If run by hand, ensure groups vector gets trashed */
399 		setgroups(1, &gid);
400 	}
401 
402 	openlog(progname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
403 	logpid();
404 
405 #ifdef RLIMIT_NOFILE
406 	if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
407 		syslog(LOG_ERR, "getrlimit: %m");
408 	} else {
409 		rlim_ofile_cur = rlim_ofile.rlim_cur;
410 		if (rlim_ofile_cur == RLIM_INFINITY)	/* ! */
411 			rlim_ofile_cur = OPEN_MAX;
412 	}
413 #endif
414 
415 	sigemptyset(&emptymask);
416 	sigemptyset(&blockmask);
417 	sigaddset(&blockmask, SIGCHLD);
418 	sigaddset(&blockmask, SIGHUP);
419 	sigaddset(&blockmask, SIGALRM);
420 
421 	memset((char *)&sa, 0, sizeof(sa));
422 	sigemptyset(&sa.sa_mask);
423 	sigaddset(&sa.sa_mask, SIGALRM);
424 	sigaddset(&sa.sa_mask, SIGCHLD);
425 	sigaddset(&sa.sa_mask, SIGHUP);
426 	sa.sa_handler = retry;
427 	sigaction(SIGALRM, &sa, NULL);
428 	doconfig();
429 	sa.sa_handler = config;
430 	sigaction(SIGHUP, &sa, NULL);
431 	sa.sa_handler = reap;
432 	sigaction(SIGCHLD, &sa, NULL);
433 	sa.sa_handler = goaway;
434 	sigaction(SIGTERM, &sa, NULL);
435 	sa.sa_handler = goaway;
436 	sigaction(SIGINT, &sa, NULL);
437 	sa.sa_handler = SIG_IGN;
438 	sigaction(SIGPIPE, &sa, &sapipe);
439 
440 	{
441 		/* space for daemons to overwrite environment for ps */
442 #define	DUMMYSIZE	100
443 		char dummy[DUMMYSIZE];
444 
445 		(void)memset(dummy, 'x', DUMMYSIZE - 1);
446 		dummy[DUMMYSIZE - 1] = '\0';
447 
448 		(void)setenv("inetd_dummy", dummy, 1);
449 	}
450 
451 	for (;;) {
452 	    int n, ctrl = -1;
453 
454 	    if (nsock == 0) {
455 		(void) sigprocmask(SIG_BLOCK, &blockmask, NULL);
456 		while (nsock == 0) {
457 		    if (wantretry || wantconfig || wantreap)
458 			break;
459 		    sigsuspend(&emptymask);
460 		}
461 		(void) sigprocmask(SIG_SETMASK, &emptymask, NULL);
462 	    }
463 
464 	    if (wantretry || wantconfig || wantreap) {
465 		if (wantretry) {
466 		    doretry();
467 		    wantretry = 0;
468 		}
469 		if (wantconfig) {
470 		    doconfig();
471 		    wantconfig = 0;
472 		}
473 		if (wantreap) {
474 		    doreap();
475 		    wantreap = 0;
476 		}
477 		continue;
478 	    }
479 
480 	    if (readablen != allsockn) {
481 		if (readablep)
482 		    free(readablep);
483 		readablep = (fd_set *)calloc(allsockn, 1);
484 		if (readablep == NULL) {
485 		    syslog(LOG_ERR, "Out of memory.");
486 		    exit(1);
487 		}
488 		readablen = allsockn;
489 	    }
490 	    bcopy(allsockp, readablep, allsockn);
491 
492 	    if ((n = select(maxsock + 1, readablep, NULL, NULL, NULL)) <= 0) {
493 		    if (n < 0 && errno != EINTR) {
494 			syslog(LOG_WARNING, "select: %m");
495 			sleep(1);
496 		    }
497 		    continue;
498 	    }
499 	    for (sep = servtab; n && sep; sep = sep->se_next)
500 	    if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, readablep)) {
501 		n--;
502 		if (debug)
503 			fprintf(stderr, "someone wants %s\n", sep->se_service);
504 		if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
505 			ctrl = accept(sep->se_fd, NULL, NULL);
506 			if (debug)
507 				fprintf(stderr, "accept, ctrl %d\n", ctrl);
508 			if (ctrl < 0) {
509 				if (errno == EINTR)
510 					continue;
511 				syslog(LOG_WARNING, "accept (for %s): %m",
512 				    sep->se_service);
513 				continue;
514 			}
515 			if (sep->se_family == AF_INET &&
516 			    sep->se_socktype == SOCK_STREAM) {
517 				struct sockaddr_in peer;
518 				int plen = sizeof(peer);
519 
520 				if (getpeername(ctrl, (struct sockaddr *)&peer,
521 				    &plen) < 0) {
522 					syslog(LOG_WARNING, "could not getpeername");
523 					close(ctrl);
524 					continue;
525 				}
526 				if (ntohs(peer.sin_port) == 20) {
527 					/* XXX ftp bounce */
528 					close(ctrl);
529 					continue;
530 				}
531 			}
532 		} else
533 			ctrl = sep->se_fd;
534 		(void) sigprocmask(SIG_BLOCK, &blockmask, NULL);
535 		pid = 0;
536 		dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
537 		if (dofork) {
538 			if (sep->se_count++ == 0)
539 			    (void)gettimeofday(&sep->se_time, NULL);
540 			else if (sep->se_count >= sep->se_max) {
541 				struct timeval now;
542 
543 				(void)gettimeofday(&now, NULL);
544 				if (now.tv_sec - sep->se_time.tv_sec >
545 				    CNT_INTVL) {
546 					sep->se_time = now;
547 					sep->se_count = 1;
548 				} else {
549 					if (!sep->se_wait &&
550 					    sep->se_socktype == SOCK_STREAM)
551 						close(ctrl);
552 					if (sep->se_family == AF_INET &&
553 					    ntohs(sep->se_ctrladdr_in.sin_port) >=
554 					    IPPORT_RESERVED) {
555 						/*
556 						 * Cannot close it -- there are
557 						 * thieves on the system.
558 						 * Simply ignore the connection.
559 						 */
560 						--sep->se_count;
561 						continue;
562 					}
563 					syslog(LOG_ERR,
564 			"%s/%s server failing (looping), service terminated",
565 					    sep->se_service, sep->se_proto);
566 					if (!sep->se_wait &&
567 					    sep->se_socktype == SOCK_STREAM)
568 						close(ctrl);
569 					FD_CLR(sep->se_fd, allsockp);
570 					(void) close(sep->se_fd);
571 					sep->se_fd = -1;
572 					sep->se_count = 0;
573 					nsock--;
574 					sigprocmask(SIG_SETMASK, &emptymask,
575 					    NULL);
576 					if (!timingout) {
577 						timingout = 1;
578 						alarm(RETRYTIME);
579 					}
580 					continue;
581 				}
582 			}
583 			pid = fork();
584 		}
585 		if (pid < 0) {
586 			syslog(LOG_ERR, "fork: %m");
587 			if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
588 				close(ctrl);
589 			sigprocmask(SIG_SETMASK, &emptymask, NULL);
590 			sleep(1);
591 			continue;
592 		}
593 		if (pid && sep->se_wait) {
594 			sep->se_wait = pid;
595 			FD_CLR(sep->se_fd, allsockp);
596 			nsock--;
597 		}
598 		sigprocmask(SIG_SETMASK, &emptymask, NULL);
599 		if (pid == 0) {
600 			if (sep->se_bi)
601 				(*sep->se_bi->bi_fn)(ctrl, sep);
602 			else {
603 				if ((pwd = getpwnam(sep->se_user)) == NULL) {
604 					syslog(LOG_ERR,
605 					    "getpwnam: %s: No such user",
606 					    sep->se_user);
607 					if (sep->se_socktype != SOCK_STREAM)
608 						recv(0, buf, sizeof (buf), 0);
609 					exit(1);
610 				}
611 				if (setsid() <0)
612 					syslog(LOG_ERR, "%s: setsid: %m",
613 					    sep->se_service);
614 				if (sep->se_group &&
615 				    (grp = getgrnam(sep->se_group)) == NULL) {
616 					syslog(LOG_ERR,
617 					    "getgrnam: %s: No such group",
618 					    sep->se_group);
619 					if (sep->se_socktype != SOCK_STREAM)
620 						recv(0, buf, sizeof (buf), 0);
621 					exit(1);
622 				}
623 				if (uid != 0) {
624 					/* a user running private inetd */
625 					if (uid != pwd->pw_uid)
626 						exit(1);
627 				} else {
628 					tmpint = LOGIN_SETALL &
629 					    ~(LOGIN_SETGROUP|LOGIN_SETLOGIN);
630 					if (pwd->pw_uid)
631 						tmpint |= LOGIN_SETGROUP|LOGIN_SETLOGIN;
632 					if (sep->se_group) {
633 						pwd->pw_gid = grp->gr_gid;
634 						tmpint |= LOGIN_SETGROUP;
635 					}
636 					if (setusercontext(0, pwd, pwd->pw_uid,
637 					    tmpint) < 0)
638 						syslog(LOG_ERR,
639 						    "%s/%s: setusercontext: %m",
640 						    sep->se_service,
641 						    sep->se_proto);
642 				}
643 				if (debug)
644 					fprintf(stderr, "%d execl %s\n",
645 					    getpid(), sep->se_server);
646 				dup2(ctrl, 0);
647 				close(ctrl);
648 				dup2(0, 1);
649 				dup2(0, 2);
650 				closelog();
651 				for (tmpint = rlim_ofile_cur-1; --tmpint > 2; )
652 					(void)close(tmpint);
653 				sigaction(SIGPIPE, &sapipe, NULL);
654 				execv(sep->se_server, sep->se_argv);
655 				if (sep->se_socktype != SOCK_STREAM)
656 					recv(0, buf, sizeof (buf), 0);
657 				syslog(LOG_ERR, "execv %s: %m", sep->se_server);
658 				exit(1);
659 			}
660 		}
661 		if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
662 			close(ctrl);
663 	    }
664 	}
665 }
666 
667 int
668 dg_badinput(sa)
669 	struct sockaddr *sa;
670 {
671 	struct in_addr in;
672 	struct in6_addr *in6;
673 	u_int16_t port;
674 
675 	switch (sa->sa_family) {
676 	case AF_INET:
677 		in.s_addr = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
678 		port = ntohs(((struct sockaddr_in *)sa)->sin_port);
679 	v4chk:
680 		if (IN_MULTICAST(in.s_addr))
681 			goto bad;
682 		switch ((in.s_addr & 0xff000000) >> 24) {
683 		case 0: case 127: case 255:
684 			goto bad;
685 		}
686 		/* XXX check for subnet broadcast using getifaddrs(3) */
687 		break;
688 	case AF_INET6:
689 		in6 = &((struct sockaddr_in6 *)sa)->sin6_addr;
690 		port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
691 		if (IN6_IS_ADDR_MULTICAST(in6) || IN6_IS_ADDR_UNSPECIFIED(in6))
692 			goto bad;
693 		/*
694 		 * OpenBSD does not support IPv4 mapped adderss (RFC2553
695 		 * inbound behavior) at all.  We should drop it.
696 		 */
697 		if (IN6_IS_ADDR_V4MAPPED(in6))
698 			goto bad;
699 		if (IN6_IS_ADDR_V4COMPAT(in6)) {
700 			memcpy(&in, &in6->s6_addr[12], sizeof(in));
701 			in.s_addr = ntohl(in.s_addr);
702 			goto v4chk;
703 		}
704 		break;
705 	default:
706 		/* XXX unsupported af, is it safe to assume it to be safe? */
707 		return 0;
708 	}
709 
710 	if (port < IPPORT_RESERVED || port == NFS_PORT)
711 		goto bad;
712 
713 	return (0);
714 
715 bad:
716 	return (1);
717 }
718 
719 void
720 reap(int sig)
721 {
722 	wantreap = 1;
723 }
724 
725 void
726 doreap(void)
727 {
728 	pid_t pid;
729 	int status;
730 	register struct servtab *sep;
731 
732 	if (debug)
733 		fprintf(stderr, "reaping asked for\n");
734 
735 	for (;;) {
736 		pid = wait3(&status, WNOHANG, NULL);
737 		if (pid <= 0)
738 			break;
739 		if (debug)
740 			fprintf(stderr, "%d reaped, status %x\n", pid, status);
741 		for (sep = servtab; sep; sep = sep->se_next)
742 			if (sep->se_wait == pid) {
743 				if (WIFEXITED(status) && WEXITSTATUS(status))
744 					syslog(LOG_WARNING,
745 					    "%s: exit status 0x%x",
746 					    sep->se_server, WEXITSTATUS(status));
747 				else if (WIFSIGNALED(status))
748 					syslog(LOG_WARNING,
749 					    "%s: exit signal 0x%x",
750 					    sep->se_server, WTERMSIG(status));
751 				sep->se_wait = 1;
752 				fd_grow(&allsockp, &allsockn, sep->se_fd);
753 				FD_SET(sep->se_fd, allsockp);
754 				nsock++;
755 				if (debug)
756 					fprintf(stderr, "restored %s, fd %d\n",
757 					    sep->se_service, sep->se_fd);
758 			}
759 	}
760 }
761 
762 int setconfig __P((void));
763 void endconfig __P((void));
764 
765 void register_rpc __P((struct servtab *));
766 void unregister_rpc __P((struct servtab *));
767 void freeconfig __P((struct servtab *));
768 void print_service __P((char *, struct servtab *));
769 void setup __P((struct servtab *));
770 struct servtab *getconfigent __P((void));
771 struct servtab *enter __P((struct servtab *));
772 int matchconf __P((struct servtab *, struct servtab *));
773 
774 void
775 config(int sig)
776 {
777 	wantconfig = 1;
778 }
779 
780 void
781 doconfig(void)
782 {
783 	register struct servtab *sep, *cp, **sepp;
784 	int n, add;
785 	char protoname[10];
786 	sigset_t omask;
787 
788 	if (!setconfig()) {
789 		syslog(LOG_ERR, "%s: %m", CONFIG);
790 		return;
791 	}
792 	for (sep = servtab; sep; sep = sep->se_next)
793 		sep->se_checked = 0;
794 	cp = getconfigent();
795 	while (cp != NULL) {
796 		for (sep = servtab; sep; sep = sep->se_next)
797 			if (matchconf(sep, cp))
798 				break;
799 		add = 0;
800 		if (sep != 0) {
801 			int i;
802 
803 #define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
804 
805 			sigprocmask(SIG_BLOCK, &blockmask, &omask);
806 			/*
807 			 * sep->se_wait may be holding the pid of a daemon
808 			 * that we're waiting for.  If so, don't overwrite
809 			 * it unless the config file explicitly says don't
810 			 * wait.
811 			 */
812 			if (cp->se_bi == 0 &&
813 			    (sep->se_wait == 1 || cp->se_wait == 0))
814 				sep->se_wait = cp->se_wait;
815 			SWAP(int, cp->se_max, sep->se_max);
816 			SWAP(char *, sep->se_user, cp->se_user);
817 			SWAP(char *, sep->se_group, cp->se_group);
818 			SWAP(char *, sep->se_server, cp->se_server);
819 			for (i = 0; i < MAXARGV; i++)
820 				SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
821 #undef SWAP
822 			if (isrpcservice(sep))
823 				unregister_rpc(sep);
824 			sep->se_rpcversl = cp->se_rpcversl;
825 			sep->se_rpcversh = cp->se_rpcversh;
826 			sigprocmask(SIG_SETMASK, &omask, NULL);
827 			freeconfig(cp);
828 			add = 1;
829 		} else {
830 			sep = enter(cp);
831 		}
832 		sep->se_checked = 1;
833 
834 		switch (sep->se_family) {
835 		case AF_UNIX:
836 			if (sep->se_fd != -1)
837 				break;
838 			(void)unlink(sep->se_service);
839 			n = strlen(sep->se_service);
840 			if (n > sizeof sep->se_ctrladdr_un.sun_path - 1)
841 				n = sizeof sep->se_ctrladdr_un.sun_path - 1;
842 			strncpy(sep->se_ctrladdr_un.sun_path,
843 			    sep->se_service, n);
844 			sep->se_ctrladdr_un.sun_path[n] = '\0';
845 			sep->se_ctrladdr_un.sun_family = AF_UNIX;
846 			sep->se_ctrladdr_size = n +
847 			    sizeof sep->se_ctrladdr_un.sun_family;
848 			setup(sep);
849 			break;
850 		case AF_INET:
851 			sep->se_ctrladdr_in.sin_family = AF_INET;
852 			/* se_ctrladdr_in was set in getconfigent */
853 			sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
854 
855 			if (isrpcservice(sep)) {
856 				struct rpcent *rp;
857 
858 				sep->se_rpcprog = atoi(sep->se_service);
859 				if (sep->se_rpcprog == 0) {
860 					rp = getrpcbyname(sep->se_service);
861 					if (rp == 0) {
862 						syslog(LOG_ERR,
863 						    "%s: unknown rpc service",
864 						    sep->se_service);
865 						goto serv_unknown;
866 					}
867 					sep->se_rpcprog = rp->r_number;
868 				}
869 				if (sep->se_fd == -1)
870 					setup(sep);
871 				if (sep->se_fd != -1)
872 					register_rpc(sep);
873 			} else {
874 				u_short port = htons(atoi(sep->se_service));
875 
876 				if (!port) {
877 					/*XXX*/
878 					strncpy(protoname, sep->se_proto,
879 						sizeof(protoname));
880 					if (isdigit(protoname[strlen(protoname) - 1]))
881 						protoname[strlen(protoname) - 1] = '\0';
882 					sp = getservbyname(sep->se_service,
883 					    protoname);
884 					if (sp == 0) {
885 						syslog(LOG_ERR,
886 						    "%s/%s: unknown service",
887 						    sep->se_service, sep->se_proto);
888 						goto serv_unknown;
889 					}
890 					port = sp->s_port;
891 				}
892 				if (port != sep->se_ctrladdr_in.sin_port) {
893 					sep->se_ctrladdr_in.sin_port = port;
894 					if (sep->se_fd != -1) {
895 						FD_CLR(sep->se_fd, allsockp);
896 						nsock--;
897 						(void) close(sep->se_fd);
898 					}
899 					sep->se_fd = -1;
900 				}
901 				if (sep->se_fd == -1)
902 					setup(sep);
903 			}
904 			break;
905 		case AF_INET6:
906 			sep->se_ctrladdr_in6.sin6_family = AF_INET6;
907 			/* se_ctrladdr_in was set in getconfigent */
908 			sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in6;
909 
910 			if (isrpcservice(sep)) {
911 				struct rpcent *rp;
912 
913 				sep->se_rpcprog = atoi(sep->se_service);
914 				if (sep->se_rpcprog == 0) {
915 					rp = getrpcbyname(sep->se_service);
916 					if (rp == 0) {
917 						syslog(LOG_ERR,
918 						    "%s: unknown rpc service",
919 						    sep->se_service);
920 						goto serv_unknown;
921 					}
922 					sep->se_rpcprog = rp->r_number;
923 				}
924 				if (sep->se_fd == -1)
925 					setup(sep);
926 				if (sep->se_fd != -1)
927 					register_rpc(sep);
928 			} else {
929 				u_short port = htons(atoi(sep->se_service));
930 
931 				if (!port) {
932 					/*XXX*/
933 					strncpy(protoname, sep->se_proto,
934 						sizeof(protoname));
935 					if (isdigit(protoname[strlen(protoname) - 1]))
936 						protoname[strlen(protoname) - 1] = '\0';
937 					sp = getservbyname(sep->se_service,
938 					    protoname);
939 					if (sp == 0) {
940 						syslog(LOG_ERR,
941 						    "%s/%s: unknown service",
942 						    sep->se_service, sep->se_proto);
943 						goto serv_unknown;
944 					}
945 					port = sp->s_port;
946 				}
947 				if (port != sep->se_ctrladdr_in6.sin6_port) {
948 					sep->se_ctrladdr_in6.sin6_port = port;
949 					if (sep->se_fd != -1) {
950 						FD_CLR(sep->se_fd, allsockp);
951 						nsock--;
952 						(void) close(sep->se_fd);
953 					}
954 					sep->se_fd = -1;
955 				}
956 				if (sep->se_fd == -1)
957 					setup(sep);
958 			}
959 			break;
960 		}
961 	serv_unknown:
962 		if (cp->se_next != NULL) {
963 			struct servtab *tmp = cp;
964 
965 			cp = cp->se_next;
966 			free(tmp);
967 		} else {
968 			free(cp);
969 			cp = getconfigent();
970 		}
971 		if (debug)
972 			print_service(add ? "REDO" : "ADD", sep);
973 	}
974 	endconfig();
975 	/*
976 	 * Purge anything not looked at above.
977 	 */
978 	sigprocmask(SIG_BLOCK, &blockmask, &omask);
979 	sepp = &servtab;
980 	while ((sep = *sepp)) {
981 		if (sep->se_checked) {
982 			sepp = &sep->se_next;
983 			continue;
984 		}
985 		*sepp = sep->se_next;
986 		if (sep->se_fd != -1) {
987 			FD_CLR(sep->se_fd, allsockp);
988 			nsock--;
989 			(void) close(sep->se_fd);
990 		}
991 		if (isrpcservice(sep))
992 			unregister_rpc(sep);
993 		if (sep->se_family == AF_UNIX)
994 			(void)unlink(sep->se_service);
995 		if (debug)
996 			print_service("FREE", sep);
997 		freeconfig(sep);
998 		free((char *)sep);
999 	}
1000 	sigprocmask(SIG_SETMASK, &omask, NULL);
1001 }
1002 
1003 void
1004 retry(int sig)
1005 {
1006 	wantretry = 1;
1007 }
1008 
1009 void
1010 doretry(void)
1011 {
1012 	register struct servtab *sep;
1013 
1014 	timingout = 0;
1015 	for (sep = servtab; sep; sep = sep->se_next) {
1016 		if (sep->se_fd == -1) {
1017 			switch (sep->se_family) {
1018 			case AF_UNIX:
1019 			case AF_INET:
1020 			case AF_INET6:
1021 				setup(sep);
1022 				if (sep->se_fd != -1 && isrpcservice(sep))
1023 					register_rpc(sep);
1024 				break;
1025 			}
1026 		}
1027 	}
1028 }
1029 
1030 void
1031 goaway(sig)
1032 	int sig;
1033 {
1034 	register struct servtab *sep;
1035 
1036 	/* XXX signal race walking sep list */
1037 	for (sep = servtab; sep; sep = sep->se_next) {
1038 		if (sep->se_fd == -1)
1039 			continue;
1040 
1041 		switch (sep->se_family) {
1042 		case AF_UNIX:
1043 			(void)unlink(sep->se_service);
1044 			break;
1045 		case AF_INET:
1046 		case AF_INET6:
1047 			if (sep->se_wait == 1 && isrpcservice(sep))
1048 				unregister_rpc(sep);	/* XXX signal race */
1049 			break;
1050 		}
1051 		(void)close(sep->se_fd);
1052 	}
1053 	(void)unlink(_PATH_INETDPID);
1054 	_exit(0);
1055 }
1056 
1057 int bump_nofile __P((void));
1058 
1059 void
1060 setup(sep)
1061 	register struct servtab *sep;
1062 {
1063 	int on = 1;
1064 	int r;
1065 
1066 	if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
1067 		syslog(LOG_ERR, "%s/%s: socket: %m",
1068 		    sep->se_service, sep->se_proto);
1069 		return;
1070 	}
1071 #define	turnon(fd, opt) \
1072 setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
1073 	if (strncmp(sep->se_proto, "tcp", 3) == 0 && (options & SO_DEBUG) &&
1074 	    turnon(sep->se_fd, SO_DEBUG) < 0)
1075 		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
1076 	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
1077 		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
1078 #undef turnon
1079 	if (isrpcservice(sep)) {
1080 		struct passwd *pwd;
1081 
1082 		/*
1083 		 * for RPC services, attempt to use a reserved port
1084 		 * if they are going to be running as root.
1085 		 *
1086 		 * Also, zero out the port for all RPC services; let bind()
1087 		 * find one.
1088 		 */
1089 		sep->se_ctrladdr_in.sin_port = 0;
1090 		if (sep->se_user && (pwd = getpwnam(sep->se_user)) &&
1091 		    pwd->pw_uid == 0 && uid == 0)
1092 			r = bindresvport(sep->se_fd, &sep->se_ctrladdr_in);
1093 		else {
1094 			r = bind(sep->se_fd, &sep->se_ctrladdr,
1095 			    sep->se_ctrladdr_size);
1096 			if (r == 0) {
1097 				int len = sep->se_ctrladdr_size;
1098 				int saveerrno = errno;
1099 
1100 				/* update se_ctrladdr_in.sin_port */
1101 				r = getsockname(sep->se_fd, &sep->se_ctrladdr,
1102 				    &len);
1103 				if (r <= 0)
1104 					errno = saveerrno;
1105 			}
1106 		}
1107 	} else
1108 		r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size);
1109 	if (r < 0) {
1110 		syslog(LOG_ERR, "%s/%s: bind: %m",
1111 		    sep->se_service, sep->se_proto);
1112 		(void) close(sep->se_fd);
1113 		sep->se_fd = -1;
1114 		if (!timingout) {
1115 			timingout = 1;
1116 			alarm(RETRYTIME);
1117 		}
1118 		return;
1119 	}
1120 	if (sep->se_socktype == SOCK_STREAM)
1121 		listen(sep->se_fd, 10);
1122 
1123 	fd_grow(&allsockp, &allsockn, sep->se_fd);
1124 	FD_SET(sep->se_fd, allsockp);
1125 	nsock++;
1126 	if (sep->se_fd > maxsock) {
1127 		maxsock = sep->se_fd;
1128 		if (maxsock > rlim_ofile_cur - FD_MARGIN)
1129 			bump_nofile();
1130 	}
1131 }
1132 
1133 void
1134 register_rpc(sep)
1135 	register struct servtab *sep;
1136 {
1137 	int n;
1138 	struct sockaddr_in sin;
1139 	struct protoent *pp;
1140 
1141 	if ((pp = getprotobyname(sep->se_proto+4)) == NULL) {
1142 		syslog(LOG_ERR, "%s: getproto: %m",
1143 		    sep->se_proto);
1144 		return;
1145 	}
1146 	n = sizeof sin;
1147 	if (getsockname(sep->se_fd, (struct sockaddr *)&sin, &n) < 0) {
1148 		syslog(LOG_ERR, "%s/%s: getsockname: %m",
1149 		    sep->se_service, sep->se_proto);
1150 		return;
1151 	}
1152 
1153 	for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
1154 		if (debug)
1155 			fprintf(stderr, "pmap_set: %u %u %u %u\n",
1156 			    sep->se_rpcprog, n, pp->p_proto,
1157 			    ntohs(sin.sin_port));
1158 		(void)pmap_unset(sep->se_rpcprog, n);
1159 		if (!pmap_set(sep->se_rpcprog, n, pp->p_proto, ntohs(sin.sin_port)))
1160 			syslog(LOG_ERR, "%s %s: pmap_set: %u %u %u %u: %m",
1161 			    sep->se_service, sep->se_proto,
1162 			    sep->se_rpcprog, n, pp->p_proto,
1163 			    ntohs(sin.sin_port));
1164 	}
1165 }
1166 
1167 void
1168 unregister_rpc(sep)
1169 	register struct servtab *sep;
1170 {
1171 	int n;
1172 
1173 	for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
1174 		if (debug)
1175 			fprintf(stderr, "pmap_unset(%u, %u)\n",
1176 			    sep->se_rpcprog, n);
1177 		if (!pmap_unset(sep->se_rpcprog, n))
1178 			syslog(LOG_ERR, "pmap_unset(%u, %u)",
1179 			    sep->se_rpcprog, n);
1180 	}
1181 }
1182 
1183 
1184 struct servtab *
1185 enter(cp)
1186 	struct servtab *cp;
1187 {
1188 	register struct servtab *sep;
1189 	sigset_t omask;
1190 
1191 	sep = (struct servtab *)malloc(sizeof (*sep));
1192 	if (sep == NULL) {
1193 		syslog(LOG_ERR, "Out of memory.");
1194 		exit(1);
1195 	}
1196 	*sep = *cp;
1197 	sep->se_fd = -1;
1198 	sep->se_rpcprog = -1;
1199 	sigprocmask(SIG_BLOCK, &blockmask, &omask);
1200 	sep->se_next = servtab;
1201 	servtab = sep;
1202 	sigprocmask(SIG_SETMASK, &omask, NULL);
1203 	return (sep);
1204 }
1205 
1206 int
1207 matchconf (old, new)
1208 	struct servtab	*old;
1209 	struct servtab	*new;
1210 {
1211 	if (strcmp(old->se_service, new->se_service) != 0)
1212 		return (0);
1213 
1214 	if (strcmp(old->se_hostaddr, new->se_hostaddr) != 0)
1215 		return (0);
1216 
1217 	if (strcmp(old->se_proto, new->se_proto) != 0)
1218 		return (0);
1219 
1220 	/*
1221 	 * If the new servtab is bound to a specific address, check that the
1222 	 * old servtab is bound to the same entry. If the new service is not
1223 	 * bound to a specific address then the check of se_hostaddr above
1224 	 * is sufficient.
1225 	 */
1226 
1227 	if (old->se_family == AF_INET && new->se_family == AF_INET &&
1228 	    bcmp(&old->se_ctrladdr_in.sin_addr,
1229 	    &new->se_ctrladdr_in.sin_addr,
1230 	    sizeof(new->se_ctrladdr_in.sin_addr)) != 0)
1231 		return (0);
1232 
1233 	if (old->se_family == AF_INET6 && new->se_family == AF_INET6 &&
1234 	    bcmp(&old->se_ctrladdr_in6.sin6_addr,
1235 	    &new->se_ctrladdr_in6.sin6_addr,
1236 	    sizeof(new->se_ctrladdr_in6.sin6_addr)) != 0)
1237 		return (0);
1238 	if (old->se_family == AF_INET6 && new->se_family == AF_INET6 &&
1239 	    old->se_ctrladdr_in6.sin6_scope_id !=
1240 	    new->se_ctrladdr_in6.sin6_scope_id)
1241 		return (0);
1242 
1243 	return (1);
1244 }
1245 
1246 FILE		*fconfig = NULL;
1247 char		line[1024];
1248 char		*defhost;
1249 char		*skip __P((char **, int));
1250 char		*nextline __P((FILE *));
1251 char		*newstr __P((char *));
1252 struct servtab	*dupconfig __P((struct servtab *));
1253 
1254 int
1255 setconfig()
1256 {
1257 	if (defhost) free(defhost);
1258 	defhost = newstr("*");
1259 	if (fconfig != NULL) {
1260 		fseek(fconfig, 0L, SEEK_SET);
1261 		return (1);
1262 	}
1263 	fconfig = fopen(CONFIG, "r");
1264 	return (fconfig != NULL);
1265 }
1266 
1267 void
1268 endconfig()
1269 {
1270 	if (fconfig) {
1271 		(void) fclose(fconfig);
1272 		fconfig = NULL;
1273 	}
1274 	if (defhost) {
1275 		free(defhost);
1276 		defhost = 0;
1277 	}
1278 }
1279 
1280 struct servtab *
1281 getconfigent()
1282 {
1283 	register struct servtab *sep;
1284 	int argc;
1285 	char *cp, *arg, *s;
1286 	char *hostdelim;
1287 	struct servtab *nsep;
1288 	struct servtab *psep;
1289 
1290 	sep = (struct servtab *) malloc(sizeof(struct servtab));
1291 	if (sep == NULL) {
1292 		syslog(LOG_ERR, "malloc: %m");
1293 		exit(1);
1294 	}
1295 
1296 	memset(sep, 0, sizeof *sep);
1297 more:
1298 	freeconfig(sep);
1299 
1300 	while ((cp = nextline(fconfig)) && *cp == '#')
1301 		;
1302 	if (cp == NULL) {
1303 		free(sep);
1304 		return (NULL);
1305 	}
1306 
1307 	memset((char *)sep, 0, sizeof *sep);
1308 	arg = skip(&cp, 0);
1309 	if (arg == NULL) {
1310 		/* A blank line. */
1311 		goto more;
1312 	}
1313 
1314 	/* Check for a host name. */
1315 	hostdelim = strrchr(arg, ':');
1316 	if (hostdelim) {
1317 		*hostdelim = '\0';
1318 		if (arg[0] == '[' && hostdelim > arg && hostdelim[-1] == ']') {
1319 			hostdelim[-1] = '\0';
1320 			sep->se_hostaddr = newstr(arg + 1);
1321 		} else
1322 			sep->se_hostaddr = newstr(arg);
1323 		arg = hostdelim + 1;
1324 		/*
1325 		 * If the line is of the form `host:', then just change the
1326 		 * default host for the following lines.
1327 		 */
1328 		if (*arg == '\0') {
1329 			arg = skip(&cp, 0);
1330 			if (cp == NULL) {
1331 				free(defhost);
1332 				defhost = newstr(sep->se_hostaddr);
1333 				goto more;
1334 			}
1335 		}
1336 	} else
1337 		sep->se_hostaddr = newstr(defhost);
1338 
1339 	sep->se_service = newstr(arg);
1340 	if ((arg = skip(&cp, 1)) == NULL)
1341 		goto more;
1342 
1343 	if (strcmp(arg, "stream") == 0)
1344 		sep->se_socktype = SOCK_STREAM;
1345 	else if (strcmp(arg, "dgram") == 0)
1346 		sep->se_socktype = SOCK_DGRAM;
1347 	else if (strcmp(arg, "rdm") == 0)
1348 		sep->se_socktype = SOCK_RDM;
1349 	else if (strcmp(arg, "seqpacket") == 0)
1350 		sep->se_socktype = SOCK_SEQPACKET;
1351 	else if (strcmp(arg, "raw") == 0)
1352 		sep->se_socktype = SOCK_RAW;
1353 	else
1354 		sep->se_socktype = -1;
1355 
1356 	if ((arg = skip(&cp, 1)) == NULL)
1357 		goto more;
1358 
1359 	sep->se_proto = newstr(arg);
1360 
1361 	if (strcmp(sep->se_proto, "unix") == 0) {
1362 		sep->se_family = AF_UNIX;
1363 	} else {
1364 		sep->se_family = AF_INET;
1365 		if (sep->se_proto[strlen(sep->se_proto) - 1] == '6')
1366 			sep->se_family = AF_INET6;
1367 		if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
1368 			char *cp, *ccp;
1369 			long l;
1370 
1371 			cp = strchr(sep->se_service, '/');
1372 			if (cp == 0) {
1373 				syslog(LOG_ERR, "%s: no rpc version",
1374 				    sep->se_service);
1375 				goto more;
1376 			}
1377 			*cp++ = '\0';
1378 			l = strtol(cp, &ccp, 0);
1379 			if (ccp == cp || l < 0 || l > INT_MAX) {
1380 		badafterall:
1381 				syslog(LOG_ERR, "%s/%s: bad rpc version",
1382 				    sep->se_service, cp);
1383 				goto more;
1384 			}
1385 			sep->se_rpcversl = sep->se_rpcversh = l;
1386 			if (*ccp == '-') {
1387 				cp = ccp + 1;
1388 				l = strtol(cp, &ccp, 0);
1389 				if (ccp == cp || l < 0 || l > INT_MAX ||
1390 				    l < sep->se_rpcversl || *ccp)
1391 					goto badafterall;
1392 				sep->se_rpcversh = l;
1393 			} else if (*ccp != '\0')
1394 				goto badafterall;
1395 		}
1396 	}
1397 	arg = skip(&cp, 1);
1398 	if (arg == NULL)
1399 		goto more;
1400 
1401 	s = strchr(arg, '.');
1402 	if (s) {
1403 		char *p;
1404 
1405 		*s++ = '\0';
1406 		sep->se_max = strtoul(s, &p, 0);
1407 		if (sep->se_max < 1 || *p) {
1408 			syslog(LOG_ERR, "%s: illegal max field \"%s\", setting to %d",
1409 			    sep->se_service, s, toomany);
1410 			sep->se_max = toomany;
1411 		}
1412 	} else
1413 		sep->se_max = toomany;
1414 
1415 	sep->se_wait = strcmp(arg, "wait") == 0;
1416 	if ((arg = skip(&cp, 1)) == NULL)
1417 		goto more;
1418 	sep->se_user = newstr(arg);
1419 	arg = strchr(sep->se_user, '.');
1420 	if (arg == NULL)
1421 		arg = strchr(sep->se_user, ':');
1422 	if (arg) {
1423 		*arg++ = '\0';
1424 		sep->se_group = newstr(arg);
1425 	}
1426 	if ((arg = skip(&cp, 1)) == NULL)
1427 		goto more;
1428 
1429 	sep->se_server = newstr(arg);
1430 	if (strcmp(sep->se_server, "internal") == 0) {
1431 		struct biltin *bi;
1432 
1433 		for (bi = biltins; bi->bi_service; bi++)
1434 			if (bi->bi_socktype == sep->se_socktype &&
1435 			    strcmp(bi->bi_service, sep->se_service) == 0)
1436 				break;
1437 		if (bi->bi_service == 0) {
1438 			syslog(LOG_ERR, "internal service %s unknown",
1439 			    sep->se_service);
1440 			goto more;
1441 		}
1442 		sep->se_bi = bi;
1443 		sep->se_wait = bi->bi_wait;
1444 	} else
1445 		sep->se_bi = NULL;
1446 	argc = 0;
1447 	for (arg = skip(&cp, 0); cp; arg = skip(&cp, 0)) {
1448 		if (argc < MAXARGV)
1449 			sep->se_argv[argc++] = newstr(arg);
1450 	}
1451 	while (argc <= MAXARGV)
1452 		sep->se_argv[argc++] = NULL;
1453 
1454 	/*
1455 	 * Now that we've processed the entire line, check if the hostname
1456 	 * specifier was a comma separated list of hostnames. If so
1457 	 * we'll make new entries for each address.
1458 	 */
1459 	while ((hostdelim = strrchr(sep->se_hostaddr, ',')) != NULL) {
1460 		nsep = dupconfig(sep);
1461 
1462 		/*
1463 		 * NULL terminate the hostname field of the existing entry,
1464 		 * and make a dup for the new entry.
1465 		 */
1466 		*hostdelim++ = '\0';
1467 		nsep->se_hostaddr = newstr(hostdelim);
1468 
1469 		nsep->se_next = sep->se_next;
1470 		sep->se_next = nsep;
1471 	}
1472 
1473 	nsep = sep;
1474 	while (nsep != NULL) {
1475 		nsep->se_checked = 1;
1476 		switch (nsep->se_family) {
1477 		case AF_INET:
1478 		case AF_INET6:
1479 		    {
1480 			struct addrinfo hints, *res0, *res;
1481 			char *host, *port;
1482 			int error;
1483 			int s;
1484 
1485 			/* check if the family is supported */
1486 			s = socket(nsep->se_family, SOCK_DGRAM, 0);
1487 			if (s < 0) {
1488 				syslog(LOG_WARNING,
1489 "%s/%s: %s: the address family is not supported by the kernel",
1490 				    nsep->se_service, nsep->se_proto,
1491 				    nsep->se_hostaddr);
1492 				nsep->se_checked = 0;
1493 				goto skip;
1494 			}
1495 			close(s);
1496 
1497 			memset(&hints, 0, sizeof(hints));
1498 			hints.ai_family = nsep->se_family;
1499 			hints.ai_socktype = nsep->se_socktype;
1500 			hints.ai_flags = AI_PASSIVE;
1501 			if (!strcmp(nsep->se_hostaddr, "*"))
1502 				host = NULL;
1503 			else
1504 				host = nsep->se_hostaddr;
1505 			port = "0";
1506 			/* XXX shortened IPv4 syntax is now forbidden */
1507 			error = getaddrinfo(host, port, &hints, &res0);
1508 			if (error) {
1509 				syslog(LOG_ERR, "%s/%s: %s: %s",
1510 				    nsep->se_service, nsep->se_proto,
1511 				    nsep->se_hostaddr,
1512 				    gai_strerror(error));
1513 				nsep->se_checked = 0;
1514 				goto skip;
1515 			}
1516 			for (res = res0; res; res = res->ai_next) {
1517 				if (res->ai_addrlen >
1518 				    sizeof(nsep->se_ctrladdr_storage))
1519 					continue;
1520 				if (res == res0) {
1521 					memcpy(&nsep->se_ctrladdr_storage,
1522 					    res->ai_addr, res->ai_addrlen);
1523 					continue;
1524 				}
1525 
1526 				psep = dupconfig(nsep);
1527 				psep->se_hostaddr = newstr(nsep->se_hostaddr);
1528 				psep->se_checked = 1;
1529 				memcpy(&psep->se_ctrladdr_storage, res->ai_addr,
1530 				    res->ai_addrlen);
1531 				psep->se_ctrladdr_size = res->ai_addrlen;
1532 
1533 				/*
1534 				 * Prepend to list, don't want to look up its
1535 				 * hostname again.
1536 				 */
1537 				psep->se_next = sep;
1538 				sep = psep;
1539 			}
1540 			freeaddrinfo(res0);
1541 			break;
1542 		    }
1543 		}
1544 skip:
1545 		nsep = nsep->se_next;
1546 	}
1547 
1548 	/*
1549 	 * Finally, free any entries which failed the gethostbyname
1550 	 * check.
1551 	 */
1552 	psep = NULL;
1553 	nsep = sep;
1554 	while (nsep != NULL) {
1555 		struct servtab *tsep;
1556 
1557 		if (nsep->se_checked == 0) {
1558 			tsep = nsep;
1559 			if (psep == NULL) {
1560 				sep = nsep->se_next;
1561 				nsep = sep;
1562 			} else {
1563 				nsep = nsep->se_next;
1564 				psep->se_next = nsep;
1565 			}
1566 			freeconfig(tsep);
1567 		} else {
1568 			nsep->se_checked = 0;
1569 			psep = nsep;
1570 			nsep = nsep->se_next;
1571 		}
1572 	}
1573 
1574 	return (sep);
1575 }
1576 
1577 void
1578 freeconfig(cp)
1579 	register struct servtab *cp;
1580 {
1581 	int i;
1582 
1583 	if (cp->se_hostaddr)
1584 		free(cp->se_hostaddr);
1585 	if (cp->se_service)
1586 		free(cp->se_service);
1587 	if (cp->se_proto)
1588 		free(cp->se_proto);
1589 	if (cp->se_user)
1590 		free(cp->se_user);
1591 	if (cp->se_group)
1592 		free(cp->se_group);
1593 	if (cp->se_server)
1594 		free(cp->se_server);
1595 	for (i = 0; i < MAXARGV; i++)
1596 		if (cp->se_argv[i])
1597 			free(cp->se_argv[i]);
1598 }
1599 
1600 char *
1601 skip(cpp, report)
1602 	char **cpp;
1603 	int report;
1604 {
1605 	char *cp = *cpp;
1606 	char *start;
1607 
1608 erp:
1609 	if (*cpp == NULL) {
1610 		if (report)
1611 			syslog(LOG_ERR, "syntax error in inetd config file");
1612 		return (NULL);
1613 	}
1614 
1615 again:
1616 	while (*cp == ' ' || *cp == '\t')
1617 		cp++;
1618 	if (*cp == '\0') {
1619 		int c;
1620 
1621 		c = getc(fconfig);
1622 		(void) ungetc(c, fconfig);
1623 		if (c == ' ' || c == '\t')
1624 			if ((cp = nextline(fconfig)))
1625 				goto again;
1626 		*cpp = NULL;
1627 		goto erp;
1628 	}
1629 	start = cp;
1630 	while (*cp && *cp != ' ' && *cp != '\t')
1631 		cp++;
1632 	if (*cp != '\0')
1633 		*cp++ = '\0';
1634 	if ((*cpp = cp) == NULL)
1635 		goto erp;
1636 
1637 	return (start);
1638 }
1639 
1640 char *
1641 nextline(fd)
1642 	FILE *fd;
1643 {
1644 	char *cp;
1645 
1646 	if (fgets(line, sizeof (line), fd) == NULL)
1647 		return (NULL);
1648 	cp = strchr(line, '\n');
1649 	if (cp)
1650 		*cp = '\0';
1651 	return (line);
1652 }
1653 
1654 char *
1655 newstr(cp)
1656 	char *cp;
1657 {
1658 	if ((cp = strdup(cp ? cp : "")))
1659 		return(cp);
1660 	syslog(LOG_ERR, "strdup: %m");
1661 	exit(1);
1662 }
1663 
1664 struct servtab *
1665 dupconfig(sep)
1666 	struct servtab *sep;
1667 {
1668 	struct servtab *newtab;
1669 	int argc;
1670 
1671 	newtab = (struct servtab *) malloc(sizeof(struct servtab));
1672 
1673 	if (newtab == NULL) {
1674 		syslog(LOG_ERR, "malloc: %m");
1675 		exit(1);
1676 	}
1677 
1678 	memset((char *)newtab, 0, sizeof(struct servtab));
1679 
1680 	newtab->se_service = sep->se_service ? newstr(sep->se_service) : NULL;
1681 	newtab->se_socktype = sep->se_socktype;
1682 	newtab->se_family = sep->se_family;
1683 	newtab->se_proto = sep->se_proto ? newstr(sep->se_proto) : NULL;
1684 	newtab->se_rpcprog = sep->se_rpcprog;
1685 	newtab->se_rpcversl = sep->se_rpcversl;
1686 	newtab->se_rpcversh = sep->se_rpcversh;
1687 	newtab->se_wait = sep->se_wait;
1688 	newtab->se_user = sep->se_user ? newstr(sep->se_user) : NULL;
1689 	newtab->se_group = sep->se_group ? newstr(sep->se_group) : NULL;
1690 	newtab->se_bi = sep->se_bi;
1691 	newtab->se_server = sep->se_server ? newstr(sep->se_server) : 0;
1692 
1693 	for (argc = 0; argc <= MAXARGV; argc++)
1694 		newtab->se_argv[argc] = sep->se_argv[argc] ?
1695 		    newstr(sep->se_argv[argc]) : NULL;
1696 	newtab->se_max = sep->se_max;
1697 
1698 	return (newtab);
1699 }
1700 
1701 void
1702 inetd_setproctitle(a, s)
1703 	char *a;
1704 	int s;
1705 {
1706 	int size;
1707 	register char *cp;
1708 	struct sockaddr_in sin;
1709 	char buf[80];
1710 
1711 	cp = Argv[0];
1712 	size = sizeof(sin);
1713 	(void) snprintf(buf, sizeof buf, "-%s", a);
1714 	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) {
1715 		char *s = inet_ntoa(sin.sin_addr);
1716 
1717 		buf[sizeof(buf) - 1 - strlen(s) - 3] = '\0';
1718 		strcat(buf, " [");
1719 		strcat(buf, s);
1720 		strcat(buf, "]");
1721 	}
1722 	strncpy(cp, buf, LastArg - cp);
1723 	cp += strlen(cp);
1724 	while (cp < LastArg)
1725 		*cp++ = ' ';
1726 }
1727 
1728 void
1729 logpid()
1730 {
1731 	FILE *fp;
1732 
1733 	if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) {
1734 		fprintf(fp, "%u\n", getpid());
1735 		(void)fclose(fp);
1736 	}
1737 }
1738 
1739 int
1740 bump_nofile()
1741 {
1742 #ifdef RLIMIT_NOFILE
1743 
1744 #define FD_CHUNK	32
1745 
1746 	struct rlimit rl;
1747 
1748 	if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
1749 		syslog(LOG_ERR, "getrlimit: %m");
1750 		return -1;
1751 	}
1752 	rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
1753 	rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);
1754 	if (rl.rlim_cur <= rlim_ofile_cur) {
1755 		syslog(LOG_ERR,
1756 		    "bump_nofile: cannot extend file limit, max = %d",
1757 		    (int)rl.rlim_cur);
1758 		return -1;
1759 	}
1760 
1761 	if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
1762 		syslog(LOG_ERR, "setrlimit: %m");
1763 		return -1;
1764 	}
1765 
1766 	rlim_ofile_cur = rl.rlim_cur;
1767 	return 0;
1768 
1769 #else
1770 	syslog(LOG_ERR, "bump_nofile: cannot extend file limit");
1771 	return -1;
1772 #endif
1773 }
1774 
1775 /*
1776  * Internet services provided internally by inetd:
1777  */
1778 #define	BUFSIZE	4096
1779 
1780 /* ARGSUSED */
1781 void
1782 echo_stream(s, sep)		/* Echo service -- echo data back */
1783 	int s;
1784 	struct servtab *sep;
1785 {
1786 	char buffer[BUFSIZE];
1787 	int i;
1788 
1789 	inetd_setproctitle(sep->se_service, s);
1790 	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
1791 	    write(s, buffer, i) > 0)
1792 		;
1793 	exit(0);
1794 }
1795 
1796 /* ARGSUSED */
1797 void
1798 echo_dg(s, sep)			/* Echo service -- echo data back */
1799 	int s;
1800 	struct servtab *sep;
1801 {
1802 	char buffer[BUFSIZE];
1803 	int i, size;
1804 	struct sockaddr_storage ss;
1805 
1806 	size = sizeof(ss);
1807 	if ((i = recvfrom(s, buffer, sizeof(buffer), 0, (struct sockaddr *)&ss,
1808 	    &size)) < 0)
1809 		return;
1810 	if (dg_badinput((struct sockaddr *)&ss))
1811 		return;
1812 	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size);
1813 }
1814 
1815 /* ARGSUSED */
1816 void
1817 discard_stream(s, sep)		/* Discard service -- ignore data */
1818 	int s;
1819 	struct servtab *sep;
1820 {
1821 	char buffer[BUFSIZE];
1822 
1823 	inetd_setproctitle(sep->se_service, s);
1824 	while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
1825 	    errno == EINTR)
1826 		;
1827 	exit(0);
1828 }
1829 
1830 /* ARGSUSED */
1831 void
1832 discard_dg(s, sep)		/* Discard service -- ignore data */
1833 	int s;
1834 	struct servtab *sep;
1835 {
1836 	char buffer[BUFSIZE];
1837 
1838 	(void) read(s, buffer, sizeof(buffer));
1839 }
1840 
1841 #include <ctype.h>
1842 #define LINESIZ 72
1843 char ring[128];
1844 char *endring;
1845 
1846 void
1847 initring()
1848 {
1849 	register int i;
1850 
1851 	endring = ring;
1852 
1853 	for (i = 0; i <= 128; ++i)
1854 		if (isprint(i))
1855 			*endring++ = i;
1856 }
1857 
1858 /* ARGSUSED */
1859 void
1860 chargen_stream(s, sep)		/* Character generator */
1861 	int s;
1862 	struct servtab *sep;
1863 {
1864 	register char *rs;
1865 	int len;
1866 	char text[LINESIZ+2];
1867 
1868 	inetd_setproctitle(sep->se_service, s);
1869 
1870 	if (!endring) {
1871 		initring();
1872 		rs = ring;
1873 	}
1874 
1875 	text[LINESIZ] = '\r';
1876 	text[LINESIZ + 1] = '\n';
1877 	for (rs = ring;;) {
1878 		if ((len = endring - rs) >= LINESIZ)
1879 			memmove(text, rs, LINESIZ);
1880 		else {
1881 			memmove(text, rs, len);
1882 			memmove(text + len, ring, LINESIZ - len);
1883 		}
1884 		if (++rs == endring)
1885 			rs = ring;
1886 		if (write(s, text, sizeof(text)) != sizeof(text))
1887 			break;
1888 	}
1889 	exit(0);
1890 }
1891 
1892 /* ARGSUSED */
1893 void
1894 chargen_dg(s, sep)		/* Character generator */
1895 	int s;
1896 	struct servtab *sep;
1897 {
1898 	struct sockaddr_storage ss;
1899 	static char *rs;
1900 	int len, size;
1901 	char text[LINESIZ+2];
1902 
1903 	if (endring == 0) {
1904 		initring();
1905 		rs = ring;
1906 	}
1907 
1908 	size = sizeof(ss);
1909 	if (recvfrom(s, text, sizeof(text), 0, (struct sockaddr *)&ss,
1910 	    &size) < 0)
1911 		return;
1912 	if (dg_badinput((struct sockaddr *)&ss))
1913 		return;
1914 
1915 	if ((len = endring - rs) >= LINESIZ)
1916 		memmove(text, rs, LINESIZ);
1917 	else {
1918 		memmove(text, rs, len);
1919 		memmove(text + len, ring, LINESIZ - len);
1920 	}
1921 	if (++rs == endring)
1922 		rs = ring;
1923 	text[LINESIZ] = '\r';
1924 	text[LINESIZ + 1] = '\n';
1925 	(void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size);
1926 }
1927 
1928 /*
1929  * Return a machine readable date and time, in the form of the
1930  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
1931  * returns the number of seconds since midnight, Jan 1, 1970,
1932  * we must add 2208988800 seconds to this figure to make up for
1933  * some seventy years Bell Labs was asleep.
1934  */
1935 
1936 u_int
1937 machtime()
1938 {
1939 	struct timeval tv;
1940 
1941 	if (gettimeofday(&tv, NULL) < 0) {
1942 		fprintf(stderr, "Unable to get time of day\n");
1943 		return (0L);
1944 	}
1945 	return (htonl((u_int)tv.tv_sec + 2208988800UL));
1946 }
1947 
1948 /* ARGSUSED */
1949 void
1950 machtime_stream(s, sep)
1951 	int s;
1952 	struct servtab *sep;
1953 {
1954 	u_int result;
1955 
1956 	result = machtime();
1957 	(void) write(s, (char *) &result, sizeof(result));
1958 }
1959 
1960 /* ARGSUSED */
1961 void
1962 machtime_dg(s, sep)
1963 	int s;
1964 	struct servtab *sep;
1965 {
1966 	u_int result;
1967 	struct sockaddr_storage ss;
1968 	int size;
1969 
1970 	size = sizeof(ss);
1971 	if (recvfrom(s, (char *)&result, sizeof(result), 0,
1972 	    (struct sockaddr *)&ss, &size) < 0)
1973 		return;
1974 	if (dg_badinput((struct sockaddr *)&ss))
1975 		return;
1976 	result = machtime();
1977 	(void) sendto(s, (char *) &result, sizeof(result), 0,
1978 	    (struct sockaddr *)&ss, size);
1979 }
1980 
1981 /* ARGSUSED */
1982 void
1983 daytime_stream(s, sep)		/* Return human-readable time of day */
1984 	int s;
1985 	struct servtab *sep;
1986 {
1987 	char buffer[256];
1988 	time_t time(), clock;
1989 
1990 	clock = time(NULL);
1991 
1992 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
1993 	(void) write(s, buffer, strlen(buffer));
1994 }
1995 
1996 /* ARGSUSED */
1997 void
1998 daytime_dg(s, sep)		/* Return human-readable time of day */
1999 	int s;
2000 	struct servtab *sep;
2001 {
2002 	char buffer[256];
2003 	time_t time(), clock;
2004 	struct sockaddr_storage ss;
2005 	int size;
2006 
2007 	clock = time((time_t *) 0);
2008 
2009 	size = sizeof(ss);
2010 	if (recvfrom(s, buffer, sizeof(buffer), 0, (struct sockaddr *)&ss,
2011 	    &size) < 0)
2012 		return;
2013 	if (dg_badinput((struct sockaddr *)&ss))
2014 		return;
2015 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
2016 	(void) sendto(s, buffer, strlen(buffer), 0, (struct sockaddr *)&ss,
2017 	    size);
2018 }
2019 
2020 /*
2021  * print_service:
2022  *	Dump relevant information to stderr
2023  */
2024 void
2025 print_service(action, sep)
2026 	char *action;
2027 	struct servtab *sep;
2028 {
2029 	if (strcmp(sep->se_hostaddr, "*") == 0)
2030 		fprintf(stderr, "%s: %s ", action, sep->se_service);
2031 	else
2032 		fprintf(stderr, "%s: %s:%s ", action, sep->se_hostaddr,
2033 		    sep->se_service);
2034 
2035 	if (isrpcservice(sep))
2036 		fprintf(stderr, "rpcprog=%d, rpcvers=%d/%d, proto=%s,",
2037 		    sep->se_rpcprog, sep->se_rpcversh,
2038 		    sep->se_rpcversl, sep->se_proto);
2039 	else
2040 		fprintf(stderr, "proto=%s,", sep->se_proto);
2041 
2042 	fprintf(stderr,
2043 	    " wait.max=%hd.%d user:group=%s.%s builtin=%lx server=%s\n",
2044 	    sep->se_wait, sep->se_max, sep->se_user,
2045 	    sep->se_group ? sep->se_group : "wheel",
2046 	    (long)sep->se_bi, sep->se_server);
2047 }
2048