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