xref: /netbsd-src/usr.sbin/inetd/inetd.c (revision 71fa92b75e339b362b9e5081fd71c1d9745d93ad)
1*71fa92b7Schristos /*	$NetBSD: inetd.c,v 1.141 2022/08/10 08:37:53 christos Exp $	*/
2723fb3ccSthorpej 
3723fb3ccSthorpej /*-
4726769adStron  * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc.
5723fb3ccSthorpej  * All rights reserved.
6723fb3ccSthorpej  *
7723fb3ccSthorpej  * This code is derived from software contributed to The NetBSD Foundation
8723fb3ccSthorpej  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9726769adStron  * NASA Ames Research Center and by Matthias Scheler.
10723fb3ccSthorpej  *
11723fb3ccSthorpej  * Redistribution and use in source and binary forms, with or without
12723fb3ccSthorpej  * modification, are permitted provided that the following conditions
13723fb3ccSthorpej  * are met:
14723fb3ccSthorpej  * 1. Redistributions of source code must retain the above copyright
15723fb3ccSthorpej  *    notice, this list of conditions and the following disclaimer.
16723fb3ccSthorpej  * 2. Redistributions in binary form must reproduce the above copyright
17723fb3ccSthorpej  *    notice, this list of conditions and the following disclaimer in the
18723fb3ccSthorpej  *    documentation and/or other materials provided with the distribution.
19723fb3ccSthorpej  *
20723fb3ccSthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21723fb3ccSthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22723fb3ccSthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23723fb3ccSthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24723fb3ccSthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25723fb3ccSthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26723fb3ccSthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27723fb3ccSthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28723fb3ccSthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29723fb3ccSthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30723fb3ccSthorpej  * POSSIBILITY OF SUCH DAMAGE.
31723fb3ccSthorpej  */
329df02875Smycroft 
3361f28255Scgd /*
34cb666b19Smycroft  * Copyright (c) 1983, 1991, 1993, 1994
35cb666b19Smycroft  *	The Regents of the University of California.  All rights reserved.
3661f28255Scgd  *
3761f28255Scgd  * Redistribution and use in source and binary forms, with or without
3861f28255Scgd  * modification, are permitted provided that the following conditions
3961f28255Scgd  * are met:
4061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
4161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
4261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
4361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
4461f28255Scgd  *    documentation and/or other materials provided with the distribution.
45326b2259Sagc  * 3. Neither the name of the University nor the names of its contributors
4661f28255Scgd  *    may be used to endorse or promote products derived from this software
4761f28255Scgd  *    without specific prior written permission.
4861f28255Scgd  *
4961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5961f28255Scgd  * SUCH DAMAGE.
6061f28255Scgd  */
6161f28255Scgd 
622d06dcebSmrg #include <sys/cdefs.h>
6361f28255Scgd #ifndef lint
649c194566Slukem __COPYRIGHT("@(#) Copyright (c) 1983, 1991, 1993, 1994\
659c194566Slukem  The Regents of the University of California.  All rights reserved.");
66cb666b19Smycroft #if 0
67cb666b19Smycroft static char sccsid[] = "@(#)inetd.c	8.4 (Berkeley) 4/13/94";
68cb666b19Smycroft #else
69*71fa92b7Schristos __RCSID("$NetBSD: inetd.c,v 1.141 2022/08/10 08:37:53 christos Exp $");
70cb666b19Smycroft #endif
7161f28255Scgd #endif /* not lint */
7261f28255Scgd 
7361f28255Scgd /*
7461f28255Scgd  * Inetd - Internet super-server
7561f28255Scgd  *
76cb666b19Smycroft  * This program invokes all internet services as needed.  Connection-oriented
77cb666b19Smycroft  * services are invoked each time a connection is made, by creating a process.
78cb666b19Smycroft  * This process is passed the connection as file descriptor 0 and is expected
79cb666b19Smycroft  * to do a getpeername to find out the source host and port.
8061f28255Scgd  *
8161f28255Scgd  * Datagram oriented services are invoked when a datagram
8261f28255Scgd  * arrives; a process is created and passed a pending message
8361f28255Scgd  * on file descriptor 0.  Datagram servers may either connect
8461f28255Scgd  * to their peer, freeing up the original socket for inetd
8561f28255Scgd  * to receive further messages on, or ``take over the socket'',
8661f28255Scgd  * processing all arriving datagrams and, eventually, timing
8761f28255Scgd  * out.	 The first type of server is said to be ``multi-threaded'';
8861f28255Scgd  * the second type of server ``single-threaded''.
8961f28255Scgd  *
9061f28255Scgd  * Inetd uses a configuration file which is read at startup
9161f28255Scgd  * and, possibly, at some later time in response to a hangup signal.
9261f28255Scgd  * The configuration file is ``free format'' with fields given in the
9361f28255Scgd  * order shown below.  Continuation lines for an entry must being with
9461f28255Scgd  * a space or tab.  All fields must be present in each entry.
9561f28255Scgd  *
96cb666b19Smycroft  *	service name			must be in /etc/services or must
97cb666b19Smycroft  *					name a tcpmux service
98717f903aStls  *	socket type[:accf[,arg]]	stream/dgram/raw/rdm/seqpacket,
99717f903aStls 					only stream can name an accept filter
10061f28255Scgd  *	protocol			must be in /etc/protocols
1014a827eaaStsarna  *	wait/nowait[:max]		single-threaded/multi-threaded, max #
102d3f47cfbSad  *	user[:group]			user/group to run daemon as
10361f28255Scgd  *	server program			full path name
104ee3280f9Smartin  *	server program arguments	maximum of MAXARGV (64)
10561f28255Scgd  *
1065dffb9b1Sbrezak  * For RPC services
1075dffb9b1Sbrezak  *	service name/version		must be in /etc/rpc
1085dffb9b1Sbrezak  *	socket type			stream/dgram/raw/rdm/seqpacket
1095dffb9b1Sbrezak  *	protocol			must be in /etc/protocols
1104a827eaaStsarna  *	wait/nowait[:max]		single-threaded/multi-threaded
111d3f47cfbSad  *	user[:group]			user to run daemon as
1125dffb9b1Sbrezak  *	server program			full path name
113ee3280f9Smartin  *	server program arguments	maximum of MAXARGV (64)
1145dffb9b1Sbrezak  *
115f02c2e56Smouse  * For non-RPC services, the "service name" can be of the form
116f02c2e56Smouse  * hostaddress:servicename, in which case the hostaddress is used
117f02c2e56Smouse  * as the host portion of the address to listen on.  If hostaddress
118f02c2e56Smouse  * consists of a single `*' character, INADDR_ANY is used.
119f02c2e56Smouse  *
120f02c2e56Smouse  * A line can also consist of just
121f02c2e56Smouse  *	hostaddress:
122f02c2e56Smouse  * where hostaddress is as in the preceding paragraph.  Such a line must
123f02c2e56Smouse  * have no further fields; the specified hostaddress is remembered and
124f02c2e56Smouse  * used for all further lines that have no hostaddress specified,
125f02c2e56Smouse  * until the next such line (or EOF).  (This is why * is provided to
126f02c2e56Smouse  * allow explicit specification of INADDR_ANY.)  A line
127f02c2e56Smouse  *	*:
128f02c2e56Smouse  * is implicitly in effect at the beginning of the file.
129f02c2e56Smouse  *
130f02c2e56Smouse  * The hostaddress specifier may (and often will) contain dots;
131f02c2e56Smouse  * the service name must not.
132f02c2e56Smouse  *
133f02c2e56Smouse  * For RPC services, host-address specifiers are accepted and will
134f02c2e56Smouse  * work to some extent; however, because of limitations in the
135f02c2e56Smouse  * portmapper interface, it will not work to try to give more than
136f02c2e56Smouse  * one line for any given RPC service, even if the host-address
137f02c2e56Smouse  * specifiers are different.
138f02c2e56Smouse  *
139cb666b19Smycroft  * TCP services without official port numbers are handled with the
140cb666b19Smycroft  * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
141cb666b19Smycroft  * requests. When a connection is made from a foreign host, the service
142cb666b19Smycroft  * requested is passed to tcpmux, which looks it up in the servtab list
143cb666b19Smycroft  * and returns the proper entry for the service. Tcpmux returns a
144cb666b19Smycroft  * negative reply if the service doesn't exist, otherwise the invoked
145cb666b19Smycroft  * server is expected to return the positive reply if the service type in
146cb666b19Smycroft  * inetd.conf file has the prefix "tcpmux/". If the service type has the
147cb666b19Smycroft  * prefix "tcpmux/+", tcpmux will return the positive reply for the
148cb666b19Smycroft  * process; this is for compatibility with older server code, and also
149cb666b19Smycroft  * allows you to invoke programs that use stdin/stdout without putting any
150cb666b19Smycroft  * special server code in them. Services that use tcpmux are "nowait"
151cb666b19Smycroft  * because they do not have a well-known port and hence cannot listen
152cb666b19Smycroft  * for new requests.
153cb666b19Smycroft  *
15461f28255Scgd  * Comment lines are indicated by a `#' in column 1.
155a77871b8Sitojun  *
156a77871b8Sitojun  * #ifdef IPSEC
157a77871b8Sitojun  * Comment lines that start with "#@" denote IPsec policy string, as described
158a77871b8Sitojun  * in ipsec_set_policy(3).  This will affect all the following items in
159a77871b8Sitojun  * inetd.conf(8).  To reset the policy, just use "#@" line.  By default,
160a77871b8Sitojun  * there's no IPsec policy.
161a77871b8Sitojun  * #endif
16261f28255Scgd  */
16308ba41f4Spk 
16408ba41f4Spk /*
165d3f47cfbSad  * Here's the scoop concerning the user:group feature:
16608ba41f4Spk  *
16708ba41f4Spk  * 1) set-group-option off.
16808ba41f4Spk  *
16908ba41f4Spk  * 	a) user = root:	NO setuid() or setgid() is done
17008ba41f4Spk  *
17108ba41f4Spk  * 	b) other:	setuid()
17208ba41f4Spk  * 			setgid(primary group as found in passwd)
17308ba41f4Spk  * 			initgroups(name, primary group)
17408ba41f4Spk  *
17508ba41f4Spk  * 2) set-group-option on.
17608ba41f4Spk  *
17708ba41f4Spk  * 	a) user = root:	NO setuid()
17808ba41f4Spk  * 			setgid(specified group)
17908ba41f4Spk  * 			NO initgroups()
18008ba41f4Spk  *
18108ba41f4Spk  * 	b) other:	setuid()
18208ba41f4Spk  * 			setgid(specified group)
18308ba41f4Spk  * 			initgroups(name, specified group)
18408ba41f4Spk  *
18508ba41f4Spk  */
18608ba41f4Spk 
18708ba41f4Spk #include <sys/param.h>
18808ba41f4Spk #include <sys/stat.h>
18908ba41f4Spk #include <sys/ioctl.h>
19008ba41f4Spk #include <sys/wait.h>
19108ba41f4Spk #include <sys/resource.h>
1928be71949Stron #include <sys/event.h>
19325573806Schristos #include <sys/socket.h>
194b19025f3Schristos #include <sys/queue.h>
19525573806Schristos 
196723fb3ccSthorpej #include <ctype.h>
19725573806Schristos #include <err.h>
198cb666b19Smycroft #include <errno.h>
199b860cb42Smycroft #include <fcntl.h>
20025573806Schristos #include <glob.h>
201cb666b19Smycroft #include <grp.h>
20225573806Schristos #include <libgen.h>
203cb666b19Smycroft #include <pwd.h>
204cb666b19Smycroft #include <signal.h>
205cb666b19Smycroft #include <stdio.h>
206cb666b19Smycroft #include <stdlib.h>
207cb666b19Smycroft #include <string.h>
208cb666b19Smycroft #include <syslog.h>
209cb666b19Smycroft #include <unistd.h>
21078688ba7Sthorpej #include <util.h>
21100a0a652Sitojun #include <ifaddrs.h>
212cb666b19Smycroft 
21325573806Schristos #include "inetd.h"
214a77871b8Sitojun 
2154e76afacSmrg #ifdef LIBWRAP
2164e76afacSmrg # include <tcpd.h>
217b95b23e3Smrg #ifndef LIBWRAP_ALLOW_FACILITY
218ccf88919Smouse # define LIBWRAP_ALLOW_FACILITY LOG_AUTH
219b95b23e3Smrg #endif
220b95b23e3Smrg #ifndef LIBWRAP_ALLOW_SEVERITY
221b95b23e3Smrg # define LIBWRAP_ALLOW_SEVERITY LOG_INFO
222b95b23e3Smrg #endif
223b95b23e3Smrg #ifndef LIBWRAP_DENY_FACILITY
224ccf88919Smouse # define LIBWRAP_DENY_FACILITY LOG_AUTH
225b95b23e3Smrg #endif
226b95b23e3Smrg #ifndef LIBWRAP_DENY_SEVERITY
227b95b23e3Smrg # define LIBWRAP_DENY_SEVERITY LOG_WARNING
228b95b23e3Smrg #endif
229b95b23e3Smrg int allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
230b95b23e3Smrg int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
2314e76afacSmrg #endif
2324e76afacSmrg 
233b19025f3Schristos static bool foreground;
2344e76afacSmrg int	debug;
2354e76afacSmrg #ifdef LIBWRAP
2364e76afacSmrg int	lflag;
2374e76afacSmrg #endif
23839e99fe8Stron int	maxsock;
2398be71949Stron int	kq;
24061f28255Scgd int	options;
24161f28255Scgd int	timingout;
24255ffb1ceSitojun const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
24308ba41f4Spk 
24408ba41f4Spk #ifndef OPEN_MAX
24508ba41f4Spk #define OPEN_MAX	64
24608ba41f4Spk #endif
24708ba41f4Spk 
24808ba41f4Spk /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
24908ba41f4Spk #define FD_MARGIN	(8)
25070c4e415Smycroft rlim_t		rlim_ofile_cur = OPEN_MAX;
25108ba41f4Spk 
25208ba41f4Spk struct rlimit	rlim_ofile;
25361f28255Scgd 
254726769adStron struct kevent	changebuf[64];
255726769adStron size_t		changes;
256726769adStron 
25725573806Schristos struct servtab *servtab;
258cb666b19Smycroft 
259223f9ca4Sryo static ssize_t	recvfromto(int, void * restrict, size_t, int,
260223f9ca4Sryo     struct sockaddr * restrict, socklen_t * restrict,
261223f9ca4Sryo     struct sockaddr * restrict, socklen_t * restrict);
262223f9ca4Sryo static ssize_t	sendfromto(int, const void *, size_t, int,
263223f9ca4Sryo     const struct sockaddr *, socklen_t, const struct sockaddr *, socklen_t);
264726769adStron static void	chargen_dg(int, struct servtab *);
265726769adStron static void	chargen_stream(int, struct servtab *);
266726769adStron static void	daytime_dg(int, struct servtab *);
267726769adStron static void	daytime_stream(int, struct servtab *);
268726769adStron static void	discard_dg(int, struct servtab *);
269726769adStron static void	discard_stream(int, struct servtab *);
270726769adStron static void	echo_dg(int, struct servtab *);
271726769adStron static void	echo_stream(int, struct servtab *);
272bec77c5fSjoerg __dead static void	goaway(void);
273726769adStron static void	machtime_dg(int, struct servtab *);
274726769adStron static void	machtime_stream(int, struct servtab *);
275726769adStron static void	reapchild(void);
276726769adStron static void	retry(void);
277e62b84baSdholland static void	run_service(int, struct servtab *, int);
278726769adStron static void	tcpmux(int, struct servtab *);
279bec77c5fSjoerg __dead static void	usage(void);
280726769adStron static void	bump_nofile(void);
281726769adStron static void	inetd_setproctitle(char *, int);
282726769adStron static void	initring(void);
283726769adStron static uint32_t	machtime(void);
284726769adStron static int	port_good_dg(struct sockaddr *);
285726769adStron static int	dg_broadcast(struct in_addr *);
28625573806Schristos static int	my_kevent(const struct kevent *, size_t, struct kevent *, size_t);
287726769adStron static struct kevent	*allocchange(void);
2887027866aSroy static int	get_line(int, char *, int);
289726769adStron static void	spawn(struct servtab *, int);
29061f28255Scgd 
29161f28255Scgd struct biltin {
2928b39f71dSchristos 	const char *bi_service;		/* internally provided service name */
29361f28255Scgd 	int	bi_socktype;		/* type of socket supported */
29461f28255Scgd 	short	bi_fork;		/* 1 if should fork before call */
29561f28255Scgd 	short	bi_wait;		/* 1 if should wait for child */
2968be71949Stron 	void	(*bi_fn)(int, struct servtab *);
2972d06dcebSmrg 					/* function which performs it */
29861f28255Scgd } biltins[] = {
29961f28255Scgd 	/* Echo received data */
300adeed07fSrillig 	{ "echo",	SOCK_STREAM,	true, false,	echo_stream },
301adeed07fSrillig 	{ "echo",	SOCK_DGRAM,	false, false,	echo_dg },
30261f28255Scgd 
30361f28255Scgd 	/* Internet /dev/null */
304adeed07fSrillig 	{ "discard",	SOCK_STREAM,	true, false,	discard_stream },
305adeed07fSrillig 	{ "discard",	SOCK_DGRAM,	false, false,	discard_dg },
30661f28255Scgd 
3077d7091ccSmrg 	/* Return 32 bit time since 1970 */
308adeed07fSrillig 	{ "time",	SOCK_STREAM,	false, false,	machtime_stream },
309adeed07fSrillig 	{ "time",	SOCK_DGRAM,	false, false,	machtime_dg },
31061f28255Scgd 
31161f28255Scgd 	/* Return human-readable time */
312adeed07fSrillig 	{ "daytime",	SOCK_STREAM,	false, false,	daytime_stream },
313adeed07fSrillig 	{ "daytime",	SOCK_DGRAM,	false, false,	daytime_dg },
31461f28255Scgd 
31561f28255Scgd 	/* Familiar character generator */
316adeed07fSrillig 	{ "chargen",	SOCK_STREAM,	true, false,	chargen_stream },
317adeed07fSrillig 	{ "chargen",	SOCK_DGRAM,	false, false,	chargen_dg },
318cb666b19Smycroft 
319b19025f3Schristos 	{ "tcpmux",	SOCK_STREAM,	true, false,	tcpmux }
32061f28255Scgd };
32161f28255Scgd 
322f6aa0f50Shwr /* list of "bad" ports. I.e. ports that are most obviously used for
323f6aa0f50Shwr  * "cycling packets" denial of service attacks. See /etc/services.
324f6aa0f50Shwr  * List must end with port number "0".
325f6aa0f50Shwr  */
326f6aa0f50Shwr 
327f6aa0f50Shwr u_int16_t bad_ports[] =  { 7, 9, 13, 19, 37, 0 };
328f6aa0f50Shwr 
329f6aa0f50Shwr 
33061f28255Scgd #define NUMINT	(sizeof(intab) / sizeof(struct inent))
3318b39f71dSchristos const char	*CONFIG = _PATH_INETDCONF;
33208ba41f4Spk 
33339e99fe8Stron static int my_signals[] =
33439e99fe8Stron     { SIGALRM, SIGHUP, SIGCHLD, SIGTERM, SIGINT, SIGPIPE };
335726769adStron 
3362d06dcebSmrg int
main(int argc,char * argv[])337726769adStron main(int argc, char *argv[])
33861f28255Scgd {
339726769adStron 	int		ch, n, reload = 1;
34061f28255Scgd 
3414e76afacSmrg 	while ((ch = getopt(argc, argv,
3424e76afacSmrg #ifdef LIBWRAP
343b19025f3Schristos 					"dfl"
3444e76afacSmrg #else
345b19025f3Schristos 					"df"
3464e76afacSmrg #endif
347a352e573Slukem 					   )) != -1)
34861f28255Scgd 		switch(ch) {
34961f28255Scgd 		case 'd':
350b19025f3Schristos 			foreground = true;
351adeed07fSrillig 			debug = true;
35261f28255Scgd 			options |= SO_DEBUG;
35361f28255Scgd 			break;
354b19025f3Schristos 		case 'f':
355b19025f3Schristos 			foreground = true;
356b19025f3Schristos 			break;
3574e76afacSmrg #ifdef LIBWRAP
3584e76afacSmrg 		case 'l':
359adeed07fSrillig 			lflag = true;
3604e76afacSmrg 			break;
3614e76afacSmrg #endif
36261f28255Scgd 		case '?':
36361f28255Scgd 		default:
364cb666b19Smycroft 			usage();
36561f28255Scgd 		}
36661f28255Scgd 	argc -= optind;
36761f28255Scgd 	argv += optind;
36861f28255Scgd 
36961f28255Scgd 	if (argc > 0)
37061f28255Scgd 		CONFIG = argv[0];
37108ba41f4Spk 
372b19025f3Schristos 	if (!foreground)
37361f28255Scgd 		daemon(0, 0);
3740645f2f6Slukem 	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
37578688ba7Sthorpej 	pidfile(NULL);
37608ba41f4Spk 
3778be71949Stron 	kq = kqueue();
3788be71949Stron 	if (kq < 0) {
3798be71949Stron 		syslog(LOG_ERR, "kqueue: %m");
3808be71949Stron 		return (EXIT_FAILURE);
3818be71949Stron 	}
3828be71949Stron 
383e0738ce1Spk 	if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
38408ba41f4Spk 		syslog(LOG_ERR, "getrlimit: %m");
38508ba41f4Spk 	} else {
38608ba41f4Spk 		rlim_ofile_cur = rlim_ofile.rlim_cur;
387e0738ce1Spk 		if (rlim_ofile_cur == RLIM_INFINITY)	/* ! */
388e0738ce1Spk 			rlim_ofile_cur = OPEN_MAX;
38908ba41f4Spk 	}
39008ba41f4Spk 
39125573806Schristos 	for (n = 0; n < (int)__arraycount(my_signals); n++) {
39239e99fe8Stron 		int	signum;
39339e99fe8Stron 
39439e99fe8Stron 		signum = my_signals[n];
3952f3bdfceSchristos 		if (signum != SIGCHLD)
39639e99fe8Stron 			(void) signal(signum, SIG_IGN);
39739e99fe8Stron 
39839e99fe8Stron 		if (signum != SIGPIPE) {
39939e99fe8Stron 			struct kevent	*ev;
40039e99fe8Stron 
401726769adStron 			ev = allocchange();
40239e99fe8Stron 			EV_SET(ev, signum, EVFILT_SIGNAL, EV_ADD | EV_ENABLE,
4037de4819eSfvdl 			    0, 0, 0);
404726769adStron 		}
40539e99fe8Stron 	}
40661f28255Scgd 
40761f28255Scgd 	for (;;) {
408726769adStron 		int		ctrl;
40939e99fe8Stron 		struct kevent	eventbuf[64], *ev;
410726769adStron 		struct servtab	*sep;
411726769adStron 
412726769adStron 		if (reload) {
413adeed07fSrillig 			reload = false;
41425573806Schristos 			config_root();
415726769adStron 		}
41661f28255Scgd 
41725573806Schristos 		n = my_kevent(changebuf, changes, eventbuf, __arraycount(eventbuf));
418726769adStron 		changes = 0;
419726769adStron 
420726769adStron 		for (ev = eventbuf; n > 0; ev++, n--) {
421726769adStron 			if (ev->filter == EVFILT_SIGNAL) {
422726769adStron 				switch (ev->ident) {
423726769adStron 				case SIGALRM:
424726769adStron 					retry();
425726769adStron 					break;
426726769adStron 				case SIGCHLD:
427726769adStron 					reapchild();
428726769adStron 					break;
429726769adStron 				case SIGTERM:
430726769adStron 				case SIGINT:
431726769adStron 					goaway();
432726769adStron 					break;
433726769adStron 				case SIGHUP:
434adeed07fSrillig 					reload = true;
435726769adStron 					break;
436726769adStron 				}
43761f28255Scgd 				continue;
438726769adStron 			}
439726769adStron 			if (ev->filter != EVFILT_READ)
440726769adStron 				continue;
441726769adStron 			sep = (struct servtab *)ev->udata;
4428be71949Stron 			/* Paranoia */
4437eb2c18bSrillig 			if ((int)ev->ident != sep->se_fd)
4442dc34bbeSitojun 				continue;
44525573806Schristos 			DPRINTF(SERV_FMT ": service requested" , SERV_PARAMS(sep));
446adeed07fSrillig 			if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM) {
4474e76afacSmrg 				/* XXX here do the libwrap check-before-accept*/
4488be71949Stron 				ctrl = accept(sep->se_fd, NULL, NULL);
44925573806Schristos 				DPRINTF(SERV_FMT ": accept, ctrl fd %d",
45025573806Schristos 				    SERV_PARAMS(sep), ctrl);
45161f28255Scgd 				if (ctrl < 0) {
452cb666b19Smycroft 					if (errno != EINTR)
453cb666b19Smycroft 						syslog(LOG_WARNING,
45425573806Schristos 						    SERV_FMT ": accept: %m",
45525573806Schristos 						    SERV_PARAMS(sep));
45661f28255Scgd 					continue;
45761f28255Scgd 				}
45861f28255Scgd 			} else
45961f28255Scgd 				ctrl = sep->se_fd;
4602dc34bbeSitojun 			spawn(sep, ctrl);
4612dc34bbeSitojun 		}
4622dc34bbeSitojun 	}
4632dc34bbeSitojun }
4642dc34bbeSitojun 
465726769adStron static void
spawn(struct servtab * sep,int ctrl)466726769adStron spawn(struct servtab *sep, int ctrl)
4672dc34bbeSitojun {
4682dc34bbeSitojun 	int dofork;
4692dc34bbeSitojun 	pid_t pid;
4702dc34bbeSitojun 
47161f28255Scgd 	pid = 0;
47251fa5e9bSmycroft #ifdef LIBWRAP_INTERNAL
473adeed07fSrillig 	dofork = true;
474df6ec691Smycroft #else
475adeed07fSrillig 	dofork = (sep->se_bi == NULL || sep->se_bi->bi_fork);
476df6ec691Smycroft #endif
47761f28255Scgd 	if (dofork) {
47825573806Schristos 		if (rl_process(sep, ctrl)) {
4792dc34bbeSitojun 			return;
48061f28255Scgd 		}
48161f28255Scgd 		pid = fork();
48261f28255Scgd 		if (pid < 0) {
48361f28255Scgd 			syslog(LOG_ERR, "fork: %m");
484adeed07fSrillig 			if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM)
48561f28255Scgd 				close(ctrl);
48661f28255Scgd 			sleep(1);
4872dc34bbeSitojun 			return;
48861f28255Scgd 		}
489adeed07fSrillig 		if (pid != 0 && sep->se_wait != 0) {
490726769adStron 			struct kevent	*ev;
4918be71949Stron 
49261f28255Scgd 			sep->se_wait = pid;
493726769adStron 			ev = allocchange();
494726769adStron 			EV_SET(ev, sep->se_fd, EVFILT_READ,
4958be71949Stron 			    EV_DELETE, 0, 0, 0);
49661f28255Scgd 		}
497b4d89784Smycroft 		if (pid == 0) {
4987eb2c18bSrillig 			size_t	n;
49939e99fe8Stron 
50025573806Schristos 			for (n = 0; n < __arraycount(my_signals); n++)
50139e99fe8Stron 				(void) signal(my_signals[n], SIG_DFL);
502b19025f3Schristos 			/* Don't put services in terminal session */
503b19025f3Schristos 			if (foreground)
504cf5f8c25Smycroft 				setsid();
505cf5f8c25Smycroft 		}
506b4d89784Smycroft 	}
50761f28255Scgd 	if (pid == 0) {
508e62b84baSdholland 		run_service(ctrl, sep, dofork);
509b860cb42Smycroft 		if (dofork)
510b19025f3Schristos 			exit(EXIT_SUCCESS);
511b860cb42Smycroft 	}
512adeed07fSrillig 	if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM)
513b860cb42Smycroft 		close(ctrl);
514b860cb42Smycroft }
515b860cb42Smycroft 
516726769adStron static void
run_service(int ctrl,struct servtab * sep,int didfork)517e62b84baSdholland run_service(int ctrl, struct servtab *sep, int didfork)
518b860cb42Smycroft {
519b860cb42Smycroft 	struct passwd *pwd;
5202d06dcebSmrg 	struct group *grp = NULL;	/* XXX gcc */
52155ffb1ceSitojun 	char buf[NI_MAXSERV];
522e62b84baSdholland 	struct servtab *s;
523b860cb42Smycroft #ifdef LIBWRAP
5245fe5dff0Schristos 	char abuf[BUFSIZ];
525b860cb42Smycroft 	struct request_info req;
526b860cb42Smycroft 	int denied;
5279fab7a4fSchristos 	char *service = NULL;	/* XXX gcc */
528b860cb42Smycroft #endif
529b860cb42Smycroft 
5309df02875Smycroft #ifdef LIBWRAP
53151fa5e9bSmycroft #ifndef LIBWRAP_INTERNAL
532a68a078fSmycroft 	if (sep->se_bi == 0)
53351fa5e9bSmycroft #endif
534adeed07fSrillig 	if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM) {
535adeed07fSrillig 		request_init(&req, RQ_DAEMON, sep->se_argv[0] != NULL ?
536b860cb42Smycroft 		    sep->se_argv[0] : sep->se_service, RQ_FILE, ctrl, NULL);
5379df02875Smycroft 		fromhost(&req);
538adeed07fSrillig 		denied = hosts_access(&req) == 0;
5399df02875Smycroft 		if (denied || lflag) {
540e1b53de4Sitojun 			if (getnameinfo(&sep->se_ctrladdr,
5418b39f71dSchristos 			    (socklen_t)sep->se_ctrladdr.sa_len, NULL, 0,
542e1b53de4Sitojun 			    buf, sizeof(buf), 0) != 0) {
543e1b53de4Sitojun 				/* shouldn't happen */
5449df02875Smycroft 				(void)snprintf(buf, sizeof buf, "%d",
5459df02875Smycroft 				    ntohs(sep->se_ctrladdr_in.sin_port));
546e1b53de4Sitojun 			}
5479df02875Smycroft 			service = buf;
548adeed07fSrillig 			if (req.client->sin != NULL) {
5495fe5dff0Schristos 				sockaddr_snprintf(abuf, sizeof(abuf), "%a",
5506a9b5acdSchristos 				    req.client->sin);
551d4ca0466Sdholland 			} else {
552d4ca0466Sdholland 				strcpy(abuf, "(null)");
553d4ca0466Sdholland 			}
5549df02875Smycroft 		}
5559df02875Smycroft 		if (denied) {
556b860cb42Smycroft 			syslog(deny_severity,
5575fe5dff0Schristos 			    "refused connection from %.500s(%s), service %s (%s)",
5585fe5dff0Schristos 			    eval_client(&req), abuf, service, sep->se_proto);
5597c7f4379Smycroft 			goto reject;
5609df02875Smycroft 		}
5619df02875Smycroft 		if (lflag) {
5629df02875Smycroft 			syslog(allow_severity,
5635fe5dff0Schristos 			    "connection from %.500s(%s), service %s (%s)",
5645fe5dff0Schristos 			    eval_client(&req), abuf, service, sep->se_proto);
5659df02875Smycroft 		}
56651fa5e9bSmycroft 	}
5679df02875Smycroft #endif /* LIBWRAP */
568b860cb42Smycroft 
569adeed07fSrillig 	if (sep->se_bi != NULL) {
570e62b84baSdholland 		if (didfork) {
571adeed07fSrillig 			for (s = servtab; s != NULL; s = s->se_next)
5728b39f71dSchristos 				if (s->se_fd != -1 && s->se_fd != ctrl) {
573e62b84baSdholland 					close(s->se_fd);
5748b39f71dSchristos 					s->se_fd = -1;
5758b39f71dSchristos 				}
576e62b84baSdholland 		}
57761f28255Scgd 		(*sep->se_bi->bi_fn)(ctrl, sep);
578249eced8Smycroft 	} else {
57961f28255Scgd 		if ((pwd = getpwnam(sep->se_user)) == NULL) {
580b860cb42Smycroft 			syslog(LOG_ERR, "%s/%s: %s: No such user",
581b860cb42Smycroft 			    sep->se_service, sep->se_proto, sep->se_user);
5827c7f4379Smycroft 			goto reject;
58361f28255Scgd 		}
584adeed07fSrillig 		if (sep->se_group != NULL &&
58508ba41f4Spk 		    (grp = getgrnam(sep->se_group)) == NULL) {
586b860cb42Smycroft 			syslog(LOG_ERR, "%s/%s: %s: No such group",
587b860cb42Smycroft 			    sep->se_service, sep->se_proto, sep->se_group);
5887c7f4379Smycroft 			goto reject;
58908ba41f4Spk 		}
590adeed07fSrillig 		if (pwd->pw_uid != 0) {
591adeed07fSrillig 			if (sep->se_group != NULL)
59208ba41f4Spk 				pwd->pw_gid = grp->gr_gid;
593cb666b19Smycroft 			if (setgid(pwd->pw_gid) < 0) {
594cb666b19Smycroft 				syslog(LOG_ERR,
595b860cb42Smycroft 				 "%s/%s: can't set gid %d: %m", sep->se_service,
596cb666b19Smycroft 				    sep->se_proto, pwd->pw_gid);
597cb666b19Smycroft 				goto reject;
598cb666b19Smycroft 			}
599cb666b19Smycroft 			(void) initgroups(pwd->pw_name,
600cb666b19Smycroft 			    pwd->pw_gid);
601cb666b19Smycroft 			if (setuid(pwd->pw_uid) < 0) {
602cb666b19Smycroft 				syslog(LOG_ERR,
603b860cb42Smycroft 				 "%s/%s: can't set uid %d: %m", sep->se_service,
604cb666b19Smycroft 				    sep->se_proto, pwd->pw_uid);
605cb666b19Smycroft 				goto reject;
606cb666b19Smycroft 			}
607adeed07fSrillig 		} else if (sep->se_group != NULL) {
60808ba41f4Spk 			(void) setgid((gid_t)grp->gr_gid);
60961f28255Scgd 		}
61025573806Schristos 		DPRINTF("%d execl %s",
61108ba41f4Spk 		    getpid(), sep->se_server);
612b860cb42Smycroft 		/* Set our control descriptor to not close-on-exec... */
613b860cb42Smycroft 		if (fcntl(ctrl, F_SETFD, 0) < 0)
6148b39f71dSchristos 			syslog(LOG_ERR, "fcntl (%d, F_SETFD, 0): %m", ctrl);
615b860cb42Smycroft 		/* ...and dup it to stdin, stdout, and stderr. */
6167c7f4379Smycroft 		if (ctrl != 0) {
61708ba41f4Spk 			dup2(ctrl, 0);
61808ba41f4Spk 			close(ctrl);
6197c7f4379Smycroft 			ctrl = 0;
6207c7f4379Smycroft 		}
62108ba41f4Spk 		dup2(0, 1);
62208ba41f4Spk 		dup2(0, 2);
623b860cb42Smycroft 		if (rlim_ofile.rlim_cur != rlim_ofile_cur &&
624b860cb42Smycroft 		    setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
625b860cb42Smycroft 			syslog(LOG_ERR, "setrlimit: %m");
62661f28255Scgd 		execv(sep->se_server, sep->se_argv);
627b860cb42Smycroft 		syslog(LOG_ERR, "cannot execute %s: %m", sep->se_server);
6287c7f4379Smycroft 	reject:
6297c7f4379Smycroft 		if (sep->se_socktype != SOCK_STREAM)
6307c7f4379Smycroft 			recv(ctrl, buf, sizeof (buf), 0);
63125573806Schristos 		_exit(EXIT_FAILURE);
63261f28255Scgd 	}
63361f28255Scgd }
63461f28255Scgd 
635726769adStron static void
reapchild(void)636726769adStron reapchild(void)
63761f28255Scgd {
63861f28255Scgd 	int status;
639cb666b19Smycroft 	pid_t pid;
640cb666b19Smycroft 	struct servtab *sep;
64161f28255Scgd 
64261f28255Scgd 	for (;;) {
6438be71949Stron 		pid = wait3(&status, WNOHANG, NULL);
64461f28255Scgd 		if (pid <= 0)
64561f28255Scgd 			break;
64625573806Schristos 		DPRINTF("%d reaped, status %#x", pid, status);
6478be71949Stron 		for (sep = servtab; sep != NULL; sep = sep->se_next)
64861f28255Scgd 			if (sep->se_wait == pid) {
649726769adStron 				struct kevent	*ev;
6508be71949Stron 
65108ba41f4Spk 				if (WIFEXITED(status) && WEXITSTATUS(status))
65261f28255Scgd 					syslog(LOG_WARNING,
653fa8ee723Skhorben 					    "%s: exit status %u",
65408ba41f4Spk 					    sep->se_server, WEXITSTATUS(status));
65508ba41f4Spk 				else if (WIFSIGNALED(status))
65608ba41f4Spk 					syslog(LOG_WARNING,
657fa8ee723Skhorben 					    "%s: exit signal %u",
65808ba41f4Spk 					    sep->se_server, WTERMSIG(status));
65908ba41f4Spk 				sep->se_wait = 1;
660726769adStron 				ev = allocchange();
661726769adStron 				EV_SET(ev, sep->se_fd, EVFILT_READ,
6628be71949Stron 				    EV_ADD | EV_ENABLE, 0, 0, (intptr_t)sep);
66325573806Schristos 				DPRINTF("restored %s, fd %d",
66461f28255Scgd 				    sep->se_service, sep->se_fd);
66561f28255Scgd 			}
66661f28255Scgd 	}
66761f28255Scgd }
66861f28255Scgd 
669726769adStron static void
retry(void)670726769adStron retry(void)
67161f28255Scgd {
672cb666b19Smycroft 	struct servtab *sep;
67361f28255Scgd 
674adeed07fSrillig 	timingout = false;
6758be71949Stron 	for (sep = servtab; sep != NULL; sep = sep->se_next) {
676cb666b19Smycroft 		if (sep->se_fd == -1 && !ISMUX(sep)) {
67708ba41f4Spk 			switch (sep->se_family) {
678786b86d7Slukem 			case AF_LOCAL:
67908ba41f4Spk 			case AF_INET:
6804b061adfSitojun #ifdef INET6
681a77871b8Sitojun 			case AF_INET6:
6824b061adfSitojun #endif
68361f28255Scgd 				setup(sep);
6848b39f71dSchristos 				if (sep->se_fd >= 0 && isrpcservice(sep))
68508ba41f4Spk 					register_rpc(sep);
68608ba41f4Spk 				break;
68761f28255Scgd 			}
68808ba41f4Spk 		}
68908ba41f4Spk 	}
69008ba41f4Spk }
69108ba41f4Spk 
692726769adStron static void
goaway(void)693726769adStron goaway(void)
69408ba41f4Spk {
6952d06dcebSmrg 	struct servtab *sep;
69608ba41f4Spk 
6978be71949Stron 	for (sep = servtab; sep != NULL; sep = sep->se_next) {
69808ba41f4Spk 		if (sep->se_fd == -1)
69908ba41f4Spk 			continue;
70008ba41f4Spk 
70108ba41f4Spk 		switch (sep->se_family) {
702786b86d7Slukem 		case AF_LOCAL:
70308ba41f4Spk 			(void)unlink(sep->se_service);
70408ba41f4Spk 			break;
70508ba41f4Spk 		case AF_INET:
7064b061adfSitojun #ifdef INET6
707a77871b8Sitojun 		case AF_INET6:
7084b061adfSitojun #endif
70908ba41f4Spk 			if (sep->se_wait == 1 && isrpcservice(sep))
71008ba41f4Spk 				unregister_rpc(sep);
71108ba41f4Spk 			break;
71208ba41f4Spk 		}
71308ba41f4Spk 		(void)close(sep->se_fd);
7148b39f71dSchristos 		sep->se_fd = -1;
71508ba41f4Spk 	}
716b19025f3Schristos 
717b19025f3Schristos 	DPRINTF("Going away.");
718b19025f3Schristos 
719b19025f3Schristos 	exit(EXIT_SUCCESS);
72008ba41f4Spk }
72108ba41f4Spk 
722b19025f3Schristos void
setup(struct servtab * sep)723726769adStron setup(struct servtab *sep)
72461f28255Scgd {
7256b2734d1Spk 	int		on = 1;
7266b2734d1Spk #ifdef INET6
7276b2734d1Spk 	int		off = 0;
7286b2734d1Spk #endif
729726769adStron 	struct kevent	*ev;
73061f28255Scgd 
73108ba41f4Spk 	if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
73225573806Schristos 		DPRINTF("socket failed on " SERV_FMT ": %s",
73325573806Schristos 		    SERV_PARAMS(sep), strerror(errno));
73461f28255Scgd 		syslog(LOG_ERR, "%s/%s: socket: %m",
73561f28255Scgd 		    sep->se_service, sep->se_proto);
73661f28255Scgd 		return;
73761f28255Scgd 	}
738e37d13ecSmycroft 	/* Set all listening sockets to close-on-exec. */
739176cc386Smycroft 	if (fcntl(sep->se_fd, F_SETFD, FD_CLOEXEC) < 0) {
74025573806Schristos 		syslog(LOG_ERR, SERV_FMT ": fcntl(F_SETFD, FD_CLOEXEC): %m",
74125573806Schristos 		    SERV_PARAMS(sep));
742176cc386Smycroft 		close(sep->se_fd);
7438b39f71dSchristos 		sep->se_fd = -1;
744176cc386Smycroft 		return;
745176cc386Smycroft 	}
746723fb3ccSthorpej 
74761f28255Scgd #define	turnon(fd, opt) \
7488b39f71dSchristos setsockopt(fd, SOL_SOCKET, opt, &on, (socklen_t)sizeof(on))
74961f28255Scgd 	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
75061f28255Scgd 	    turnon(sep->se_fd, SO_DEBUG) < 0)
75161f28255Scgd 		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
75261f28255Scgd 	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
75361f28255Scgd 		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
75461f28255Scgd #undef turnon
755723fb3ccSthorpej 
756723fb3ccSthorpej 	/* Set the socket buffer sizes, if specified. */
757723fb3ccSthorpej 	if (sep->se_sndbuf != 0 && setsockopt(sep->se_fd, SOL_SOCKET,
7588b39f71dSchristos 	    SO_SNDBUF, &sep->se_sndbuf, (socklen_t)sizeof(sep->se_sndbuf)) < 0)
759723fb3ccSthorpej 		syslog(LOG_ERR, "setsockopt (SO_SNDBUF %d): %m",
760723fb3ccSthorpej 		    sep->se_sndbuf);
761723fb3ccSthorpej 	if (sep->se_rcvbuf != 0 && setsockopt(sep->se_fd, SOL_SOCKET,
7628b39f71dSchristos 	    SO_RCVBUF, &sep->se_rcvbuf, (socklen_t)sizeof(sep->se_rcvbuf)) < 0)
763723fb3ccSthorpej 		syslog(LOG_ERR, "setsockopt (SO_RCVBUF %d): %m",
764723fb3ccSthorpej 		    sep->se_rcvbuf);
765906a2ba4Sitojun #ifdef INET6
766906a2ba4Sitojun 	if (sep->se_family == AF_INET6) {
767906a2ba4Sitojun 		int *v;
768906a2ba4Sitojun 		v = (sep->se_type == FAITH_TYPE) ? &on : &off;
769906a2ba4Sitojun 		if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH,
7708b39f71dSchristos 		    v, (socklen_t)sizeof(*v)) < 0)
771798ee686Sitojun 			syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m");
772906a2ba4Sitojun 	}
773906a2ba4Sitojun #endif
774a77871b8Sitojun #ifdef IPSEC
77537c5cac0Sozaki-r 	/* Avoid setting a policy if a policy specifier doesn't exist. */
77637c5cac0Sozaki-r 	if (sep->se_policy != NULL) {
77737c5cac0Sozaki-r 		int e = ipsecsetup(sep->se_family, sep->se_fd, sep->se_policy);
77837c5cac0Sozaki-r 		if (e < 0) {
77925573806Schristos 			syslog(LOG_ERR, SERV_FMT ": ipsec setup failed",
78025573806Schristos 			    SERV_PARAMS(sep));
7814b061adfSitojun 			(void)close(sep->se_fd);
7824b061adfSitojun 			sep->se_fd = -1;
78393de5675Sitojun 			return;
78493de5675Sitojun 		}
78537c5cac0Sozaki-r 	}
786a77871b8Sitojun #endif
787723fb3ccSthorpej 
78825573806Schristos 	if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) {
78925573806Schristos 		DPRINTF(SERV_FMT ": bind failed: %s",
79025573806Schristos 			SERV_PARAMS(sep), strerror(errno));
79125573806Schristos 		syslog(LOG_ERR, SERV_FMT ": bind: %m",
79225573806Schristos 		    SERV_PARAMS(sep));
79361f28255Scgd 		(void) close(sep->se_fd);
79461f28255Scgd 		sep->se_fd = -1;
79561f28255Scgd 		if (!timingout) {
796adeed07fSrillig 			timingout = true;
79761f28255Scgd 			alarm(RETRYTIME);
79861f28255Scgd 		}
79961f28255Scgd 		return;
80061f28255Scgd 	}
80161f28255Scgd 	if (sep->se_socktype == SOCK_STREAM)
80261f28255Scgd 		listen(sep->se_fd, 10);
80308ba41f4Spk 
804223f9ca4Sryo 	/* for internal dgram, setsockopt() is required for recvfromto() */
805223f9ca4Sryo 	if (sep->se_socktype == SOCK_DGRAM && sep->se_bi != NULL) {
806223f9ca4Sryo 		switch (sep->se_family) {
807223f9ca4Sryo 		case AF_INET:
808223f9ca4Sryo 			if (setsockopt(sep->se_fd, IPPROTO_IP,
809223f9ca4Sryo 			    IP_RECVDSTADDR, &on, sizeof(on)) < 0)
810223f9ca4Sryo 				syslog(LOG_ERR,
811223f9ca4Sryo 				    "setsockopt (IP_RECVDSTADDR): %m");
812223f9ca4Sryo 			break;
813223f9ca4Sryo #ifdef INET6
814223f9ca4Sryo 		case AF_INET6:
815223f9ca4Sryo 			if (setsockopt(sep->se_fd, IPPROTO_IPV6,
816223f9ca4Sryo 			    IPV6_RECVPKTINFO, &on, sizeof(on)) < 0)
817223f9ca4Sryo 				syslog(LOG_ERR,
818223f9ca4Sryo 				    "setsockopt (IPV6_RECVPKTINFO): %m");
819223f9ca4Sryo 			break;
820223f9ca4Sryo #endif
821223f9ca4Sryo 		}
822223f9ca4Sryo 	}
823223f9ca4Sryo 
824717f903aStls 	/* Set the accept filter, if specified. To be done after listen.*/
825717f903aStls 	if (sep->se_accf.af_name[0] != 0 && setsockopt(sep->se_fd, SOL_SOCKET,
8268b39f71dSchristos 	    SO_ACCEPTFILTER, &sep->se_accf,
8278b39f71dSchristos 	    (socklen_t)sizeof(sep->se_accf)) < 0)
828717f903aStls 		syslog(LOG_ERR, "setsockopt(SO_ACCEPTFILTER %s): %m",
829717f903aStls 		    sep->se_accf.af_name);
830717f903aStls 
831726769adStron 	ev = allocchange();
832726769adStron 	EV_SET(ev, sep->se_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0,
8338be71949Stron 	    (intptr_t)sep);
83408ba41f4Spk 	if (sep->se_fd > maxsock) {
83561f28255Scgd 		maxsock = sep->se_fd;
8368d37bc29Slukem 		if (maxsock > (int)(rlim_ofile_cur - FD_MARGIN))
83708ba41f4Spk 			bump_nofile();
83861f28255Scgd 	}
83925573806Schristos 	DPRINTF(SERV_FMT ": registered on fd %d", SERV_PARAMS(sep), sep->se_fd);
840cb666b19Smycroft }
841cb666b19Smycroft 
842cb666b19Smycroft /*
843cb666b19Smycroft  * Finish with a service and its socket.
844cb666b19Smycroft  */
845b19025f3Schristos void
close_sep(struct servtab * sep)846726769adStron close_sep(struct servtab *sep)
847cb666b19Smycroft {
84825573806Schristos 
849cb666b19Smycroft 	if (sep->se_fd >= 0) {
850cb666b19Smycroft 		(void) close(sep->se_fd);
851cb666b19Smycroft 		sep->se_fd = -1;
852cb666b19Smycroft 	}
853cb666b19Smycroft 	sep->se_count = 0;
85425573806Schristos 	if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) {
855b19025f3Schristos 		rl_clear_ip_list(sep);
85625573806Schristos 	}
85708ba41f4Spk }
85808ba41f4Spk 
859b19025f3Schristos void
register_rpc(struct servtab * sep)860726769adStron register_rpc(struct servtab *sep)
86108ba41f4Spk {
86208ba41f4Spk #ifdef RPC
8632db4d2fdSfvdl 	struct netbuf nbuf;
864a77871b8Sitojun 	struct sockaddr_storage ss;
8652db4d2fdSfvdl 	struct netconfig *nconf;
8667eb2c18bSrillig 	socklen_t socklen;
8677eb2c18bSrillig 	int n;
86808ba41f4Spk 
8692db4d2fdSfvdl 	if ((nconf = getnetconfigent(sep->se_proto+4)) == NULL) {
8702db4d2fdSfvdl 		syslog(LOG_ERR, "%s: getnetconfigent failed",
87108ba41f4Spk 		    sep->se_proto);
87208ba41f4Spk 		return;
87308ba41f4Spk 	}
8747eb2c18bSrillig 	socklen = sizeof ss;
8758b39f71dSchristos 	if (getsockname(sep->se_fd, (struct sockaddr *)(void *)&ss, &socklen) < 0) {
87625573806Schristos 		syslog(LOG_ERR, SERV_FMT ": getsockname: %m",
87725573806Schristos 		    SERV_PARAMS(sep));
87808ba41f4Spk 		return;
87908ba41f4Spk 	}
88008ba41f4Spk 
8812db4d2fdSfvdl 	nbuf.buf = &ss;
8822db4d2fdSfvdl 	nbuf.len = ss.ss_len;
8832db4d2fdSfvdl 	nbuf.maxlen = sizeof (struct sockaddr_storage);
88408ba41f4Spk 	for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
88525573806Schristos 		DPRINTF("rpcb_set: %u %d %s %s",
8862db4d2fdSfvdl 		    sep->se_rpcprog, n, nconf->nc_netid,
8872db4d2fdSfvdl 		    taddr2uaddr(nconf, &nbuf));
8888b39f71dSchristos 		(void)rpcb_unset((unsigned int)sep->se_rpcprog, (unsigned int)n, nconf);
889adeed07fSrillig 		if (rpcb_set((unsigned int)sep->se_rpcprog, (unsigned int)n, nconf, &nbuf) == 0)
8902db4d2fdSfvdl 			syslog(LOG_ERR, "rpcb_set: %u %d %s %s%s",
8912db4d2fdSfvdl 			    sep->se_rpcprog, n, nconf->nc_netid,
8922db4d2fdSfvdl 			    taddr2uaddr(nconf, &nbuf), clnt_spcreateerror(""));
89308ba41f4Spk 	}
89408ba41f4Spk #endif /* RPC */
89508ba41f4Spk }
89608ba41f4Spk 
897b19025f3Schristos void
unregister_rpc(struct servtab * sep)898726769adStron unregister_rpc(struct servtab *sep)
89908ba41f4Spk {
90008ba41f4Spk #ifdef RPC
90108ba41f4Spk 	int n;
9022db4d2fdSfvdl 	struct netconfig *nconf;
9032db4d2fdSfvdl 
9042db4d2fdSfvdl 	if ((nconf = getnetconfigent(sep->se_proto+4)) == NULL) {
9052db4d2fdSfvdl 		syslog(LOG_ERR, "%s: getnetconfigent failed",
9062db4d2fdSfvdl 		    sep->se_proto);
9072db4d2fdSfvdl 		return;
9082db4d2fdSfvdl 	}
90908ba41f4Spk 
91008ba41f4Spk 	for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
91125573806Schristos 		DPRINTF("rpcb_unset(%u, %d, %s)",
9122db4d2fdSfvdl 		    sep->se_rpcprog, n, nconf->nc_netid);
913adeed07fSrillig 		if (rpcb_unset((unsigned int)sep->se_rpcprog, (unsigned int)n, nconf) == 0)
9142db4d2fdSfvdl 			syslog(LOG_ERR, "rpcb_unset(%u, %d, %s) failed\n",
9152db4d2fdSfvdl 			    sep->se_rpcprog, n, nconf->nc_netid);
91608ba41f4Spk 	}
91708ba41f4Spk #endif /* RPC */
91808ba41f4Spk }
91908ba41f4Spk 
920726769adStron static void
inetd_setproctitle(char * a,int s)921726769adStron inetd_setproctitle(char *a, int s)
92261f28255Scgd {
9231d1ced82Schristos 	socklen_t size;
924a77871b8Sitojun 	struct sockaddr_storage ss;
9258b39f71dSchristos 	char hbuf[NI_MAXHOST];
9268b39f71dSchristos 	const char *hp;
9278b39f71dSchristos 	struct sockaddr *sa;
92861f28255Scgd 
929a77871b8Sitojun 	size = sizeof(ss);
9308b39f71dSchristos 	sa = (struct sockaddr *)(void *)&ss;
9318b39f71dSchristos 	if (getpeername(s, sa, &size) == 0) {
9328b39f71dSchristos 		if (getnameinfo(sa, size, hbuf, (socklen_t)sizeof(hbuf), NULL,
9338b39f71dSchristos 		    0, niflags) != 0)
934a0d7588fSchristos 			hp = "?";
9358b39f71dSchristos 		else
9368b39f71dSchristos 			hp = hbuf;
937a0d7588fSchristos 		setproctitle("-%s [%s]", a, hp);
938a77871b8Sitojun 	} else
9391d1ced82Schristos 		setproctitle("-%s", a);
94061f28255Scgd }
94161f28255Scgd 
942726769adStron static void
bump_nofile(void)943726769adStron bump_nofile(void)
94408ba41f4Spk {
94508ba41f4Spk #define FD_CHUNK	32
94608ba41f4Spk 	struct rlimit rl;
94708ba41f4Spk 
948e0738ce1Spk 	if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
94908ba41f4Spk 		syslog(LOG_ERR, "getrlimit: %m");
9502d06dcebSmrg 		return;
95108ba41f4Spk 	}
95208ba41f4Spk 	rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
95308ba41f4Spk 	if (rl.rlim_cur <= rlim_ofile_cur) {
95408ba41f4Spk 		syslog(LOG_ERR,
95508ba41f4Spk 		    "bump_nofile: cannot extend file limit, max = %d",
9562d06dcebSmrg 		    (int)rl.rlim_cur);
9572d06dcebSmrg 		return;
95808ba41f4Spk 	}
95908ba41f4Spk 
960e0738ce1Spk 	if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
96108ba41f4Spk 		syslog(LOG_ERR, "setrlimit: %m");
9622d06dcebSmrg 		return;
96308ba41f4Spk 	}
96408ba41f4Spk 
96508ba41f4Spk 	rlim_ofile_cur = rl.rlim_cur;
9662d06dcebSmrg 	return;
96708ba41f4Spk }
96808ba41f4Spk 
96961f28255Scgd /*
970223f9ca4Sryo  * In order to get the destination address (`to') with recvfromto(),
971223f9ca4Sryo  * IP_RECVDSTADDR or IP_RECVPKTINFO for AF_INET, or IPV6_RECVPKTINFO
972223f9ca4Sryo  * for AF_INET6, must be enabled with setsockopt(2).
973223f9ca4Sryo  *
974223f9ca4Sryo  * .sin_port and .sin6_port in 'to' are always stored as zero.
975223f9ca4Sryo  * If necessary, extract them using getsockname(2).
976223f9ca4Sryo  */
977223f9ca4Sryo static ssize_t
recvfromto(int s,void * restrict buf,size_t len,int flags,struct sockaddr * restrict from,socklen_t * restrict fromlen,struct sockaddr * restrict to,socklen_t * restrict tolen)978223f9ca4Sryo recvfromto(int s, void * restrict buf, size_t len, int flags,
979223f9ca4Sryo     struct sockaddr * restrict from, socklen_t * restrict fromlen,
980223f9ca4Sryo     struct sockaddr * restrict to, socklen_t * restrict tolen)
981223f9ca4Sryo {
982223f9ca4Sryo 	struct msghdr msg;
983223f9ca4Sryo 	struct iovec vec;
984223f9ca4Sryo 	struct cmsghdr *cmsg;
985223f9ca4Sryo 	struct sockaddr_storage ss;
986223f9ca4Sryo 	char cmsgbuf[1024];
987223f9ca4Sryo 	ssize_t rc;
988223f9ca4Sryo 
989223f9ca4Sryo 	if (to == NULL)
990223f9ca4Sryo 		return recvfrom(s, buf, len, flags, from, fromlen);
991223f9ca4Sryo 
992223f9ca4Sryo 	if (tolen == NULL || fromlen == NULL) {
993223f9ca4Sryo 		errno = EFAULT;
994223f9ca4Sryo 		return -1;
995223f9ca4Sryo 	}
996223f9ca4Sryo 
997223f9ca4Sryo 	vec.iov_base = buf;
998223f9ca4Sryo 	vec.iov_len = len;
999223f9ca4Sryo 	msg.msg_name = from;
1000223f9ca4Sryo 	msg.msg_namelen = *fromlen;
1001223f9ca4Sryo 	msg.msg_iov = &vec;
1002223f9ca4Sryo 	msg.msg_iovlen = 1;
1003223f9ca4Sryo 	msg.msg_control = cmsgbuf;
1004223f9ca4Sryo 	msg.msg_controllen = sizeof(cmsgbuf);
1005223f9ca4Sryo 
1006223f9ca4Sryo 	rc = recvmsg(s, &msg, flags);
1007223f9ca4Sryo 	if (rc < 0)
1008223f9ca4Sryo 		return rc;
1009223f9ca4Sryo 	*fromlen = msg.msg_namelen;
1010223f9ca4Sryo 
1011223f9ca4Sryo 	memset(&ss, 0, sizeof(ss));
1012223f9ca4Sryo 	for (cmsg = (struct cmsghdr *)CMSG_FIRSTHDR(&msg); cmsg != NULL;
1013223f9ca4Sryo 	    cmsg = (struct cmsghdr *)CMSG_NXTHDR(&msg, cmsg)) {
1014223f9ca4Sryo 		if (cmsg->cmsg_level == IPPROTO_IP &&
1015223f9ca4Sryo 		    cmsg->cmsg_type == IP_RECVDSTADDR) {
1016223f9ca4Sryo 			struct in_addr *dst = (struct in_addr *)CMSG_DATA(cmsg);
1017223f9ca4Sryo 			struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
1018223f9ca4Sryo 
1019223f9ca4Sryo 			sin->sin_len = sizeof(*sin);
1020223f9ca4Sryo 			sin->sin_family = AF_INET;
1021223f9ca4Sryo 			sin->sin_addr = *dst;
1022223f9ca4Sryo 			break;
1023223f9ca4Sryo 		}
1024223f9ca4Sryo 		if (cmsg->cmsg_level == IPPROTO_IP &&
1025223f9ca4Sryo 		    cmsg->cmsg_type == IP_PKTINFO) {
1026223f9ca4Sryo 			struct in_pktinfo *pi =
1027223f9ca4Sryo 			    (struct in_pktinfo *)CMSG_DATA(cmsg);
1028223f9ca4Sryo 			struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
1029223f9ca4Sryo 
1030223f9ca4Sryo 			sin->sin_len = sizeof(*sin);
1031223f9ca4Sryo 			sin->sin_family = AF_INET;
1032223f9ca4Sryo 			sin->sin_addr = pi->ipi_addr;
1033223f9ca4Sryo 			break;
1034223f9ca4Sryo 		}
1035223f9ca4Sryo #ifdef INET6
1036223f9ca4Sryo 		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
1037223f9ca4Sryo 		    cmsg->cmsg_type == IPV6_PKTINFO) {
1038223f9ca4Sryo 			struct in6_pktinfo *pi6 =
1039223f9ca4Sryo 			    (struct in6_pktinfo *)CMSG_DATA(cmsg);
1040223f9ca4Sryo 			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
1041223f9ca4Sryo 
1042223f9ca4Sryo 			sin6->sin6_len = sizeof(*sin6);
1043223f9ca4Sryo 			sin6->sin6_family = AF_INET6;
1044223f9ca4Sryo 			sin6->sin6_addr = pi6->ipi6_addr;
1045223f9ca4Sryo 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
1046223f9ca4Sryo 			    IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr))
1047223f9ca4Sryo 				sin6->sin6_scope_id = pi6->ipi6_ifindex;
1048223f9ca4Sryo 			else
1049223f9ca4Sryo 				sin6->sin6_scope_id = 0;
1050223f9ca4Sryo 			break;
1051223f9ca4Sryo 		}
1052223f9ca4Sryo #endif /* INET6 */
1053223f9ca4Sryo 	}
1054223f9ca4Sryo 
1055223f9ca4Sryo 	socklen_t sslen = (*tolen < ss.ss_len) ? *tolen : ss.ss_len;
1056223f9ca4Sryo 	if (sslen > 0)
1057223f9ca4Sryo 		memcpy(to, &ss, sslen);
1058223f9ca4Sryo 	*tolen = sslen;
1059223f9ca4Sryo 
1060223f9ca4Sryo 	return rc;
1061223f9ca4Sryo }
1062223f9ca4Sryo 
1063223f9ca4Sryo /*
1064223f9ca4Sryo  * When sending, the source port is selected as the one bind(2)'ed
1065223f9ca4Sryo  * to the socket.
1066223f9ca4Sryo  * .sin_port and .sin6_port in `from' are always ignored.
1067223f9ca4Sryo  */
1068223f9ca4Sryo static ssize_t
sendfromto(int s,const void * buf,size_t len,int flags,const struct sockaddr * from,socklen_t fromlen,const struct sockaddr * to,socklen_t tolen)1069223f9ca4Sryo sendfromto(int s, const void *buf, size_t len, int flags,
1070223f9ca4Sryo     const struct sockaddr *from, socklen_t fromlen,
1071223f9ca4Sryo     const struct sockaddr *to, socklen_t tolen)
1072223f9ca4Sryo {
1073223f9ca4Sryo 	struct msghdr msg;
1074223f9ca4Sryo 	struct iovec vec;
1075223f9ca4Sryo 	struct cmsghdr *cmsg;
1076223f9ca4Sryo 	char cmsgbuf[256];
1077223f9ca4Sryo 	__CTASSERT(sizeof(cmsgbuf) > CMSG_SPACE(sizeof(struct in_pktinfo)));
1078223f9ca4Sryo #ifdef INET6
1079223f9ca4Sryo 	__CTASSERT(sizeof(cmsgbuf) > CMSG_SPACE(sizeof(struct in6_pktinfo)));
1080223f9ca4Sryo #endif
1081223f9ca4Sryo 
1082223f9ca4Sryo 	if (from == NULL || fromlen == 0)
1083223f9ca4Sryo 		return sendto(s, buf, len, flags, to, tolen);
1084223f9ca4Sryo 
1085223f9ca4Sryo 	vec.iov_base = __UNCONST(buf);
1086223f9ca4Sryo 	vec.iov_len = len;
1087223f9ca4Sryo 	msg.msg_name = __UNCONST(to);
1088223f9ca4Sryo 	msg.msg_namelen = tolen;
1089223f9ca4Sryo 	msg.msg_iov = &vec;
1090223f9ca4Sryo 	msg.msg_iovlen = 1;
1091223f9ca4Sryo 	msg.msg_control = cmsgbuf;
1092223f9ca4Sryo 	msg.msg_controllen = 0;
1093223f9ca4Sryo 
1094223f9ca4Sryo 	if (fromlen < 2) {	/* sa_len + sa_family */
1095223f9ca4Sryo 		errno = EINVAL;
1096223f9ca4Sryo 		return -1;
1097223f9ca4Sryo 	}
1098223f9ca4Sryo 
1099223f9ca4Sryo 	cmsg = (struct cmsghdr *)cmsgbuf;
1100223f9ca4Sryo 	if (from->sa_family == AF_INET) {
1101223f9ca4Sryo 		const struct sockaddr_in *from4 =
1102223f9ca4Sryo 		    (const struct sockaddr_in *)from;
1103223f9ca4Sryo 		struct in_pktinfo *pi;
1104223f9ca4Sryo 
1105223f9ca4Sryo 		if (fromlen != sizeof(struct sockaddr_in) ||
1106223f9ca4Sryo 		    from4->sin_family != AF_INET) {
1107223f9ca4Sryo 			errno = EINVAL;
1108223f9ca4Sryo 			return -1;
1109223f9ca4Sryo 		}
1110223f9ca4Sryo 
1111223f9ca4Sryo 		msg.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo));
1112223f9ca4Sryo 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
1113223f9ca4Sryo 		cmsg->cmsg_level = IPPROTO_IP;
1114223f9ca4Sryo 		cmsg->cmsg_type = IP_PKTINFO;
1115223f9ca4Sryo 
1116223f9ca4Sryo 		pi = (struct in_pktinfo *)CMSG_DATA(cmsg);
1117223f9ca4Sryo 		pi->ipi_addr = from4->sin_addr;
1118223f9ca4Sryo 		pi->ipi_ifindex = 0;
1119223f9ca4Sryo #ifdef INET6
1120223f9ca4Sryo 	} else if (from->sa_family == AF_INET6) {
1121223f9ca4Sryo 		const struct sockaddr_in6 *from6 =
1122223f9ca4Sryo 		    (const struct sockaddr_in6 *)from;
1123223f9ca4Sryo 		struct in6_pktinfo *pi6;
1124223f9ca4Sryo 
1125223f9ca4Sryo 		if (fromlen != sizeof(struct sockaddr_in6) ||
1126223f9ca4Sryo 		    from6->sin6_family != AF_INET6) {
1127223f9ca4Sryo 			errno = EINVAL;
1128223f9ca4Sryo 			return -1;
1129223f9ca4Sryo 		}
1130223f9ca4Sryo 
1131223f9ca4Sryo 		msg.msg_controllen += CMSG_SPACE(sizeof(struct in6_pktinfo));
1132223f9ca4Sryo 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
1133223f9ca4Sryo 		cmsg->cmsg_level = IPPROTO_IPV6;
1134223f9ca4Sryo 		cmsg->cmsg_type = IPV6_PKTINFO;
1135223f9ca4Sryo 
1136223f9ca4Sryo 		pi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
1137223f9ca4Sryo 		pi6->ipi6_addr = from6->sin6_addr;
1138223f9ca4Sryo 		if (IN6_IS_ADDR_LINKLOCAL(&from6->sin6_addr) ||
1139223f9ca4Sryo 		    IN6_IS_ADDR_MC_LINKLOCAL(&from6->sin6_addr)) {
1140223f9ca4Sryo 			pi6->ipi6_ifindex = from6->sin6_scope_id;
1141223f9ca4Sryo 		} else {
1142223f9ca4Sryo 			pi6->ipi6_ifindex = 0;
1143223f9ca4Sryo 		}
1144223f9ca4Sryo #endif /* INET6 */
1145223f9ca4Sryo 	} else {
1146223f9ca4Sryo 		return sendto(s, buf, len, flags, to, tolen);
1147223f9ca4Sryo 	}
1148223f9ca4Sryo 
1149223f9ca4Sryo 	return sendmsg(s, &msg, flags);
1150223f9ca4Sryo }
1151223f9ca4Sryo 
1152223f9ca4Sryo /*
115361f28255Scgd  * Internet services provided internally by inetd:
115461f28255Scgd  */
115508ba41f4Spk #define	BUFSIZE	4096
115661f28255Scgd 
115761f28255Scgd /* ARGSUSED */
1158726769adStron static void
echo_stream(int s,struct servtab * sep)1159726769adStron echo_stream(int s, struct servtab *sep)	/* Echo service -- echo data back */
116061f28255Scgd {
116161f28255Scgd 	char buffer[BUFSIZE];
11628b39f71dSchristos 	ssize_t i;
116361f28255Scgd 
1164f6f602f9Scgd 	inetd_setproctitle(sep->se_service, s);
116561f28255Scgd 	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
11668b39f71dSchristos 	    write(s, buffer, (size_t)i) > 0)
1167b19025f3Schristos 		continue;
116861f28255Scgd }
116961f28255Scgd 
117061f28255Scgd /* ARGSUSED */
1171726769adStron static void
echo_dg(int s,struct servtab * sep)1172726769adStron echo_dg(int s, struct servtab *sep)	/* Echo service -- echo data back */
117361f28255Scgd {
117461f28255Scgd 	char buffer[BUFSIZE];
11758b39f71dSchristos 	ssize_t i;
1176223f9ca4Sryo 	socklen_t rsize, lsize;
1177223f9ca4Sryo 	struct sockaddr_storage remote, local;
1178223f9ca4Sryo 	struct sockaddr *lsa, *rsa;
117961f28255Scgd 
1180223f9ca4Sryo 	rsa = (struct sockaddr *)(void *)&remote;
1181223f9ca4Sryo 	lsa = (struct sockaddr *)(void *)&local;
1182223f9ca4Sryo 	rsize = sizeof(remote);
1183223f9ca4Sryo 	lsize = sizeof(local);
1184223f9ca4Sryo 	if ((i = recvfromto(s, buffer, sizeof(buffer), 0,
1185223f9ca4Sryo 	    rsa, &rsize, lsa, &lsize)) < 0)
118661f28255Scgd 		return;
1187223f9ca4Sryo 	if (port_good_dg(rsa))
1188223f9ca4Sryo 		(void) sendfromto(s, buffer, (size_t)i, 0,
1189223f9ca4Sryo 		    lsa, lsize, rsa, rsize);
119061f28255Scgd }
119161f28255Scgd 
119261f28255Scgd /* ARGSUSED */
1193726769adStron static void
discard_stream(int s,struct servtab * sep)1194726769adStron discard_stream(int s, struct servtab *sep) /* Discard service -- ignore data */
119561f28255Scgd {
119661f28255Scgd 	char buffer[BUFSIZE];
119761f28255Scgd 
1198f6f602f9Scgd 	inetd_setproctitle(sep->se_service, s);
119908ba41f4Spk 	while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
120008ba41f4Spk 			errno == EINTR)
120161f28255Scgd 		;
120261f28255Scgd }
120361f28255Scgd 
120461f28255Scgd /* ARGSUSED */
1205726769adStron static void
discard_dg(int s,struct servtab * sep)1206726769adStron discard_dg(int s, struct servtab *sep)	/* Discard service -- ignore data */
1207726769adStron 
120861f28255Scgd {
120961f28255Scgd 	char buffer[BUFSIZE];
121061f28255Scgd 
121161f28255Scgd 	(void) read(s, buffer, sizeof(buffer));
121261f28255Scgd }
121361f28255Scgd 
121461f28255Scgd #define LINESIZ 72
121561f28255Scgd char ring[128];
121661f28255Scgd char *endring;
121761f28255Scgd 
1218726769adStron static void
initring(void)1219726769adStron initring(void)
122061f28255Scgd {
1221cb666b19Smycroft 	int i;
122261f28255Scgd 
122361f28255Scgd 	endring = ring;
122461f28255Scgd 
122561f28255Scgd 	for (i = 0; i <= 128; ++i)
122661f28255Scgd 		if (isprint(i))
12275d133dbcSrillig 			*endring++ = (char)i;
122861f28255Scgd }
122961f28255Scgd 
123061f28255Scgd /* ARGSUSED */
1231726769adStron static void
chargen_stream(int s,struct servtab * sep)1232726769adStron chargen_stream(int s, struct servtab *sep)	/* Character generator */
123361f28255Scgd {
12348b39f71dSchristos 	size_t len;
1235cb666b19Smycroft 	char *rs, text[LINESIZ+2];
123661f28255Scgd 
1237f6f602f9Scgd 	inetd_setproctitle(sep->se_service, s);
123861f28255Scgd 
1239adeed07fSrillig 	if (endring == NULL) {
124061f28255Scgd 		initring();
124161f28255Scgd 		rs = ring;
124261f28255Scgd 	}
124361f28255Scgd 
124461f28255Scgd 	text[LINESIZ] = '\r';
124561f28255Scgd 	text[LINESIZ + 1] = '\n';
124661f28255Scgd 	for (rs = ring;;) {
12475d133dbcSrillig 		if ((len = (size_t)(endring - rs)) >= LINESIZ)
1248cb666b19Smycroft 			memmove(text, rs, LINESIZ);
124961f28255Scgd 		else {
1250cb666b19Smycroft 			memmove(text, rs, len);
1251cb666b19Smycroft 			memmove(text + len, ring, LINESIZ - len);
125261f28255Scgd 		}
125361f28255Scgd 		if (++rs == endring)
125461f28255Scgd 			rs = ring;
125561f28255Scgd 		if (write(s, text, sizeof(text)) != sizeof(text))
125661f28255Scgd 			break;
125761f28255Scgd 	}
125861f28255Scgd }
125961f28255Scgd 
126061f28255Scgd /* ARGSUSED */
1261726769adStron static void
chargen_dg(int s,struct servtab * sep)1262726769adStron chargen_dg(int s, struct servtab *sep)		/* Character generator */
126361f28255Scgd {
1264223f9ca4Sryo 	struct sockaddr_storage remote, local;
1265223f9ca4Sryo 	struct sockaddr *rsa, *lsa;
126661f28255Scgd 	static char *rs;
12678b39f71dSchristos 	size_t len;
1268223f9ca4Sryo 	socklen_t rsize, lsize;
126961f28255Scgd 	char text[LINESIZ+2];
127061f28255Scgd 
127161f28255Scgd 	if (endring == 0) {
127261f28255Scgd 		initring();
127361f28255Scgd 		rs = ring;
127461f28255Scgd 	}
127561f28255Scgd 
1276223f9ca4Sryo 	rsa = (struct sockaddr *)(void *)&remote;
1277223f9ca4Sryo 	lsa = (struct sockaddr *)(void *)&local;
1278223f9ca4Sryo 	rsize = sizeof(remote);
1279223f9ca4Sryo 	lsize = sizeof(local);
1280223f9ca4Sryo 	if (recvfromto(s, text, sizeof(text), 0,
1281223f9ca4Sryo 	    rsa, &rsize, lsa, &lsize) < 0)
128261f28255Scgd 		return;
128361f28255Scgd 
1284223f9ca4Sryo 	if (!port_good_dg(rsa))
1285f6aa0f50Shwr 		return;
1286f6aa0f50Shwr 
12875d133dbcSrillig 	if ((len = (size_t)(endring - rs)) >= LINESIZ)
1288cb666b19Smycroft 		memmove(text, rs, LINESIZ);
128961f28255Scgd 	else {
1290cb666b19Smycroft 		memmove(text, rs, len);
1291cb666b19Smycroft 		memmove(text + len, ring, LINESIZ - len);
129261f28255Scgd 	}
129361f28255Scgd 	if (++rs == endring)
129461f28255Scgd 		rs = ring;
129561f28255Scgd 	text[LINESIZ] = '\r';
129661f28255Scgd 	text[LINESIZ + 1] = '\n';
1297223f9ca4Sryo 	(void) sendfromto(s, text, sizeof(text), 0, lsa, lsize, rsa, rsize);
129861f28255Scgd }
129961f28255Scgd 
130061f28255Scgd /*
130161f28255Scgd  * Return a machine readable date and time, in the form of the
130261f28255Scgd  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
130361f28255Scgd  * returns the number of seconds since midnight, Jan 1, 1970,
130461f28255Scgd  * we must add 2208988800 seconds to this figure to make up for
130561f28255Scgd  * some seventy years Bell Labs was asleep.
130661f28255Scgd  */
130761f28255Scgd 
1308726769adStron static uint32_t
machtime(void)1309726769adStron machtime(void)
131061f28255Scgd {
131161f28255Scgd 	struct timeval tv;
131261f28255Scgd 
13138be71949Stron 	if (gettimeofday(&tv, NULL) < 0) {
131425573806Schristos 		DPRINTF("Unable to get time of day");
1315dd94d9b2Sabs 		return (0);
131661f28255Scgd 	}
1317dd94d9b2Sabs #define	OFFSET ((uint32_t)25567 * 24*60*60)
1318dd94d9b2Sabs 	return (htonl((uint32_t)(tv.tv_sec + OFFSET)));
1319cb666b19Smycroft #undef OFFSET
132061f28255Scgd }
132161f28255Scgd 
132261f28255Scgd /* ARGSUSED */
1323726769adStron static void
machtime_stream(int s,struct servtab * sep)1324726769adStron machtime_stream(int s, struct servtab *sep)
132561f28255Scgd {
1326dd94d9b2Sabs 	uint32_t result;
132761f28255Scgd 
132861f28255Scgd 	result = machtime();
13298b39f71dSchristos 	(void) write(s, &result, sizeof(result));
133061f28255Scgd }
133161f28255Scgd 
133261f28255Scgd /* ARGSUSED */
1333cb666b19Smycroft void
machtime_dg(int s,struct servtab * sep)1334726769adStron machtime_dg(int s, struct servtab *sep)
133561f28255Scgd {
1336dd94d9b2Sabs 	uint32_t result;
1337223f9ca4Sryo 	struct sockaddr_storage remote, local;
1338223f9ca4Sryo 	struct sockaddr *rsa, *lsa;
1339223f9ca4Sryo 	socklen_t rsize, lsize;
134061f28255Scgd 
1341223f9ca4Sryo 	rsa = (struct sockaddr *)(void *)&remote;
1342223f9ca4Sryo 	lsa = (struct sockaddr *)(void *)&local;
1343223f9ca4Sryo 	rsize = sizeof(remote);
1344223f9ca4Sryo 	lsize = sizeof(local);
1345223f9ca4Sryo 	if (recvfromto(s, &result, sizeof(result), 0,
1346223f9ca4Sryo 	    rsa, &rsize, lsa, &lsize) < 0)
134761f28255Scgd 		return;
1348223f9ca4Sryo 	if (!port_good_dg(rsa))
1349f6aa0f50Shwr 		return;
135061f28255Scgd 	result = machtime();
1351223f9ca4Sryo 	(void)sendfromto(s, &result, sizeof(result), 0, lsa, lsize, rsa, rsize);
135261f28255Scgd }
135361f28255Scgd 
135461f28255Scgd /* ARGSUSED */
1355726769adStron static void
daytime_stream(int s,struct servtab * sep)1356726769adStron daytime_stream(int s,struct servtab *sep)
1357726769adStron /* Return human-readable time of day */
135861f28255Scgd {
135961f28255Scgd 	char buffer[256];
13608b39f71dSchristos 	time_t clk;
13614e76afacSmrg 	int len;
136261f28255Scgd 
13638b39f71dSchristos 	clk = time((time_t *) 0);
136461f28255Scgd 
13658b39f71dSchristos 	len = snprintf(buffer, sizeof buffer, "%.24s\r\n", ctime(&clk));
13665d133dbcSrillig 	(void) write(s, buffer, (size_t)len);
136761f28255Scgd }
136861f28255Scgd 
136961f28255Scgd /* ARGSUSED */
1370cb666b19Smycroft void
daytime_dg(int s,struct servtab * sep)1371726769adStron daytime_dg(int s, struct servtab *sep)
1372726769adStron /* Return human-readable time of day */
137361f28255Scgd {
137461f28255Scgd 	char buffer[256];
13758b39f71dSchristos 	time_t clk;
1376223f9ca4Sryo 	struct sockaddr_storage remote, local;
1377223f9ca4Sryo 	struct sockaddr *rsa, *lsa;
1378223f9ca4Sryo 	socklen_t rsize, lsize;
137969c161beSitojun 	int len;
138061f28255Scgd 
13818b39f71dSchristos 	clk = time((time_t *) 0);
138261f28255Scgd 
1383223f9ca4Sryo 	rsa = (struct sockaddr *)(void *)&remote;
1384223f9ca4Sryo 	lsa = (struct sockaddr *)(void *)&local;
1385223f9ca4Sryo 	rsize = sizeof(remote);
1386223f9ca4Sryo 	lsize = sizeof(local);
1387223f9ca4Sryo 	if (recvfromto(s, buffer, sizeof(buffer), 0,
1388223f9ca4Sryo 	    rsa, &rsize, lsa, &lsize) < 0)
138961f28255Scgd 		return;
1390223f9ca4Sryo 	if (!port_good_dg(rsa))
1391f6aa0f50Shwr 		return;
13928b39f71dSchristos 	len = snprintf(buffer, sizeof buffer, "%.24s\r\n", ctime(&clk));
1393223f9ca4Sryo 	(void) sendfromto(s, buffer, (size_t)len, 0, lsa, lsize, rsa, rsize);
139461f28255Scgd }
139561f28255Scgd 
1396726769adStron static void
usage(void)1397726769adStron usage(void)
1398cb666b19Smycroft {
1399cb666b19Smycroft #ifdef LIBWRAP
140025bdbb66Scgd 	(void)fprintf(stderr, "usage: %s [-dl] [conf]\n", getprogname());
1401cb666b19Smycroft #else
140225bdbb66Scgd 	(void)fprintf(stderr, "usage: %s [-d] [conf]\n", getprogname());
1403cb666b19Smycroft #endif
140425573806Schristos 	exit(EXIT_FAILURE);
1405cb666b19Smycroft }
1406cb666b19Smycroft 
1407cb666b19Smycroft 
1408cb666b19Smycroft /*
1409cb666b19Smycroft  *  Based on TCPMUX.C by Mark K. Lottor November 1988
1410cb666b19Smycroft  *  sri-nic::ps:<mkl>tcpmux.c
1411cb666b19Smycroft  */
1412cb666b19Smycroft 
1413cb666b19Smycroft static int		/* # of characters upto \r,\n or \0 */
get_line(int fd,char * buf,int len)14147027866aSroy get_line(int fd,	char *buf, int len)
1415cb666b19Smycroft {
14168b39f71dSchristos 	int count = 0;
14178b39f71dSchristos 	ssize_t n;
1418cb666b19Smycroft 
1419cb666b19Smycroft 	do {
14205d133dbcSrillig 		n = read(fd, buf, (size_t)(len - count));
1421cb666b19Smycroft 		if (n == 0)
1422cb666b19Smycroft 			return (count);
1423cb666b19Smycroft 		if (n < 0)
1424cb666b19Smycroft 			return (-1);
1425cb666b19Smycroft 		while (--n >= 0) {
1426cb666b19Smycroft 			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
1427cb666b19Smycroft 				return (count);
1428cb666b19Smycroft 			count++;
1429cb666b19Smycroft 			buf++;
1430cb666b19Smycroft 		}
1431cb666b19Smycroft 	} while (count < len);
1432cb666b19Smycroft 	return (count);
1433cb666b19Smycroft }
1434cb666b19Smycroft 
1435cb666b19Smycroft #define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
1436cb666b19Smycroft 
1437cb666b19Smycroft #define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
1438cb666b19Smycroft 
1439726769adStron static void
tcpmux(int ctrl,struct servtab * sep)1440726769adStron tcpmux(int ctrl, struct servtab *sep)
1441b860cb42Smycroft {
1442cb666b19Smycroft 	char service[MAX_SERV_LEN+1];
1443cb666b19Smycroft 	int len;
1444cb666b19Smycroft 
1445cb666b19Smycroft 	/* Get requested service name */
14467027866aSroy 	if ((len = get_line(ctrl, service, MAX_SERV_LEN)) < 0) {
1447b860cb42Smycroft 		strwrite(ctrl, "-Error reading service name\r\n");
1448b860cb42Smycroft 		goto reject;
1449cb666b19Smycroft 	}
1450cb666b19Smycroft 	service[len] = '\0';
1451cb666b19Smycroft 
145225573806Schristos 	DPRINTF("tcpmux: %s: service requested", service);
1453cb666b19Smycroft 
1454cb666b19Smycroft 	/*
1455cb666b19Smycroft 	 * Help is a required command, and lists available services,
1456cb666b19Smycroft 	 * one per line.
1457cb666b19Smycroft 	 */
1458adeed07fSrillig 	if (strcasecmp(service, "help") == 0) {
14596ebf01d1Smycroft 		strwrite(ctrl, "+Available services:\r\n");
14606ebf01d1Smycroft 		strwrite(ctrl, "help\r\n");
14618be71949Stron 		for (sep = servtab; sep != NULL; sep = sep->se_next) {
1462cb666b19Smycroft 			if (!ISMUX(sep))
1463cb666b19Smycroft 				continue;
1464b860cb42Smycroft 			(void)write(ctrl, sep->se_service,
1465b860cb42Smycroft 			    strlen(sep->se_service));
1466b860cb42Smycroft 			strwrite(ctrl, "\r\n");
1467cb666b19Smycroft 		}
1468b860cb42Smycroft 		goto reject;
1469cb666b19Smycroft 	}
1470cb666b19Smycroft 
1471cb666b19Smycroft 	/* Try matching a service in inetd.conf with the request */
14728be71949Stron 	for (sep = servtab; sep != NULL; sep = sep->se_next) {
1473cb666b19Smycroft 		if (!ISMUX(sep))
1474cb666b19Smycroft 			continue;
1475adeed07fSrillig 		if (strcasecmp(service, sep->se_service) == 0) {
1476b860cb42Smycroft 			if (ISMUXPLUS(sep))
1477b860cb42Smycroft 				strwrite(ctrl, "+Go\r\n");
1478adeed07fSrillig 			run_service(ctrl, sep, true /* forked */);
1479b860cb42Smycroft 			return;
1480cb666b19Smycroft 		}
1481cb666b19Smycroft 	}
1482b860cb42Smycroft 	strwrite(ctrl, "-Service not available\r\n");
1483b860cb42Smycroft reject:
148425573806Schristos 	_exit(EXIT_FAILURE);
1485cb666b19Smycroft }
1486cb666b19Smycroft 
1487f6aa0f50Shwr /*
148851156effSitojun  * check if the address/port where send data to is one of the obvious ports
1489f6aa0f50Shwr  * that are used for denial of service attacks like two echo ports
1490f6aa0f50Shwr  * just echoing data between them
1491f6aa0f50Shwr  */
1492726769adStron static int
port_good_dg(struct sockaddr * sa)1493726769adStron port_good_dg(struct sockaddr *sa)
1494f6aa0f50Shwr {
149551156effSitojun 	struct in_addr in;
14968b39f71dSchristos 	struct sockaddr_in *sin;
149751156effSitojun #ifdef INET6
149851156effSitojun 	struct in6_addr *in6;
14998b39f71dSchristos 	struct sockaddr_in6 *sin6;
150051156effSitojun #endif
1501f6aa0f50Shwr 	u_int16_t port;
15028b39f71dSchristos 	int i;
150355ffb1ceSitojun 	char hbuf[NI_MAXHOST];
1504f6aa0f50Shwr 
1505f7c22e9eSitojun 	switch (sa->sa_family) {
1506f7c22e9eSitojun 	case AF_INET:
15078b39f71dSchristos 		sin = (struct sockaddr_in *)(void *)sa;
15088b39f71dSchristos 		in.s_addr = ntohl(sin->sin_addr.s_addr);
15098b39f71dSchristos 		port = ntohs(sin->sin_port);
15106b2734d1Spk #ifdef INET6
151151156effSitojun 	v4chk:
15126b2734d1Spk #endif
151351156effSitojun 		if (IN_MULTICAST(in.s_addr))
151451156effSitojun 			goto bad;
151551156effSitojun 		switch ((in.s_addr & 0xff000000) >> 24) {
151651156effSitojun 		case 0: case 127: case 255:
151751156effSitojun 			goto bad;
151851156effSitojun 		}
151900a0a652Sitojun 		if (dg_broadcast(&in))
152000a0a652Sitojun 			goto bad;
1521f7c22e9eSitojun 		break;
15224b061adfSitojun #ifdef INET6
1523f7c22e9eSitojun 	case AF_INET6:
15248b39f71dSchristos 		sin6 = (struct sockaddr_in6 *)(void *)sa;
15258b39f71dSchristos 		in6 = &sin6->sin6_addr;
15268b39f71dSchristos 		port = ntohs(sin6->sin6_port);
152751156effSitojun 		if (IN6_IS_ADDR_MULTICAST(in6) || IN6_IS_ADDR_UNSPECIFIED(in6))
152851156effSitojun 			goto bad;
152951156effSitojun 		if (IN6_IS_ADDR_V4MAPPED(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
153051156effSitojun 			memcpy(&in, &in6->s6_addr[12], sizeof(in));
153151156effSitojun 			in.s_addr = ntohl(in.s_addr);
153251156effSitojun 			goto v4chk;
153351156effSitojun 		}
1534f7c22e9eSitojun 		break;
15354b061adfSitojun #endif
1536f7c22e9eSitojun 	default:
1537f7c22e9eSitojun 		/* XXX unsupported af, is it safe to assume it to be safe? */
1538adeed07fSrillig 		return true;
1539f7c22e9eSitojun 	}
1540f6aa0f50Shwr 
154151156effSitojun 	for (i = 0; bad_ports[i] != 0; i++) {
154251156effSitojun 		if (port == bad_ports[i])
154351156effSitojun 			goto bad;
1544f6aa0f50Shwr 	}
1545f6aa0f50Shwr 
1546adeed07fSrillig 	return true;
154751156effSitojun 
154851156effSitojun bad:
15498b39f71dSchristos 	if (getnameinfo(sa, sa->sa_len, hbuf, (socklen_t)sizeof(hbuf), NULL, 0,
15500062113fSitojun 	    niflags) != 0)
15511d060048Sitojun 		strlcpy(hbuf, "?", sizeof(hbuf));
1552f6aa0f50Shwr 	syslog(LOG_WARNING,"Possible DoS attack from %s, Port %d",
1553f7c22e9eSitojun 		hbuf, port);
1554adeed07fSrillig 	return false;
1555f6aa0f50Shwr }
155600a0a652Sitojun 
155700a0a652Sitojun /* XXX need optimization */
1558726769adStron static int
dg_broadcast(struct in_addr * in)1559726769adStron dg_broadcast(struct in_addr *in)
156000a0a652Sitojun {
156100a0a652Sitojun 	struct ifaddrs *ifa, *ifap;
156200a0a652Sitojun 	struct sockaddr_in *sin;
156300a0a652Sitojun 
156400a0a652Sitojun 	if (getifaddrs(&ifap) < 0)
1565adeed07fSrillig 		return false;
1566adeed07fSrillig 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
156700a0a652Sitojun 		if (ifa->ifa_addr->sa_family != AF_INET ||
156800a0a652Sitojun 		    (ifa->ifa_flags & IFF_BROADCAST) == 0)
156900a0a652Sitojun 			continue;
15708b39f71dSchristos 		sin = (struct sockaddr_in *)(void *)ifa->ifa_broadaddr;
157100a0a652Sitojun 		if (sin->sin_addr.s_addr == in->s_addr) {
157200a0a652Sitojun 			freeifaddrs(ifap);
1573adeed07fSrillig 			return true;
157400a0a652Sitojun 		}
157500a0a652Sitojun 	}
157600a0a652Sitojun 	freeifaddrs(ifap);
1577adeed07fSrillig 	return false;
157800a0a652Sitojun }
15798be71949Stron 
15808be71949Stron static int
my_kevent(const struct kevent * changelist,size_t nchanges,struct kevent * eventlist,size_t nevents)15818be71949Stron my_kevent(const struct kevent *changelist, size_t nchanges,
15828be71949Stron     struct kevent *eventlist, size_t nevents)
15838be71949Stron {
15848be71949Stron 	int	result;
15858be71949Stron 
15868be71949Stron 	while ((result = kevent(kq, changelist, nchanges, eventlist, nevents,
15878be71949Stron 	    NULL)) < 0)
15888be71949Stron 		if (errno != EINTR) {
15898be71949Stron 			syslog(LOG_ERR, "kevent: %m");
15908be71949Stron 			exit(EXIT_FAILURE);
15918be71949Stron 		}
15928be71949Stron 
15938be71949Stron 	return (result);
15948be71949Stron }
1595726769adStron 
1596726769adStron static struct kevent *
allocchange(void)1597726769adStron allocchange(void)
1598726769adStron {
159925573806Schristos 	if (changes == __arraycount(changebuf)) {
160025573806Schristos 		(void) my_kevent(changebuf, __arraycount(changebuf), NULL, 0);
1601726769adStron 		changes = 0;
1602726769adStron 	}
1603726769adStron 
1604726769adStron 	return (&changebuf[changes++]);
1605726769adStron }
160625573806Schristos 
1607b19025f3Schristos bool
try_biltin(struct servtab * sep)1608b19025f3Schristos try_biltin(struct servtab *sep)
160925573806Schristos {
1610b19025f3Schristos 	for (size_t i = 0; i < __arraycount(biltins); i++) {
1611b19025f3Schristos 		if (biltins[i].bi_socktype == sep->se_socktype &&
1612b19025f3Schristos 		    strcmp(biltins[i].bi_service, sep->se_service) == 0) {
1613b19025f3Schristos 			sep->se_bi = &biltins[i];
1614b19025f3Schristos 			sep->se_wait = biltins[i].bi_wait;
161525573806Schristos 			return true;
161625573806Schristos 		}
161725573806Schristos 	}
1618b19025f3Schristos 	return false;
161925573806Schristos }
1620