xref: /openbsd-src/usr.sbin/inetd/inetd.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: inetd.c,v 1.83 2001/07/04 06:46:58 deraadt 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.83 2001/07/04 06:46:58 deraadt 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 #define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
178 
179 void	config __P((int));
180 void	doconfig __P((void));
181 void	reap __P((int));
182 void	doreap __P((void));
183 void	retry __P((int));
184 void	doretry __P((void));
185 void	goaway __P((int));
186 
187 int	debug = 0;
188 int	nsock, maxsock;
189 fd_set	*allsockp;
190 int	allsockn;
191 int	toomany = TOOMANY;
192 int	options;
193 int	timingout;
194 struct	servent *sp;
195 char	*curdom;
196 uid_t	uid;
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 	memset((char *)&sa, 0, sizeof(sa));
416 	sigemptyset(&sa.sa_mask);
417 	sigaddset(&sa.sa_mask, SIGALRM);
418 	sigaddset(&sa.sa_mask, SIGCHLD);
419 	sigaddset(&sa.sa_mask, SIGHUP);
420 	sa.sa_handler = retry;
421 	sigaction(SIGALRM, &sa, NULL);
422 	doconfig();
423 	sa.sa_handler = config;
424 	sigaction(SIGHUP, &sa, NULL);
425 	sa.sa_handler = reap;
426 	sigaction(SIGCHLD, &sa, NULL);
427 	sa.sa_handler = goaway;
428 	sigaction(SIGTERM, &sa, NULL);
429 	sa.sa_handler = goaway;
430 	sigaction(SIGINT, &sa, NULL);
431 	sa.sa_handler = SIG_IGN;
432 	sigaction(SIGPIPE, &sa, &sapipe);
433 
434 	{
435 		/* space for daemons to overwrite environment for ps */
436 #define	DUMMYSIZE	100
437 		char dummy[DUMMYSIZE];
438 
439 		(void)memset(dummy, 'x', DUMMYSIZE - 1);
440 		dummy[DUMMYSIZE - 1] = '\0';
441 
442 		(void)setenv("inetd_dummy", dummy, 1);
443 	}
444 
445 	for (;;) {
446 	    int n, ctrl = -1;
447 
448 	    if (nsock == 0) {
449 		(void) sigblock(SIGBLOCK);
450 		while (nsock == 0) {
451 		    if (wantretry || wantconfig || wantreap)
452 			break;
453 		    sigpause(0L);
454 		}
455 		(void) sigsetmask(0L);
456 	    }
457 
458 	    if (wantretry || wantconfig || wantreap) {
459 		if (wantretry) {
460 		    doretry();
461 		    wantretry = 0;
462 		}
463 		if (wantconfig) {
464 		    doconfig();
465 		    wantconfig = 0;
466 		}
467 		if (wantreap) {
468 		    doreap();
469 		    wantreap = 0;
470 		}
471 		continue;
472 	    }
473 
474 	    if (readablen != allsockn) {
475 		if (readablep)
476 		    free(readablep);
477 		readablep = (fd_set *)calloc(allsockn, 1);
478 		if (readablep == NULL) {
479 		    syslog(LOG_ERR, "Out of memory.");
480 		    exit(1);
481 		}
482 		readablen = allsockn;
483 	    }
484 	    bcopy(allsockp, readablep, allsockn);
485 
486 	    if ((n = select(maxsock + 1, readablep, NULL, NULL, NULL)) <= 0) {
487 		    if (n < 0 && errno != EINTR) {
488 			syslog(LOG_WARNING, "select: %m");
489 			sleep(1);
490 		    }
491 		    continue;
492 	    }
493 	    for (sep = servtab; n && sep; sep = sep->se_next)
494 	    if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, readablep)) {
495 		n--;
496 		if (debug)
497 			fprintf(stderr, "someone wants %s\n", sep->se_service);
498 		if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
499 			ctrl = accept(sep->se_fd, NULL, NULL);
500 			if (debug)
501 				fprintf(stderr, "accept, ctrl %d\n", ctrl);
502 			if (ctrl < 0) {
503 				if (errno == EINTR)
504 					continue;
505 				syslog(LOG_WARNING, "accept (for %s): %m",
506 				    sep->se_service);
507 				continue;
508 			}
509 			if (sep->se_family == AF_INET &&
510 			    sep->se_socktype == SOCK_STREAM) {
511 				struct sockaddr_in peer;
512 				int plen = sizeof(peer);
513 
514 				if (getpeername(ctrl, (struct sockaddr *)&peer,
515 				    &plen) < 0) {
516 					syslog(LOG_WARNING, "could not getpeername");
517 					close(ctrl);
518 					continue;
519 				}
520 				if (ntohs(peer.sin_port) == 20) {
521 					/* XXX ftp bounce */
522 					close(ctrl);
523 					continue;
524 				}
525 			}
526 		} else
527 			ctrl = sep->se_fd;
528 		(void) sigblock(SIGBLOCK);
529 		pid = 0;
530 		dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
531 		if (dofork) {
532 			if (sep->se_count++ == 0)
533 			    (void)gettimeofday(&sep->se_time, NULL);
534 			else if (sep->se_count >= sep->se_max) {
535 				struct timeval now;
536 
537 				(void)gettimeofday(&now, NULL);
538 				if (now.tv_sec - sep->se_time.tv_sec >
539 				    CNT_INTVL) {
540 					sep->se_time = now;
541 					sep->se_count = 1;
542 				} else {
543 					if (!sep->se_wait &&
544 					    sep->se_socktype == SOCK_STREAM)
545 						close(ctrl);
546 					if (sep->se_family == AF_INET &&
547 					    ntohs(sep->se_ctrladdr_in.sin_port) >=
548 					    IPPORT_RESERVED) {
549 						/*
550 						 * Cannot close it -- there are
551 						 * thieves on the system.
552 						 * Simply ignore the connection.
553 						 */
554 						--sep->se_count;
555 						continue;
556 					}
557 					syslog(LOG_ERR,
558 			"%s/%s server failing (looping), service terminated",
559 					    sep->se_service, sep->se_proto);
560 					if (!sep->se_wait &&
561 					    sep->se_socktype == SOCK_STREAM)
562 						close(ctrl);
563 					FD_CLR(sep->se_fd, allsockp);
564 					(void) close(sep->se_fd);
565 					sep->se_fd = -1;
566 					sep->se_count = 0;
567 					nsock--;
568 					sigsetmask(0L);
569 					if (!timingout) {
570 						timingout = 1;
571 						alarm(RETRYTIME);
572 					}
573 					continue;
574 				}
575 			}
576 			pid = fork();
577 		}
578 		if (pid < 0) {
579 			syslog(LOG_ERR, "fork: %m");
580 			if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
581 				close(ctrl);
582 			sigsetmask(0L);
583 			sleep(1);
584 			continue;
585 		}
586 		if (pid && sep->se_wait) {
587 			sep->se_wait = pid;
588 			FD_CLR(sep->se_fd, allsockp);
589 			nsock--;
590 		}
591 		sigsetmask(0L);
592 		if (pid == 0) {
593 			if (sep->se_bi)
594 				(*sep->se_bi->bi_fn)(ctrl, sep);
595 			else {
596 				if ((pwd = getpwnam(sep->se_user)) == NULL) {
597 					syslog(LOG_ERR,
598 					    "getpwnam: %s: No such user",
599 					    sep->se_user);
600 					if (sep->se_socktype != SOCK_STREAM)
601 						recv(0, buf, sizeof (buf), 0);
602 					exit(1);
603 				}
604 				if (setsid() <0)
605 					syslog(LOG_ERR, "%s: setsid: %m",
606 					    sep->se_service);
607 				if (sep->se_group &&
608 				    (grp = getgrnam(sep->se_group)) == NULL) {
609 					syslog(LOG_ERR,
610 					    "getgrnam: %s: No such group",
611 					    sep->se_group);
612 					if (sep->se_socktype != SOCK_STREAM)
613 						recv(0, buf, sizeof (buf), 0);
614 					exit(1);
615 				}
616 				if (uid != 0) {
617 					/* a user running private inetd */
618 					if (uid != pwd->pw_uid)
619 						exit(1);
620 				} else {
621 					tmpint = LOGIN_SETALL &
622 					    ~(LOGIN_SETGROUP|LOGIN_SETLOGIN);
623 					if (pwd->pw_uid)
624 						tmpint |= LOGIN_SETGROUP|LOGIN_SETLOGIN;
625 					if (sep->se_group) {
626 						pwd->pw_gid = grp->gr_gid;
627 						tmpint |= LOGIN_SETGROUP;
628 					}
629 					if (setusercontext(0, pwd, pwd->pw_uid,
630 					    tmpint) < 0)
631 						syslog(LOG_ERR,
632 						    "%s/%s: setusercontext: %m",
633 						    sep->se_service,
634 						    sep->se_proto);
635 				}
636 				if (debug)
637 					fprintf(stderr, "%d execl %s\n",
638 					    getpid(), sep->se_server);
639 				dup2(ctrl, 0);
640 				close(ctrl);
641 				dup2(0, 1);
642 				dup2(0, 2);
643 				closelog();
644 				for (tmpint = rlim_ofile_cur-1; --tmpint > 2; )
645 					(void)close(tmpint);
646 				sigaction(SIGPIPE, &sapipe, NULL);
647 				execv(sep->se_server, sep->se_argv);
648 				if (sep->se_socktype != SOCK_STREAM)
649 					recv(0, buf, sizeof (buf), 0);
650 				syslog(LOG_ERR, "execv %s: %m", sep->se_server);
651 				exit(1);
652 			}
653 		}
654 		if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
655 			close(ctrl);
656 	    }
657 	}
658 }
659 
660 int
661 dg_badinput(sa)
662 	struct sockaddr *sa;
663 {
664 	struct in_addr in;
665 	struct in6_addr *in6;
666 	u_int16_t port;
667 
668 	switch (sa->sa_family) {
669 	case AF_INET:
670 		in.s_addr = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
671 		port = ntohs(((struct sockaddr_in *)sa)->sin_port);
672 	v4chk:
673 		if (IN_MULTICAST(in.s_addr))
674 			goto bad;
675 		switch ((in.s_addr & 0xff000000) >> 24) {
676 		case 0: case 127: case 255:
677 			goto bad;
678 		}
679 		/* XXX check for subnet broadcast using getifaddrs(3) */
680 		break;
681 	case AF_INET6:
682 		in6 = &((struct sockaddr_in6 *)sa)->sin6_addr;
683 		port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
684 		if (IN6_IS_ADDR_MULTICAST(in6) || IN6_IS_ADDR_UNSPECIFIED(in6))
685 			goto bad;
686 		/*
687 		 * OpenBSD does not support IPv4 mapped adderss (RFC2553
688 		 * inbound behavior) at all.  We should drop it.
689 		 */
690 		if (IN6_IS_ADDR_V4MAPPED(in6))
691 			goto bad;
692 		if (IN6_IS_ADDR_V4COMPAT(in6)) {
693 			memcpy(&in, &in6->s6_addr[12], sizeof(in));
694 			in.s_addr = ntohl(in.s_addr);
695 			goto v4chk;
696 		}
697 		break;
698 	default:
699 		/* XXX unsupported af, is it safe to assume it to be safe? */
700 		return 0;
701 	}
702 
703 	if (port < IPPORT_RESERVED || port == NFS_PORT)
704 		goto bad;
705 
706 	return (0);
707 
708 bad:
709 	return (1);
710 }
711 
712 void
713 reap(int sig)
714 {
715 	wantreap = 1;
716 }
717 
718 void
719 doreap(void)
720 {
721 	pid_t pid;
722 	int save_errno = errno, status;
723 	register struct servtab *sep;
724 
725 	if (debug)
726 		fprintf(stderr, "reaping asked for\n");
727 
728 	for (;;) {
729 		pid = wait3(&status, WNOHANG, NULL);
730 		if (pid <= 0)
731 			break;
732 		if (debug)
733 			fprintf(stderr, "%d reaped, status %x\n", pid, status);
734 		for (sep = servtab; sep; sep = sep->se_next)
735 			if (sep->se_wait == pid) {
736 				if (WIFEXITED(status) && WEXITSTATUS(status))
737 					syslog(LOG_WARNING,
738 					    "%s: exit status 0x%x",
739 					    sep->se_server, WEXITSTATUS(status));
740 				else if (WIFSIGNALED(status))
741 					syslog(LOG_WARNING,
742 					    "%s: exit signal 0x%x",
743 					    sep->se_server, WTERMSIG(status));
744 				sep->se_wait = 1;
745 				fd_grow(&allsockp, &allsockn, sep->se_fd);
746 				FD_SET(sep->se_fd, allsockp);
747 				nsock++;
748 				if (debug)
749 					fprintf(stderr, "restored %s, fd %d\n",
750 					    sep->se_service, sep->se_fd);
751 			}
752 	}
753 	errno = save_errno;
754 }
755 
756 int setconfig __P((void));
757 void endconfig __P((void));
758 
759 void register_rpc __P((struct servtab *));
760 void unregister_rpc __P((struct servtab *));
761 void freeconfig __P((struct servtab *));
762 void print_service __P((char *, struct servtab *));
763 void setup __P((struct servtab *));
764 struct servtab *getconfigent __P((void));
765 struct servtab *enter __P((struct servtab *));
766 int matchconf __P((struct servtab *, struct servtab *));
767 
768 void
769 config(int sig)
770 {
771 	wantconfig = 1;
772 }
773 
774 void
775 doconfig(void)
776 {
777 	register struct servtab *sep, *cp, **sepp;
778 	int omask;
779 	int n, add;
780 	char protoname[10];
781 
782 	if (!setconfig()) {
783 		syslog(LOG_ERR, "%s: %m", CONFIG);
784 		return;
785 	}
786 	for (sep = servtab; sep; sep = sep->se_next)
787 		sep->se_checked = 0;
788 	cp = getconfigent();
789 	while (cp != NULL) {
790 		for (sep = servtab; sep; sep = sep->se_next)
791 			if (matchconf(sep, cp))
792 				break;
793 		add = 0;
794 		if (sep != 0) {
795 			int i;
796 
797 #define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
798 
799 			omask = sigblock(SIGBLOCK);
800 			/*
801 			 * sep->se_wait may be holding the pid of a daemon
802 			 * that we're waiting for.  If so, don't overwrite
803 			 * it unless the config file explicitly says don't
804 			 * wait.
805 			 */
806 			if (cp->se_bi == 0 &&
807 			    (sep->se_wait == 1 || cp->se_wait == 0))
808 				sep->se_wait = cp->se_wait;
809 			SWAP(int, cp->se_max, sep->se_max);
810 			SWAP(char *, sep->se_user, cp->se_user);
811 			SWAP(char *, sep->se_group, cp->se_group);
812 			SWAP(char *, sep->se_server, cp->se_server);
813 			for (i = 0; i < MAXARGV; i++)
814 				SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
815 #undef SWAP
816 			if (isrpcservice(sep))
817 				unregister_rpc(sep);
818 			sep->se_rpcversl = cp->se_rpcversl;
819 			sep->se_rpcversh = cp->se_rpcversh;
820 			sigsetmask(omask);
821 			freeconfig(cp);
822 			add = 1;
823 		} else {
824 			sep = enter(cp);
825 		}
826 		sep->se_checked = 1;
827 
828 		switch (sep->se_family) {
829 		case AF_UNIX:
830 			if (sep->se_fd != -1)
831 				break;
832 			(void)unlink(sep->se_service);
833 			n = strlen(sep->se_service);
834 			if (n > sizeof sep->se_ctrladdr_un.sun_path - 1)
835 				n = sizeof sep->se_ctrladdr_un.sun_path - 1;
836 			strncpy(sep->se_ctrladdr_un.sun_path,
837 			    sep->se_service, n);
838 			sep->se_ctrladdr_un.sun_path[n] = '\0';
839 			sep->se_ctrladdr_un.sun_family = AF_UNIX;
840 			sep->se_ctrladdr_size = n +
841 			    sizeof sep->se_ctrladdr_un.sun_family;
842 			setup(sep);
843 			break;
844 		case AF_INET:
845 			sep->se_ctrladdr_in.sin_family = AF_INET;
846 			/* se_ctrladdr_in was set in getconfigent */
847 			sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
848 
849 			if (isrpcservice(sep)) {
850 				struct rpcent *rp;
851 
852 				sep->se_rpcprog = atoi(sep->se_service);
853 				if (sep->se_rpcprog == 0) {
854 					rp = getrpcbyname(sep->se_service);
855 					if (rp == 0) {
856 						syslog(LOG_ERR,
857 						    "%s: unknown rpc service",
858 						    sep->se_service);
859 						goto serv_unknown;
860 					}
861 					sep->se_rpcprog = rp->r_number;
862 				}
863 				if (sep->se_fd == -1)
864 					setup(sep);
865 				if (sep->se_fd != -1)
866 					register_rpc(sep);
867 			} else {
868 				u_short port = htons(atoi(sep->se_service));
869 
870 				if (!port) {
871 					/*XXX*/
872 					strncpy(protoname, sep->se_proto,
873 						sizeof(protoname));
874 					if (isdigit(protoname[strlen(protoname) - 1]))
875 						protoname[strlen(protoname) - 1] = '\0';
876 					sp = getservbyname(sep->se_service,
877 					    protoname);
878 					if (sp == 0) {
879 						syslog(LOG_ERR,
880 						    "%s/%s: unknown service",
881 						    sep->se_service, sep->se_proto);
882 						goto serv_unknown;
883 					}
884 					port = sp->s_port;
885 				}
886 				if (port != sep->se_ctrladdr_in.sin_port) {
887 					sep->se_ctrladdr_in.sin_port = port;
888 					if (sep->se_fd != -1) {
889 						FD_CLR(sep->se_fd, allsockp);
890 						nsock--;
891 						(void) close(sep->se_fd);
892 					}
893 					sep->se_fd = -1;
894 				}
895 				if (sep->se_fd == -1)
896 					setup(sep);
897 			}
898 			break;
899 		case AF_INET6:
900 			sep->se_ctrladdr_in6.sin6_family = AF_INET6;
901 			/* se_ctrladdr_in was set in getconfigent */
902 			sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in6;
903 
904 			if (isrpcservice(sep)) {
905 				struct rpcent *rp;
906 
907 				sep->se_rpcprog = atoi(sep->se_service);
908 				if (sep->se_rpcprog == 0) {
909 					rp = getrpcbyname(sep->se_service);
910 					if (rp == 0) {
911 						syslog(LOG_ERR,
912 						    "%s: unknown rpc service",
913 						    sep->se_service);
914 						goto serv_unknown;
915 					}
916 					sep->se_rpcprog = rp->r_number;
917 				}
918 				if (sep->se_fd == -1)
919 					setup(sep);
920 				if (sep->se_fd != -1)
921 					register_rpc(sep);
922 			} else {
923 				u_short port = htons(atoi(sep->se_service));
924 
925 				if (!port) {
926 					/*XXX*/
927 					strncpy(protoname, sep->se_proto,
928 						sizeof(protoname));
929 					if (isdigit(protoname[strlen(protoname) - 1]))
930 						protoname[strlen(protoname) - 1] = '\0';
931 					sp = getservbyname(sep->se_service,
932 					    protoname);
933 					if (sp == 0) {
934 						syslog(LOG_ERR,
935 						    "%s/%s: unknown service",
936 						    sep->se_service, sep->se_proto);
937 						goto serv_unknown;
938 					}
939 					port = sp->s_port;
940 				}
941 				if (port != sep->se_ctrladdr_in6.sin6_port) {
942 					sep->se_ctrladdr_in6.sin6_port = port;
943 					if (sep->se_fd != -1) {
944 						FD_CLR(sep->se_fd, allsockp);
945 						nsock--;
946 						(void) close(sep->se_fd);
947 					}
948 					sep->se_fd = -1;
949 				}
950 				if (sep->se_fd == -1)
951 					setup(sep);
952 			}
953 			break;
954 		}
955 	serv_unknown:
956 		if (cp->se_next != NULL) {
957 			struct servtab *tmp = cp;
958 
959 			cp = cp->se_next;
960 			free(tmp);
961 		} else {
962 			free(cp);
963 			cp = getconfigent();
964 		}
965 		if (debug)
966 			print_service(add ? "REDO" : "ADD", sep);
967 	}
968 	endconfig();
969 	/*
970 	 * Purge anything not looked at above.
971 	 */
972 	omask = sigblock(SIGBLOCK);
973 	sepp = &servtab;
974 	while ((sep = *sepp)) {
975 		if (sep->se_checked) {
976 			sepp = &sep->se_next;
977 			continue;
978 		}
979 		*sepp = sep->se_next;
980 		if (sep->se_fd != -1) {
981 			FD_CLR(sep->se_fd, allsockp);
982 			nsock--;
983 			(void) close(sep->se_fd);
984 		}
985 		if (isrpcservice(sep))
986 			unregister_rpc(sep);
987 		if (sep->se_family == AF_UNIX)
988 			(void)unlink(sep->se_service);
989 		if (debug)
990 			print_service("FREE", sep);
991 		freeconfig(sep);
992 		free((char *)sep);
993 	}
994 	(void) sigsetmask(omask);
995 }
996 
997 void
998 retry(int sig)
999 {
1000 	wantretry = 1;
1001 }
1002 
1003 void
1004 doretry(void)
1005 {
1006 	register struct servtab *sep;
1007 
1008 	timingout = 0;
1009 	for (sep = servtab; sep; sep = sep->se_next) {
1010 		if (sep->se_fd == -1) {
1011 			switch (sep->se_family) {
1012 			case AF_UNIX:
1013 			case AF_INET:
1014 			case AF_INET6:
1015 				setup(sep);
1016 				if (sep->se_fd != -1 && isrpcservice(sep))
1017 					register_rpc(sep);
1018 				break;
1019 			}
1020 		}
1021 	}
1022 }
1023 
1024 void
1025 goaway(sig)
1026 	int sig;
1027 {
1028 	register struct servtab *sep;
1029 
1030 	/* XXX signal race walking sep list */
1031 	for (sep = servtab; sep; sep = sep->se_next) {
1032 		if (sep->se_fd == -1)
1033 			continue;
1034 
1035 		switch (sep->se_family) {
1036 		case AF_UNIX:
1037 			(void)unlink(sep->se_service);
1038 			break;
1039 		case AF_INET:
1040 		case AF_INET6:
1041 			if (sep->se_wait == 1 && isrpcservice(sep))
1042 				unregister_rpc(sep);	/* XXX signal race */
1043 			break;
1044 		}
1045 		(void)close(sep->se_fd);
1046 	}
1047 	(void)unlink(_PATH_INETDPID);
1048 	_exit(0);
1049 }
1050 
1051 int bump_nofile __P((void));
1052 
1053 void
1054 setup(sep)
1055 	register struct servtab *sep;
1056 {
1057 	int on = 1;
1058 	int r;
1059 
1060 	if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
1061 		syslog(LOG_ERR, "%s/%s: socket: %m",
1062 		    sep->se_service, sep->se_proto);
1063 		return;
1064 	}
1065 #define	turnon(fd, opt) \
1066 setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
1067 	if (strncmp(sep->se_proto, "tcp", 3) == 0 && (options & SO_DEBUG) &&
1068 	    turnon(sep->se_fd, SO_DEBUG) < 0)
1069 		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
1070 	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
1071 		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
1072 #undef turnon
1073 	if (isrpcservice(sep)) {
1074 		struct passwd *pwd;
1075 
1076 		/*
1077 		 * for RPC services, attempt to use a reserved port
1078 		 * if they are going to be running as root.
1079 		 *
1080 		 * Also, zero out the port for all RPC services; let bind()
1081 		 * find one.
1082 		 */
1083 		sep->se_ctrladdr_in.sin_port = 0;
1084 		if (sep->se_user && (pwd = getpwnam(sep->se_user)) &&
1085 		    pwd->pw_uid == 0 && uid == 0)
1086 			r = bindresvport(sep->se_fd, &sep->se_ctrladdr_in);
1087 		else {
1088 			r = bind(sep->se_fd, &sep->se_ctrladdr,
1089 			    sep->se_ctrladdr_size);
1090 			if (r == 0) {
1091 				int len = sep->se_ctrladdr_size;
1092 				int saveerrno = errno;
1093 
1094 				/* update se_ctrladdr_in.sin_port */
1095 				r = getsockname(sep->se_fd, &sep->se_ctrladdr,
1096 				    &len);
1097 				if (r <= 0)
1098 					errno = saveerrno;
1099 			}
1100 		}
1101 	} else
1102 		r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size);
1103 	if (r < 0) {
1104 		syslog(LOG_ERR, "%s/%s: bind: %m",
1105 		    sep->se_service, sep->se_proto);
1106 		(void) close(sep->se_fd);
1107 		sep->se_fd = -1;
1108 		if (!timingout) {
1109 			timingout = 1;
1110 			alarm(RETRYTIME);
1111 		}
1112 		return;
1113 	}
1114 	if (sep->se_socktype == SOCK_STREAM)
1115 		listen(sep->se_fd, 10);
1116 
1117 	fd_grow(&allsockp, &allsockn, sep->se_fd);
1118 	FD_SET(sep->se_fd, allsockp);
1119 	nsock++;
1120 	if (sep->se_fd > maxsock) {
1121 		maxsock = sep->se_fd;
1122 		if (maxsock > rlim_ofile_cur - FD_MARGIN)
1123 			bump_nofile();
1124 	}
1125 }
1126 
1127 void
1128 register_rpc(sep)
1129 	register struct servtab *sep;
1130 {
1131 	int n;
1132 	struct sockaddr_in sin;
1133 	struct protoent *pp;
1134 
1135 	if ((pp = getprotobyname(sep->se_proto+4)) == NULL) {
1136 		syslog(LOG_ERR, "%s: getproto: %m",
1137 		    sep->se_proto);
1138 		return;
1139 	}
1140 	n = sizeof sin;
1141 	if (getsockname(sep->se_fd, (struct sockaddr *)&sin, &n) < 0) {
1142 		syslog(LOG_ERR, "%s/%s: getsockname: %m",
1143 		    sep->se_service, sep->se_proto);
1144 		return;
1145 	}
1146 
1147 	for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
1148 		if (debug)
1149 			fprintf(stderr, "pmap_set: %u %u %u %u\n",
1150 			    sep->se_rpcprog, n, pp->p_proto,
1151 			    ntohs(sin.sin_port));
1152 		(void)pmap_unset(sep->se_rpcprog, n);
1153 		if (!pmap_set(sep->se_rpcprog, n, pp->p_proto, ntohs(sin.sin_port)))
1154 			syslog(LOG_ERR, "%s %s: pmap_set: %u %u %u %u: %m",
1155 			    sep->se_service, sep->se_proto,
1156 			    sep->se_rpcprog, n, pp->p_proto,
1157 			    ntohs(sin.sin_port));
1158 	}
1159 }
1160 
1161 void
1162 unregister_rpc(sep)
1163 	register struct servtab *sep;
1164 {
1165 	int n;
1166 
1167 	for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
1168 		if (debug)
1169 			fprintf(stderr, "pmap_unset(%u, %u)\n",
1170 			    sep->se_rpcprog, n);
1171 		if (!pmap_unset(sep->se_rpcprog, n))
1172 			syslog(LOG_ERR, "pmap_unset(%u, %u)",
1173 			    sep->se_rpcprog, n);
1174 	}
1175 }
1176 
1177 
1178 struct servtab *
1179 enter(cp)
1180 	struct servtab *cp;
1181 {
1182 	register struct servtab *sep;
1183 	int omask;
1184 
1185 	sep = (struct servtab *)malloc(sizeof (*sep));
1186 	if (sep == NULL) {
1187 		syslog(LOG_ERR, "Out of memory.");
1188 		exit(1);
1189 	}
1190 	*sep = *cp;
1191 	sep->se_fd = -1;
1192 	sep->se_rpcprog = -1;
1193 	omask = sigblock(SIGBLOCK);
1194 	sep->se_next = servtab;
1195 	servtab = sep;
1196 	sigsetmask(omask);
1197 	return (sep);
1198 }
1199 
1200 int
1201 matchconf (old, new)
1202 	struct servtab	*old;
1203 	struct servtab	*new;
1204 {
1205 	if (strcmp(old->se_service, new->se_service) != 0)
1206 		return (0);
1207 
1208 	if (strcmp(old->se_hostaddr, new->se_hostaddr) != 0)
1209 		return (0);
1210 
1211 	if (strcmp(old->se_proto, new->se_proto) != 0)
1212 		return (0);
1213 
1214 	/*
1215 	 * If the new servtab is bound to a specific address, check that the
1216 	 * old servtab is bound to the same entry. If the new service is not
1217 	 * bound to a specific address then the check of se_hostaddr above
1218 	 * is sufficient.
1219 	 */
1220 
1221 	if (old->se_family == AF_INET && new->se_family == AF_INET &&
1222 	    bcmp(&old->se_ctrladdr_in.sin_addr,
1223 	    &new->se_ctrladdr_in.sin_addr,
1224 	    sizeof(new->se_ctrladdr_in.sin_addr)) != 0)
1225 		return (0);
1226 
1227 	if (old->se_family == AF_INET6 && new->se_family == AF_INET6 &&
1228 	    bcmp(&old->se_ctrladdr_in6.sin6_addr,
1229 	    &new->se_ctrladdr_in6.sin6_addr,
1230 	    sizeof(new->se_ctrladdr_in6.sin6_addr)) != 0)
1231 		return (0);
1232 	if (old->se_family == AF_INET6 && new->se_family == AF_INET6 &&
1233 	    old->se_ctrladdr_in6.sin6_scope_id !=
1234 	    new->se_ctrladdr_in6.sin6_scope_id)
1235 		return (0);
1236 
1237 	return (1);
1238 }
1239 
1240 FILE		*fconfig = NULL;
1241 char		line[1024];
1242 char		*defhost;
1243 char		*skip __P((char **, int));
1244 char		*nextline __P((FILE *));
1245 char		*newstr __P((char *));
1246 struct servtab	*dupconfig __P((struct servtab *));
1247 
1248 int
1249 setconfig()
1250 {
1251 	if (defhost) free(defhost);
1252 	defhost = newstr("*");
1253 	if (fconfig != NULL) {
1254 		fseek(fconfig, 0L, SEEK_SET);
1255 		return (1);
1256 	}
1257 	fconfig = fopen(CONFIG, "r");
1258 	return (fconfig != NULL);
1259 }
1260 
1261 void
1262 endconfig()
1263 {
1264 	if (fconfig) {
1265 		(void) fclose(fconfig);
1266 		fconfig = NULL;
1267 	}
1268 	if (defhost) {
1269 		free(defhost);
1270 		defhost = 0;
1271 	}
1272 }
1273 
1274 struct servtab *
1275 getconfigent()
1276 {
1277 	register struct servtab *sep;
1278 	int argc;
1279 	char *cp, *arg, *s;
1280 	char *hostdelim;
1281 	struct servtab *nsep;
1282 	struct servtab *psep;
1283 
1284 	sep = (struct servtab *) malloc(sizeof(struct servtab));
1285 	if (sep == NULL) {
1286 		syslog(LOG_ERR, "malloc: %m");
1287 		exit(1);
1288 	}
1289 
1290 	memset(sep, 0, sizeof *sep);
1291 more:
1292 	freeconfig(sep);
1293 
1294 	while ((cp = nextline(fconfig)) && *cp == '#')
1295 		;
1296 	if (cp == NULL) {
1297 		free(sep);
1298 		return (NULL);
1299 	}
1300 
1301 	memset((char *)sep, 0, sizeof *sep);
1302 	arg = skip(&cp, 0);
1303 	if (arg == NULL) {
1304 		/* A blank line. */
1305 		goto more;
1306 	}
1307 
1308 	/* Check for a host name. */
1309 	hostdelim = strrchr(arg, ':');
1310 	if (hostdelim) {
1311 		*hostdelim = '\0';
1312 		if (arg[0] == '[' && hostdelim > arg && hostdelim[-1] == ']') {
1313 			hostdelim[-1] = '\0';
1314 			sep->se_hostaddr = newstr(arg + 1);
1315 		} else
1316 			sep->se_hostaddr = newstr(arg);
1317 		arg = hostdelim + 1;
1318 		/*
1319 		 * If the line is of the form `host:', then just change the
1320 		 * default host for the following lines.
1321 		 */
1322 		if (*arg == '\0') {
1323 			arg = skip(&cp, 0);
1324 			if (cp == NULL) {
1325 				free(defhost);
1326 				defhost = newstr(sep->se_hostaddr);
1327 				goto more;
1328 			}
1329 		}
1330 	} else
1331 		sep->se_hostaddr = newstr(defhost);
1332 
1333 	sep->se_service = newstr(arg);
1334 	if ((arg = skip(&cp, 1)) == NULL)
1335 		goto more;
1336 
1337 	if (strcmp(arg, "stream") == 0)
1338 		sep->se_socktype = SOCK_STREAM;
1339 	else if (strcmp(arg, "dgram") == 0)
1340 		sep->se_socktype = SOCK_DGRAM;
1341 	else if (strcmp(arg, "rdm") == 0)
1342 		sep->se_socktype = SOCK_RDM;
1343 	else if (strcmp(arg, "seqpacket") == 0)
1344 		sep->se_socktype = SOCK_SEQPACKET;
1345 	else if (strcmp(arg, "raw") == 0)
1346 		sep->se_socktype = SOCK_RAW;
1347 	else
1348 		sep->se_socktype = -1;
1349 
1350 	if ((arg = skip(&cp, 1)) == NULL)
1351 		goto more;
1352 
1353 	sep->se_proto = newstr(arg);
1354 
1355 	if (strcmp(sep->se_proto, "unix") == 0) {
1356 		sep->se_family = AF_UNIX;
1357 	} else {
1358 		sep->se_family = AF_INET;
1359 		if (sep->se_proto[strlen(sep->se_proto) - 1] == '6')
1360 			sep->se_family = AF_INET6;
1361 		if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
1362 			char *cp, *ccp;
1363 			long l;
1364 
1365 			cp = strchr(sep->se_service, '/');
1366 			if (cp == 0) {
1367 				syslog(LOG_ERR, "%s: no rpc version",
1368 				    sep->se_service);
1369 				goto more;
1370 			}
1371 			*cp++ = '\0';
1372 			l = strtol(cp, &ccp, 0);
1373 			if (ccp == cp || l < 0 || l > INT_MAX) {
1374 		badafterall:
1375 				syslog(LOG_ERR, "%s/%s: bad rpc version",
1376 				    sep->se_service, cp);
1377 				goto more;
1378 			}
1379 			sep->se_rpcversl = sep->se_rpcversh = l;
1380 			if (*ccp == '-') {
1381 				cp = ccp + 1;
1382 				l = strtol(cp, &ccp, 0);
1383 				if (ccp == cp || l < 0 || l > INT_MAX ||
1384 				    l < sep->se_rpcversl || *ccp)
1385 					goto badafterall;
1386 				sep->se_rpcversh = l;
1387 			} else if (*ccp != '\0')
1388 				goto badafterall;
1389 		}
1390 	}
1391 	arg = skip(&cp, 1);
1392 	if (arg == NULL)
1393 		goto more;
1394 
1395 	s = strchr(arg, '.');
1396 	if (s) {
1397 		char *p;
1398 
1399 		*s++ = '\0';
1400 		sep->se_max = strtoul(s, &p, 0);
1401 		if (sep->se_max < 1 || *p) {
1402 			syslog(LOG_ERR, "%s: illegal max field \"%s\", setting to %d",
1403 			    sep->se_service, s, toomany);
1404 			sep->se_max = toomany;
1405 		}
1406 	} else
1407 		sep->se_max = toomany;
1408 
1409 	sep->se_wait = strcmp(arg, "wait") == 0;
1410 	if ((arg = skip(&cp, 1)) == NULL)
1411 		goto more;
1412 	sep->se_user = newstr(arg);
1413 	arg = strchr(sep->se_user, '.');
1414 	if (arg == NULL)
1415 		arg = strchr(sep->se_user, ':');
1416 	if (arg) {
1417 		*arg++ = '\0';
1418 		sep->se_group = newstr(arg);
1419 	}
1420 	if ((arg = skip(&cp, 1)) == NULL)
1421 		goto more;
1422 
1423 	sep->se_server = newstr(arg);
1424 	if (strcmp(sep->se_server, "internal") == 0) {
1425 		struct biltin *bi;
1426 
1427 		for (bi = biltins; bi->bi_service; bi++)
1428 			if (bi->bi_socktype == sep->se_socktype &&
1429 			    strcmp(bi->bi_service, sep->se_service) == 0)
1430 				break;
1431 		if (bi->bi_service == 0) {
1432 			syslog(LOG_ERR, "internal service %s unknown",
1433 			    sep->se_service);
1434 			goto more;
1435 		}
1436 		sep->se_bi = bi;
1437 		sep->se_wait = bi->bi_wait;
1438 	} else
1439 		sep->se_bi = NULL;
1440 	argc = 0;
1441 	for (arg = skip(&cp, 0); cp; arg = skip(&cp, 0)) {
1442 		if (argc < MAXARGV)
1443 			sep->se_argv[argc++] = newstr(arg);
1444 	}
1445 	while (argc <= MAXARGV)
1446 		sep->se_argv[argc++] = NULL;
1447 
1448 	/*
1449 	 * Now that we've processed the entire line, check if the hostname
1450 	 * specifier was a comma separated list of hostnames. If so
1451 	 * we'll make new entries for each address.
1452 	 */
1453 	while ((hostdelim = strrchr(sep->se_hostaddr, ',')) != NULL) {
1454 		nsep = dupconfig(sep);
1455 
1456 		/*
1457 		 * NULL terminate the hostname field of the existing entry,
1458 		 * and make a dup for the new entry.
1459 		 */
1460 		*hostdelim++ = '\0';
1461 		nsep->se_hostaddr = newstr(hostdelim);
1462 
1463 		nsep->se_next = sep->se_next;
1464 		sep->se_next = nsep;
1465 	}
1466 
1467 	nsep = sep;
1468 	while (nsep != NULL) {
1469 		nsep->se_checked = 1;
1470 		switch (nsep->se_family) {
1471 		case AF_INET:
1472 		case AF_INET6:
1473 		    {
1474 			struct addrinfo hints, *res0, *res;
1475 			char *host, *port;
1476 			int error;
1477 			int s;
1478 
1479 			/* check if the family is supported */
1480 			s = socket(nsep->se_family, SOCK_DGRAM, 0);
1481 			if (s < 0) {
1482 				syslog(LOG_WARNING,
1483 "%s/%s: %s: the address family is not supported by the kernel",
1484 				    nsep->se_service, nsep->se_proto,
1485 				    nsep->se_hostaddr);
1486 				nsep->se_checked = 0;
1487 				goto skip;
1488 			}
1489 			close(s);
1490 
1491 			memset(&hints, 0, sizeof(hints));
1492 			hints.ai_family = nsep->se_family;
1493 			hints.ai_socktype = nsep->se_socktype;
1494 			hints.ai_flags = AI_PASSIVE;
1495 			if (!strcmp(nsep->se_hostaddr, "*"))
1496 				host = NULL;
1497 			else
1498 				host = nsep->se_hostaddr;
1499 			port = "0";
1500 			/* XXX shortened IPv4 syntax is now forbidden */
1501 			error = getaddrinfo(host, port, &hints, &res0);
1502 			if (error) {
1503 				syslog(LOG_ERR, "%s/%s: %s: %s",
1504 				    nsep->se_service, nsep->se_proto,
1505 				    nsep->se_hostaddr,
1506 				    gai_strerror(error));
1507 				nsep->se_checked = 0;
1508 				goto skip;
1509 			}
1510 			for (res = res0; res; res = res->ai_next) {
1511 				if (res->ai_addrlen >
1512 				    sizeof(nsep->se_ctrladdr_storage))
1513 					continue;
1514 				if (res == res0) {
1515 					memcpy(&nsep->se_ctrladdr_storage,
1516 					    res->ai_addr, res->ai_addrlen);
1517 					continue;
1518 				}
1519 
1520 				psep = dupconfig(nsep);
1521 				psep->se_hostaddr = newstr(nsep->se_hostaddr);
1522 				psep->se_checked = 1;
1523 				memcpy(&psep->se_ctrladdr_storage, res->ai_addr,
1524 				    res->ai_addrlen);
1525 				psep->se_ctrladdr_size = res->ai_addrlen;
1526 
1527 				/*
1528 				 * Prepend to list, don't want to look up its
1529 				 * hostname again.
1530 				 */
1531 				psep->se_next = sep;
1532 				sep = psep;
1533 			}
1534 			freeaddrinfo(res0);
1535 			break;
1536 		    }
1537 		}
1538 skip:
1539 		nsep = nsep->se_next;
1540 	}
1541 
1542 	/*
1543 	 * Finally, free any entries which failed the gethostbyname
1544 	 * check.
1545 	 */
1546 	psep = NULL;
1547 	nsep = sep;
1548 	while (nsep != NULL) {
1549 		struct servtab *tsep;
1550 
1551 		if (nsep->se_checked == 0) {
1552 			tsep = nsep;
1553 			if (psep == NULL) {
1554 				sep = nsep->se_next;
1555 				nsep = sep;
1556 			} else {
1557 				nsep = nsep->se_next;
1558 				psep->se_next = nsep;
1559 			}
1560 			freeconfig(tsep);
1561 		} else {
1562 			nsep->se_checked = 0;
1563 			psep = nsep;
1564 			nsep = nsep->se_next;
1565 		}
1566 	}
1567 
1568 	return (sep);
1569 }
1570 
1571 void
1572 freeconfig(cp)
1573 	register struct servtab *cp;
1574 {
1575 	int i;
1576 
1577 	if (cp->se_hostaddr)
1578 		free(cp->se_hostaddr);
1579 	if (cp->se_service)
1580 		free(cp->se_service);
1581 	if (cp->se_proto)
1582 		free(cp->se_proto);
1583 	if (cp->se_user)
1584 		free(cp->se_user);
1585 	if (cp->se_group)
1586 		free(cp->se_group);
1587 	if (cp->se_server)
1588 		free(cp->se_server);
1589 	for (i = 0; i < MAXARGV; i++)
1590 		if (cp->se_argv[i])
1591 			free(cp->se_argv[i]);
1592 }
1593 
1594 char *
1595 skip(cpp, report)
1596 	char **cpp;
1597 	int report;
1598 {
1599 	char *cp = *cpp;
1600 	char *start;
1601 
1602 erp:
1603 	if (*cpp == NULL) {
1604 		if (report)
1605 			syslog(LOG_ERR, "syntax error in inetd config file");
1606 		return (NULL);
1607 	}
1608 
1609 again:
1610 	while (*cp == ' ' || *cp == '\t')
1611 		cp++;
1612 	if (*cp == '\0') {
1613 		int c;
1614 
1615 		c = getc(fconfig);
1616 		(void) ungetc(c, fconfig);
1617 		if (c == ' ' || c == '\t')
1618 			if ((cp = nextline(fconfig)))
1619 				goto again;
1620 		*cpp = NULL;
1621 		goto erp;
1622 	}
1623 	start = cp;
1624 	while (*cp && *cp != ' ' && *cp != '\t')
1625 		cp++;
1626 	if (*cp != '\0')
1627 		*cp++ = '\0';
1628 	if ((*cpp = cp) == NULL)
1629 		goto erp;
1630 
1631 	return (start);
1632 }
1633 
1634 char *
1635 nextline(fd)
1636 	FILE *fd;
1637 {
1638 	char *cp;
1639 
1640 	if (fgets(line, sizeof (line), fd) == NULL)
1641 		return (NULL);
1642 	cp = strchr(line, '\n');
1643 	if (cp)
1644 		*cp = '\0';
1645 	return (line);
1646 }
1647 
1648 char *
1649 newstr(cp)
1650 	char *cp;
1651 {
1652 	if ((cp = strdup(cp ? cp : "")))
1653 		return(cp);
1654 	syslog(LOG_ERR, "strdup: %m");
1655 	exit(1);
1656 }
1657 
1658 struct servtab *
1659 dupconfig(sep)
1660 	struct servtab *sep;
1661 {
1662 	struct servtab *newtab;
1663 	int argc;
1664 
1665 	newtab = (struct servtab *) malloc(sizeof(struct servtab));
1666 
1667 	if (newtab == NULL) {
1668 		syslog(LOG_ERR, "malloc: %m");
1669 		exit(1);
1670 	}
1671 
1672 	memset((char *)newtab, 0, sizeof(struct servtab));
1673 
1674 	newtab->se_service = sep->se_service ? newstr(sep->se_service) : NULL;
1675 	newtab->se_socktype = sep->se_socktype;
1676 	newtab->se_family = sep->se_family;
1677 	newtab->se_proto = sep->se_proto ? newstr(sep->se_proto) : NULL;
1678 	newtab->se_rpcprog = sep->se_rpcprog;
1679 	newtab->se_rpcversl = sep->se_rpcversl;
1680 	newtab->se_rpcversh = sep->se_rpcversh;
1681 	newtab->se_wait = sep->se_wait;
1682 	newtab->se_user = sep->se_user ? newstr(sep->se_user) : NULL;
1683 	newtab->se_group = sep->se_group ? newstr(sep->se_group) : NULL;
1684 	newtab->se_bi = sep->se_bi;
1685 	newtab->se_server = sep->se_server ? newstr(sep->se_server) : 0;
1686 
1687 	for (argc = 0; argc <= MAXARGV; argc++)
1688 		newtab->se_argv[argc] = sep->se_argv[argc] ?
1689 		    newstr(sep->se_argv[argc]) : NULL;
1690 	newtab->se_max = sep->se_max;
1691 
1692 	return (newtab);
1693 }
1694 
1695 void
1696 inetd_setproctitle(a, s)
1697 	char *a;
1698 	int s;
1699 {
1700 	int size;
1701 	register char *cp;
1702 	struct sockaddr_in sin;
1703 	char buf[80];
1704 
1705 	cp = Argv[0];
1706 	size = sizeof(sin);
1707 	(void) snprintf(buf, sizeof buf, "-%s", a);
1708 	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) {
1709 		char *s = inet_ntoa(sin.sin_addr);
1710 
1711 		buf[sizeof(buf) - 1 - strlen(s) - 3] = '\0';
1712 		strcat(buf, " [");
1713 		strcat(buf, s);
1714 		strcat(buf, "]");
1715 	}
1716 	strncpy(cp, buf, LastArg - cp);
1717 	cp += strlen(cp);
1718 	while (cp < LastArg)
1719 		*cp++ = ' ';
1720 }
1721 
1722 void
1723 logpid()
1724 {
1725 	FILE *fp;
1726 
1727 	if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) {
1728 		fprintf(fp, "%u\n", getpid());
1729 		(void)fclose(fp);
1730 	}
1731 }
1732 
1733 int
1734 bump_nofile()
1735 {
1736 #ifdef RLIMIT_NOFILE
1737 
1738 #define FD_CHUNK	32
1739 
1740 	struct rlimit rl;
1741 
1742 	if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
1743 		syslog(LOG_ERR, "getrlimit: %m");
1744 		return -1;
1745 	}
1746 	rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
1747 	rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);
1748 	if (rl.rlim_cur <= rlim_ofile_cur) {
1749 		syslog(LOG_ERR,
1750 		    "bump_nofile: cannot extend file limit, max = %d",
1751 		    (int)rl.rlim_cur);
1752 		return -1;
1753 	}
1754 
1755 	if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
1756 		syslog(LOG_ERR, "setrlimit: %m");
1757 		return -1;
1758 	}
1759 
1760 	rlim_ofile_cur = rl.rlim_cur;
1761 	return 0;
1762 
1763 #else
1764 	syslog(LOG_ERR, "bump_nofile: cannot extend file limit");
1765 	return -1;
1766 #endif
1767 }
1768 
1769 /*
1770  * Internet services provided internally by inetd:
1771  */
1772 #define	BUFSIZE	4096
1773 
1774 /* ARGSUSED */
1775 void
1776 echo_stream(s, sep)		/* Echo service -- echo data back */
1777 	int s;
1778 	struct servtab *sep;
1779 {
1780 	char buffer[BUFSIZE];
1781 	int i;
1782 
1783 	inetd_setproctitle(sep->se_service, s);
1784 	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
1785 	    write(s, buffer, i) > 0)
1786 		;
1787 	exit(0);
1788 }
1789 
1790 /* ARGSUSED */
1791 void
1792 echo_dg(s, sep)			/* Echo service -- echo data back */
1793 	int s;
1794 	struct servtab *sep;
1795 {
1796 	char buffer[BUFSIZE];
1797 	int i, size;
1798 	struct sockaddr_storage ss;
1799 
1800 	size = sizeof(ss);
1801 	if ((i = recvfrom(s, buffer, sizeof(buffer), 0, (struct sockaddr *)&ss,
1802 	    &size)) < 0)
1803 		return;
1804 	if (dg_badinput((struct sockaddr *)&ss))
1805 		return;
1806 	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size);
1807 }
1808 
1809 /* ARGSUSED */
1810 void
1811 discard_stream(s, sep)		/* Discard service -- ignore data */
1812 	int s;
1813 	struct servtab *sep;
1814 {
1815 	char buffer[BUFSIZE];
1816 
1817 	inetd_setproctitle(sep->se_service, s);
1818 	while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
1819 	    errno == EINTR)
1820 		;
1821 	exit(0);
1822 }
1823 
1824 /* ARGSUSED */
1825 void
1826 discard_dg(s, sep)		/* Discard service -- ignore data */
1827 	int s;
1828 	struct servtab *sep;
1829 {
1830 	char buffer[BUFSIZE];
1831 
1832 	(void) read(s, buffer, sizeof(buffer));
1833 }
1834 
1835 #include <ctype.h>
1836 #define LINESIZ 72
1837 char ring[128];
1838 char *endring;
1839 
1840 void
1841 initring()
1842 {
1843 	register int i;
1844 
1845 	endring = ring;
1846 
1847 	for (i = 0; i <= 128; ++i)
1848 		if (isprint(i))
1849 			*endring++ = i;
1850 }
1851 
1852 /* ARGSUSED */
1853 void
1854 chargen_stream(s, sep)		/* Character generator */
1855 	int s;
1856 	struct servtab *sep;
1857 {
1858 	register char *rs;
1859 	int len;
1860 	char text[LINESIZ+2];
1861 
1862 	inetd_setproctitle(sep->se_service, s);
1863 
1864 	if (!endring) {
1865 		initring();
1866 		rs = ring;
1867 	}
1868 
1869 	text[LINESIZ] = '\r';
1870 	text[LINESIZ + 1] = '\n';
1871 	for (rs = ring;;) {
1872 		if ((len = endring - rs) >= LINESIZ)
1873 			memmove(text, rs, LINESIZ);
1874 		else {
1875 			memmove(text, rs, len);
1876 			memmove(text + len, ring, LINESIZ - len);
1877 		}
1878 		if (++rs == endring)
1879 			rs = ring;
1880 		if (write(s, text, sizeof(text)) != sizeof(text))
1881 			break;
1882 	}
1883 	exit(0);
1884 }
1885 
1886 /* ARGSUSED */
1887 void
1888 chargen_dg(s, sep)		/* Character generator */
1889 	int s;
1890 	struct servtab *sep;
1891 {
1892 	struct sockaddr_storage ss;
1893 	static char *rs;
1894 	int len, size;
1895 	char text[LINESIZ+2];
1896 
1897 	if (endring == 0) {
1898 		initring();
1899 		rs = ring;
1900 	}
1901 
1902 	size = sizeof(ss);
1903 	if (recvfrom(s, text, sizeof(text), 0, (struct sockaddr *)&ss,
1904 	    &size) < 0)
1905 		return;
1906 	if (dg_badinput((struct sockaddr *)&ss))
1907 		return;
1908 
1909 	if ((len = endring - rs) >= LINESIZ)
1910 		memmove(text, rs, LINESIZ);
1911 	else {
1912 		memmove(text, rs, len);
1913 		memmove(text + len, ring, LINESIZ - len);
1914 	}
1915 	if (++rs == endring)
1916 		rs = ring;
1917 	text[LINESIZ] = '\r';
1918 	text[LINESIZ + 1] = '\n';
1919 	(void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size);
1920 }
1921 
1922 /*
1923  * Return a machine readable date and time, in the form of the
1924  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
1925  * returns the number of seconds since midnight, Jan 1, 1970,
1926  * we must add 2208988800 seconds to this figure to make up for
1927  * some seventy years Bell Labs was asleep.
1928  */
1929 
1930 u_int
1931 machtime()
1932 {
1933 	struct timeval tv;
1934 
1935 	if (gettimeofday(&tv, NULL) < 0) {
1936 		fprintf(stderr, "Unable to get time of day\n");
1937 		return (0L);
1938 	}
1939 	return (htonl((u_int)tv.tv_sec + 2208988800UL));
1940 }
1941 
1942 /* ARGSUSED */
1943 void
1944 machtime_stream(s, sep)
1945 	int s;
1946 	struct servtab *sep;
1947 {
1948 	u_int result;
1949 
1950 	result = machtime();
1951 	(void) write(s, (char *) &result, sizeof(result));
1952 }
1953 
1954 /* ARGSUSED */
1955 void
1956 machtime_dg(s, sep)
1957 	int s;
1958 	struct servtab *sep;
1959 {
1960 	u_int result;
1961 	struct sockaddr_storage ss;
1962 	int size;
1963 
1964 	size = sizeof(ss);
1965 	if (recvfrom(s, (char *)&result, sizeof(result), 0,
1966 	    (struct sockaddr *)&ss, &size) < 0)
1967 		return;
1968 	if (dg_badinput((struct sockaddr *)&ss))
1969 		return;
1970 	result = machtime();
1971 	(void) sendto(s, (char *) &result, sizeof(result), 0,
1972 	    (struct sockaddr *)&ss, size);
1973 }
1974 
1975 /* ARGSUSED */
1976 void
1977 daytime_stream(s, sep)		/* Return human-readable time of day */
1978 	int s;
1979 	struct servtab *sep;
1980 {
1981 	char buffer[256];
1982 	time_t time(), clock;
1983 
1984 	clock = time(NULL);
1985 
1986 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
1987 	(void) write(s, buffer, strlen(buffer));
1988 }
1989 
1990 /* ARGSUSED */
1991 void
1992 daytime_dg(s, sep)		/* Return human-readable time of day */
1993 	int s;
1994 	struct servtab *sep;
1995 {
1996 	char buffer[256];
1997 	time_t time(), clock;
1998 	struct sockaddr_storage ss;
1999 	int size;
2000 
2001 	clock = time((time_t *) 0);
2002 
2003 	size = sizeof(ss);
2004 	if (recvfrom(s, buffer, sizeof(buffer), 0, (struct sockaddr *)&ss,
2005 	    &size) < 0)
2006 		return;
2007 	if (dg_badinput((struct sockaddr *)&ss))
2008 		return;
2009 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
2010 	(void) sendto(s, buffer, strlen(buffer), 0, (struct sockaddr *)&ss,
2011 	    size);
2012 }
2013 
2014 /*
2015  * print_service:
2016  *	Dump relevant information to stderr
2017  */
2018 void
2019 print_service(action, sep)
2020 	char *action;
2021 	struct servtab *sep;
2022 {
2023 	if (strcmp(sep->se_hostaddr, "*") == 0)
2024 		fprintf(stderr, "%s: %s ", action, sep->se_service);
2025 	else
2026 		fprintf(stderr, "%s: %s:%s ", action, sep->se_hostaddr,
2027 		    sep->se_service);
2028 
2029 	if (isrpcservice(sep))
2030 		fprintf(stderr, "rpcprog=%d, rpcvers=%d/%d, proto=%s,",
2031 		    sep->se_rpcprog, sep->se_rpcversh,
2032 		    sep->se_rpcversl, sep->se_proto);
2033 	else
2034 		fprintf(stderr, "proto=%s,", sep->se_proto);
2035 
2036 	fprintf(stderr,
2037 	    " wait.max=%hd.%d user:group=%s.%s builtin=%lx server=%s\n",
2038 	    sep->se_wait, sep->se_max, sep->se_user,
2039 	    sep->se_group ? sep->se_group : "wheel",
2040 	    (long)sep->se_bi, sep->se_server);
2041 }
2042