inetd.c revision 1.137 1 /* $NetBSD: inetd.c,v 1.137 2021/10/12 19:08:04 christos Exp $ */
2
3 /*-
4 * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center and by Matthias Scheler.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * Copyright (c) 1983, 1991, 1993, 1994
35 * The Regents of the University of California. All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 * 3. Neither the name of the University nor the names of its contributors
46 * may be used to endorse or promote products derived from this software
47 * without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 */
61
62 #include <sys/cdefs.h>
63 #ifndef lint
64 __COPYRIGHT("@(#) Copyright (c) 1983, 1991, 1993, 1994\
65 The Regents of the University of California. All rights reserved.");
66 #if 0
67 static char sccsid[] = "@(#)inetd.c 8.4 (Berkeley) 4/13/94";
68 #else
69 __RCSID("$NetBSD: inetd.c,v 1.137 2021/10/12 19:08:04 christos Exp $");
70 #endif
71 #endif /* not lint */
72
73 /*
74 * Inetd - Internet super-server
75 *
76 * This program invokes all internet services as needed. Connection-oriented
77 * services are invoked each time a connection is made, by creating a process.
78 * This process is passed the connection as file descriptor 0 and is expected
79 * to do a getpeername to find out the source host and port.
80 *
81 * Datagram oriented services are invoked when a datagram
82 * arrives; a process is created and passed a pending message
83 * on file descriptor 0. Datagram servers may either connect
84 * to their peer, freeing up the original socket for inetd
85 * to receive further messages on, or ``take over the socket'',
86 * processing all arriving datagrams and, eventually, timing
87 * out. The first type of server is said to be ``multi-threaded'';
88 * the second type of server ``single-threaded''.
89 *
90 * Inetd uses a configuration file which is read at startup
91 * and, possibly, at some later time in response to a hangup signal.
92 * The configuration file is ``free format'' with fields given in the
93 * order shown below. Continuation lines for an entry must being with
94 * a space or tab. All fields must be present in each entry.
95 *
96 * service name must be in /etc/services or must
97 * name a tcpmux service
98 * socket type[:accf[,arg]] stream/dgram/raw/rdm/seqpacket,
99 only stream can name an accept filter
100 * protocol must be in /etc/protocols
101 * wait/nowait[:max] single-threaded/multi-threaded, max #
102 * user[:group] user/group to run daemon as
103 * server program full path name
104 * server program arguments maximum of MAXARGV (64)
105 *
106 * For RPC services
107 * service name/version must be in /etc/rpc
108 * socket type stream/dgram/raw/rdm/seqpacket
109 * protocol must be in /etc/protocols
110 * wait/nowait[:max] single-threaded/multi-threaded
111 * user[:group] user to run daemon as
112 * server program full path name
113 * server program arguments maximum of MAXARGV (64)
114 *
115 * For non-RPC services, the "service name" can be of the form
116 * hostaddress:servicename, in which case the hostaddress is used
117 * as the host portion of the address to listen on. If hostaddress
118 * consists of a single `*' character, INADDR_ANY is used.
119 *
120 * A line can also consist of just
121 * hostaddress:
122 * where hostaddress is as in the preceding paragraph. Such a line must
123 * have no further fields; the specified hostaddress is remembered and
124 * used for all further lines that have no hostaddress specified,
125 * until the next such line (or EOF). (This is why * is provided to
126 * allow explicit specification of INADDR_ANY.) A line
127 * *:
128 * is implicitly in effect at the beginning of the file.
129 *
130 * The hostaddress specifier may (and often will) contain dots;
131 * the service name must not.
132 *
133 * For RPC services, host-address specifiers are accepted and will
134 * work to some extent; however, because of limitations in the
135 * portmapper interface, it will not work to try to give more than
136 * one line for any given RPC service, even if the host-address
137 * specifiers are different.
138 *
139 * TCP services without official port numbers are handled with the
140 * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
141 * requests. When a connection is made from a foreign host, the service
142 * requested is passed to tcpmux, which looks it up in the servtab list
143 * and returns the proper entry for the service. Tcpmux returns a
144 * negative reply if the service doesn't exist, otherwise the invoked
145 * server is expected to return the positive reply if the service type in
146 * inetd.conf file has the prefix "tcpmux/". If the service type has the
147 * prefix "tcpmux/+", tcpmux will return the positive reply for the
148 * process; this is for compatibility with older server code, and also
149 * allows you to invoke programs that use stdin/stdout without putting any
150 * special server code in them. Services that use tcpmux are "nowait"
151 * because they do not have a well-known port and hence cannot listen
152 * for new requests.
153 *
154 * Comment lines are indicated by a `#' in column 1.
155 *
156 * #ifdef IPSEC
157 * Comment lines that start with "#@" denote IPsec policy string, as described
158 * in ipsec_set_policy(3). This will affect all the following items in
159 * inetd.conf(8). To reset the policy, just use "#@" line. By default,
160 * there's no IPsec policy.
161 * #endif
162 */
163
164 /*
165 * Here's the scoop concerning the user:group feature:
166 *
167 * 1) set-group-option off.
168 *
169 * a) user = root: NO setuid() or setgid() is done
170 *
171 * b) other: setuid()
172 * setgid(primary group as found in passwd)
173 * initgroups(name, primary group)
174 *
175 * 2) set-group-option on.
176 *
177 * a) user = root: NO setuid()
178 * setgid(specified group)
179 * NO initgroups()
180 *
181 * b) other: setuid()
182 * setgid(specified group)
183 * initgroups(name, specified group)
184 *
185 */
186
187 #include <sys/param.h>
188 #include <sys/stat.h>
189 #include <sys/ioctl.h>
190 #include <sys/wait.h>
191 #include <sys/resource.h>
192 #include <sys/event.h>
193 #include <sys/socket.h>
194 #include <sys/queue.h>
195
196
197 #ifndef NO_RPC
198 #define RPC
199 #endif
200
201 #include <net/if.h>
202
203 #ifdef RPC
204 #include <rpc/rpc.h>
205 #include <rpc/rpcb_clnt.h>
206 #include <netconfig.h>
207 #endif
208
209 #include <ctype.h>
210 #include <err.h>
211 #include <errno.h>
212 #include <fcntl.h>
213 #include <glob.h>
214 #include <grp.h>
215 #include <libgen.h>
216 #include <pwd.h>
217 #include <signal.h>
218 #include <stdio.h>
219 #include <stdlib.h>
220 #include <string.h>
221 #include <syslog.h>
222 #include <unistd.h>
223 #include <util.h>
224 #include <ifaddrs.h>
225
226 #include "inetd.h"
227
228 #ifdef LIBWRAP
229 # include <tcpd.h>
230 #ifndef LIBWRAP_ALLOW_FACILITY
231 # define LIBWRAP_ALLOW_FACILITY LOG_AUTH
232 #endif
233 #ifndef LIBWRAP_ALLOW_SEVERITY
234 # define LIBWRAP_ALLOW_SEVERITY LOG_INFO
235 #endif
236 #ifndef LIBWRAP_DENY_FACILITY
237 # define LIBWRAP_DENY_FACILITY LOG_AUTH
238 #endif
239 #ifndef LIBWRAP_DENY_SEVERITY
240 # define LIBWRAP_DENY_SEVERITY LOG_WARNING
241 #endif
242 int allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
243 int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
244 #endif
245
246 static bool foreground;
247 int debug;
248 #ifdef LIBWRAP
249 int lflag;
250 #endif
251 int maxsock;
252 int kq;
253 int options;
254 int timingout;
255 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
256
257 #ifndef OPEN_MAX
258 #define OPEN_MAX 64
259 #endif
260
261 /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
262 #define FD_MARGIN (8)
263 rlim_t rlim_ofile_cur = OPEN_MAX;
264
265 struct rlimit rlim_ofile;
266
267 struct kevent changebuf[64];
268 size_t changes;
269
270 struct servtab *servtab;
271
272 static void chargen_dg(int, struct servtab *);
273 static void chargen_stream(int, struct servtab *);
274 static void daytime_dg(int, struct servtab *);
275 static void daytime_stream(int, struct servtab *);
276 static void discard_dg(int, struct servtab *);
277 static void discard_stream(int, struct servtab *);
278 static void echo_dg(int, struct servtab *);
279 static void echo_stream(int, struct servtab *);
280 __dead static void goaway(void);
281 static void machtime_dg(int, struct servtab *);
282 static void machtime_stream(int, struct servtab *);
283 static void reapchild(void);
284 static void retry(void);
285 static void run_service(int, struct servtab *, int);
286 static void tcpmux(int, struct servtab *);
287 __dead static void usage(void);
288 static void bump_nofile(void);
289 static void inetd_setproctitle(char *, int);
290 static void initring(void);
291 static uint32_t machtime(void);
292 static int port_good_dg(struct sockaddr *);
293 static int dg_broadcast(struct in_addr *);
294 static int my_kevent(const struct kevent *, size_t, struct kevent *, size_t);
295 static struct kevent *allocchange(void);
296 static int get_line(int, char *, int);
297 static void spawn(struct servtab *, int);
298
299 struct biltin {
300 const char *bi_service; /* internally provided service name */
301 int bi_socktype; /* type of socket supported */
302 short bi_fork; /* 1 if should fork before call */
303 short bi_wait; /* 1 if should wait for child */
304 void (*bi_fn)(int, struct servtab *);
305 /* function which performs it */
306 } biltins[] = {
307 /* Echo received data */
308 { "echo", SOCK_STREAM, true, false, echo_stream },
309 { "echo", SOCK_DGRAM, false, false, echo_dg },
310
311 /* Internet /dev/null */
312 { "discard", SOCK_STREAM, true, false, discard_stream },
313 { "discard", SOCK_DGRAM, false, false, discard_dg },
314
315 /* Return 32 bit time since 1970 */
316 { "time", SOCK_STREAM, false, false, machtime_stream },
317 { "time", SOCK_DGRAM, false, false, machtime_dg },
318
319 /* Return human-readable time */
320 { "daytime", SOCK_STREAM, false, false, daytime_stream },
321 { "daytime", SOCK_DGRAM, false, false, daytime_dg },
322
323 /* Familiar character generator */
324 { "chargen", SOCK_STREAM, true, false, chargen_stream },
325 { "chargen", SOCK_DGRAM, false, false, chargen_dg },
326
327 { "tcpmux", SOCK_STREAM, true, false, tcpmux }
328 };
329
330 /* list of "bad" ports. I.e. ports that are most obviously used for
331 * "cycling packets" denial of service attacks. See /etc/services.
332 * List must end with port number "0".
333 */
334
335 u_int16_t bad_ports[] = { 7, 9, 13, 19, 37, 0 };
336
337
338 #define NUMINT (sizeof(intab) / sizeof(struct inent))
339 const char *CONFIG = _PATH_INETDCONF;
340
341 static int my_signals[] =
342 { SIGALRM, SIGHUP, SIGCHLD, SIGTERM, SIGINT, SIGPIPE };
343
344 int
345 main(int argc, char *argv[])
346 {
347 int ch, n, reload = 1;
348
349 while ((ch = getopt(argc, argv,
350 #ifdef LIBWRAP
351 "dfl"
352 #else
353 "df"
354 #endif
355 )) != -1)
356 switch(ch) {
357 case 'd':
358 foreground = true;
359 debug = true;
360 options |= SO_DEBUG;
361 break;
362 case 'f':
363 foreground = true;
364 break;
365 #ifdef LIBWRAP
366 case 'l':
367 lflag = true;
368 break;
369 #endif
370 case '?':
371 default:
372 usage();
373 }
374 argc -= optind;
375 argv += optind;
376
377 if (argc > 0)
378 CONFIG = argv[0];
379
380 if (!foreground)
381 daemon(0, 0);
382 openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
383 pidfile(NULL);
384
385 kq = kqueue();
386 if (kq < 0) {
387 syslog(LOG_ERR, "kqueue: %m");
388 return (EXIT_FAILURE);
389 }
390
391 if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
392 syslog(LOG_ERR, "getrlimit: %m");
393 } else {
394 rlim_ofile_cur = rlim_ofile.rlim_cur;
395 if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
396 rlim_ofile_cur = OPEN_MAX;
397 }
398
399 for (n = 0; n < (int)__arraycount(my_signals); n++) {
400 int signum;
401
402 signum = my_signals[n];
403 if (signum != SIGCHLD)
404 (void) signal(signum, SIG_IGN);
405
406 if (signum != SIGPIPE) {
407 struct kevent *ev;
408
409 ev = allocchange();
410 EV_SET(ev, signum, EVFILT_SIGNAL, EV_ADD | EV_ENABLE,
411 0, 0, 0);
412 }
413 }
414
415 for (;;) {
416 int ctrl;
417 struct kevent eventbuf[64], *ev;
418 struct servtab *sep;
419
420 if (reload) {
421 reload = false;
422 config_root();
423 }
424
425 n = my_kevent(changebuf, changes, eventbuf, __arraycount(eventbuf));
426 changes = 0;
427
428 for (ev = eventbuf; n > 0; ev++, n--) {
429 if (ev->filter == EVFILT_SIGNAL) {
430 switch (ev->ident) {
431 case SIGALRM:
432 retry();
433 break;
434 case SIGCHLD:
435 reapchild();
436 break;
437 case SIGTERM:
438 case SIGINT:
439 goaway();
440 break;
441 case SIGHUP:
442 reload = true;
443 break;
444 }
445 continue;
446 }
447 if (ev->filter != EVFILT_READ)
448 continue;
449 sep = (struct servtab *)ev->udata;
450 /* Paranoia */
451 if ((int)ev->ident != sep->se_fd)
452 continue;
453 DPRINTF(SERV_FMT ": service requested" , SERV_PARAMS(sep));
454 if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM) {
455 /* XXX here do the libwrap check-before-accept*/
456 ctrl = accept(sep->se_fd, NULL, NULL);
457 DPRINTF(SERV_FMT ": accept, ctrl fd %d",
458 SERV_PARAMS(sep), ctrl);
459 if (ctrl < 0) {
460 if (errno != EINTR)
461 syslog(LOG_WARNING,
462 SERV_FMT ": accept: %m",
463 SERV_PARAMS(sep));
464 continue;
465 }
466 } else
467 ctrl = sep->se_fd;
468 spawn(sep, ctrl);
469 }
470 }
471 }
472
473 static void
474 spawn(struct servtab *sep, int ctrl)
475 {
476 int dofork;
477 pid_t pid;
478
479 pid = 0;
480 #ifdef LIBWRAP_INTERNAL
481 dofork = true;
482 #else
483 dofork = (sep->se_bi == NULL || sep->se_bi->bi_fork);
484 #endif
485 if (dofork) {
486 if (rl_process(sep, ctrl)) {
487 return;
488 }
489 pid = fork();
490 if (pid < 0) {
491 syslog(LOG_ERR, "fork: %m");
492 if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM)
493 close(ctrl);
494 sleep(1);
495 return;
496 }
497 if (pid != 0 && sep->se_wait != 0) {
498 struct kevent *ev;
499
500 sep->se_wait = pid;
501 ev = allocchange();
502 EV_SET(ev, sep->se_fd, EVFILT_READ,
503 EV_DELETE, 0, 0, 0);
504 }
505 if (pid == 0) {
506 size_t n;
507
508 for (n = 0; n < __arraycount(my_signals); n++)
509 (void) signal(my_signals[n], SIG_DFL);
510 /* Don't put services in terminal session */
511 if (foreground)
512 setsid();
513 }
514 }
515 if (pid == 0) {
516 run_service(ctrl, sep, dofork);
517 if (dofork)
518 exit(EXIT_SUCCESS);
519 }
520 if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM)
521 close(ctrl);
522 }
523
524 static void
525 run_service(int ctrl, struct servtab *sep, int didfork)
526 {
527 struct passwd *pwd;
528 struct group *grp = NULL; /* XXX gcc */
529 char buf[NI_MAXSERV];
530 struct servtab *s;
531 #ifdef LIBWRAP
532 char abuf[BUFSIZ];
533 struct request_info req;
534 int denied;
535 char *service = NULL; /* XXX gcc */
536 #endif
537
538 #ifdef LIBWRAP
539 #ifndef LIBWRAP_INTERNAL
540 if (sep->se_bi == 0)
541 #endif
542 if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM) {
543 request_init(&req, RQ_DAEMON, sep->se_argv[0] != NULL ?
544 sep->se_argv[0] : sep->se_service, RQ_FILE, ctrl, NULL);
545 fromhost(&req);
546 denied = hosts_access(&req) == 0;
547 if (denied || lflag) {
548 if (getnameinfo(&sep->se_ctrladdr,
549 (socklen_t)sep->se_ctrladdr.sa_len, NULL, 0,
550 buf, sizeof(buf), 0) != 0) {
551 /* shouldn't happen */
552 (void)snprintf(buf, sizeof buf, "%d",
553 ntohs(sep->se_ctrladdr_in.sin_port));
554 }
555 service = buf;
556 if (req.client->sin != NULL) {
557 sockaddr_snprintf(abuf, sizeof(abuf), "%a",
558 req.client->sin);
559 } else {
560 strcpy(abuf, "(null)");
561 }
562 }
563 if (denied) {
564 syslog(deny_severity,
565 "refused connection from %.500s(%s), service %s (%s)",
566 eval_client(&req), abuf, service, sep->se_proto);
567 goto reject;
568 }
569 if (lflag) {
570 syslog(allow_severity,
571 "connection from %.500s(%s), service %s (%s)",
572 eval_client(&req), abuf, service, sep->se_proto);
573 }
574 }
575 #endif /* LIBWRAP */
576
577 if (sep->se_bi != NULL) {
578 if (didfork) {
579 for (s = servtab; s != NULL; s = s->se_next)
580 if (s->se_fd != -1 && s->se_fd != ctrl) {
581 close(s->se_fd);
582 s->se_fd = -1;
583 }
584 }
585 (*sep->se_bi->bi_fn)(ctrl, sep);
586 } else {
587 if ((pwd = getpwnam(sep->se_user)) == NULL) {
588 syslog(LOG_ERR, "%s/%s: %s: No such user",
589 sep->se_service, sep->se_proto, sep->se_user);
590 goto reject;
591 }
592 if (sep->se_group != NULL &&
593 (grp = getgrnam(sep->se_group)) == NULL) {
594 syslog(LOG_ERR, "%s/%s: %s: No such group",
595 sep->se_service, sep->se_proto, sep->se_group);
596 goto reject;
597 }
598 if (pwd->pw_uid != 0) {
599 if (sep->se_group != NULL)
600 pwd->pw_gid = grp->gr_gid;
601 if (setgid(pwd->pw_gid) < 0) {
602 syslog(LOG_ERR,
603 "%s/%s: can't set gid %d: %m", sep->se_service,
604 sep->se_proto, pwd->pw_gid);
605 goto reject;
606 }
607 (void) initgroups(pwd->pw_name,
608 pwd->pw_gid);
609 if (setuid(pwd->pw_uid) < 0) {
610 syslog(LOG_ERR,
611 "%s/%s: can't set uid %d: %m", sep->se_service,
612 sep->se_proto, pwd->pw_uid);
613 goto reject;
614 }
615 } else if (sep->se_group != NULL) {
616 (void) setgid((gid_t)grp->gr_gid);
617 }
618 DPRINTF("%d execl %s",
619 getpid(), sep->se_server);
620 /* Set our control descriptor to not close-on-exec... */
621 if (fcntl(ctrl, F_SETFD, 0) < 0)
622 syslog(LOG_ERR, "fcntl (%d, F_SETFD, 0): %m", ctrl);
623 /* ...and dup it to stdin, stdout, and stderr. */
624 if (ctrl != 0) {
625 dup2(ctrl, 0);
626 close(ctrl);
627 ctrl = 0;
628 }
629 dup2(0, 1);
630 dup2(0, 2);
631 if (rlim_ofile.rlim_cur != rlim_ofile_cur &&
632 setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
633 syslog(LOG_ERR, "setrlimit: %m");
634 execv(sep->se_server, sep->se_argv);
635 syslog(LOG_ERR, "cannot execute %s: %m", sep->se_server);
636 reject:
637 if (sep->se_socktype != SOCK_STREAM)
638 recv(ctrl, buf, sizeof (buf), 0);
639 _exit(EXIT_FAILURE);
640 }
641 }
642
643 static void
644 reapchild(void)
645 {
646 int status;
647 pid_t pid;
648 struct servtab *sep;
649
650 for (;;) {
651 pid = wait3(&status, WNOHANG, NULL);
652 if (pid <= 0)
653 break;
654 DPRINTF("%d reaped, status %#x", pid, status);
655 for (sep = servtab; sep != NULL; sep = sep->se_next)
656 if (sep->se_wait == pid) {
657 struct kevent *ev;
658
659 if (WIFEXITED(status) && WEXITSTATUS(status))
660 syslog(LOG_WARNING,
661 "%s: exit status %u",
662 sep->se_server, WEXITSTATUS(status));
663 else if (WIFSIGNALED(status))
664 syslog(LOG_WARNING,
665 "%s: exit signal %u",
666 sep->se_server, WTERMSIG(status));
667 sep->se_wait = 1;
668 ev = allocchange();
669 EV_SET(ev, sep->se_fd, EVFILT_READ,
670 EV_ADD | EV_ENABLE, 0, 0, (intptr_t)sep);
671 DPRINTF("restored %s, fd %d",
672 sep->se_service, sep->se_fd);
673 }
674 }
675 }
676
677 static void
678 retry(void)
679 {
680 struct servtab *sep;
681
682 timingout = false;
683 for (sep = servtab; sep != NULL; sep = sep->se_next) {
684 if (sep->se_fd == -1 && !ISMUX(sep)) {
685 switch (sep->se_family) {
686 case AF_LOCAL:
687 case AF_INET:
688 #ifdef INET6
689 case AF_INET6:
690 #endif
691 setup(sep);
692 if (sep->se_fd >= 0 && isrpcservice(sep))
693 register_rpc(sep);
694 break;
695 }
696 }
697 }
698 }
699
700 static void
701 goaway(void)
702 {
703 struct servtab *sep;
704
705 for (sep = servtab; sep != NULL; sep = sep->se_next) {
706 if (sep->se_fd == -1)
707 continue;
708
709 switch (sep->se_family) {
710 case AF_LOCAL:
711 (void)unlink(sep->se_service);
712 break;
713 case AF_INET:
714 #ifdef INET6
715 case AF_INET6:
716 #endif
717 if (sep->se_wait == 1 && isrpcservice(sep))
718 unregister_rpc(sep);
719 break;
720 }
721 (void)close(sep->se_fd);
722 sep->se_fd = -1;
723 }
724
725 DPRINTF("Going away.");
726
727 exit(EXIT_SUCCESS);
728 }
729
730 void
731 setup(struct servtab *sep)
732 {
733 int on = 1;
734 #ifdef INET6
735 int off = 0;
736 #endif
737 struct kevent *ev;
738
739 if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
740 DPRINTF("socket failed on " SERV_FMT ": %s",
741 SERV_PARAMS(sep), strerror(errno));
742 syslog(LOG_ERR, "%s/%s: socket: %m",
743 sep->se_service, sep->se_proto);
744 return;
745 }
746 /* Set all listening sockets to close-on-exec. */
747 if (fcntl(sep->se_fd, F_SETFD, FD_CLOEXEC) < 0) {
748 syslog(LOG_ERR, SERV_FMT ": fcntl(F_SETFD, FD_CLOEXEC): %m",
749 SERV_PARAMS(sep));
750 close(sep->se_fd);
751 sep->se_fd = -1;
752 return;
753 }
754
755 #define turnon(fd, opt) \
756 setsockopt(fd, SOL_SOCKET, opt, &on, (socklen_t)sizeof(on))
757 if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
758 turnon(sep->se_fd, SO_DEBUG) < 0)
759 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
760 if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
761 syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
762 #undef turnon
763
764 /* Set the socket buffer sizes, if specified. */
765 if (sep->se_sndbuf != 0 && setsockopt(sep->se_fd, SOL_SOCKET,
766 SO_SNDBUF, &sep->se_sndbuf, (socklen_t)sizeof(sep->se_sndbuf)) < 0)
767 syslog(LOG_ERR, "setsockopt (SO_SNDBUF %d): %m",
768 sep->se_sndbuf);
769 if (sep->se_rcvbuf != 0 && setsockopt(sep->se_fd, SOL_SOCKET,
770 SO_RCVBUF, &sep->se_rcvbuf, (socklen_t)sizeof(sep->se_rcvbuf)) < 0)
771 syslog(LOG_ERR, "setsockopt (SO_RCVBUF %d): %m",
772 sep->se_rcvbuf);
773 #ifdef INET6
774 if (sep->se_family == AF_INET6) {
775 int *v;
776 v = (sep->se_type == FAITH_TYPE) ? &on : &off;
777 if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH,
778 v, (socklen_t)sizeof(*v)) < 0)
779 syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m");
780 }
781 #endif
782 #ifdef IPSEC
783 /* Avoid setting a policy if a policy specifier doesn't exist. */
784 if (sep->se_policy != NULL) {
785 int e = ipsecsetup(sep->se_family, sep->se_fd, sep->se_policy);
786 if (e < 0) {
787 syslog(LOG_ERR, SERV_FMT ": ipsec setup failed",
788 SERV_PARAMS(sep));
789 (void)close(sep->se_fd);
790 sep->se_fd = -1;
791 return;
792 }
793 }
794 #endif
795
796 if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) {
797 DPRINTF(SERV_FMT ": bind failed: %s",
798 SERV_PARAMS(sep), strerror(errno));
799 syslog(LOG_ERR, SERV_FMT ": bind: %m",
800 SERV_PARAMS(sep));
801 (void) close(sep->se_fd);
802 sep->se_fd = -1;
803 if (!timingout) {
804 timingout = true;
805 alarm(RETRYTIME);
806 }
807 return;
808 }
809 if (sep->se_socktype == SOCK_STREAM)
810 listen(sep->se_fd, 10);
811
812 /* Set the accept filter, if specified. To be done after listen.*/
813 if (sep->se_accf.af_name[0] != 0 && setsockopt(sep->se_fd, SOL_SOCKET,
814 SO_ACCEPTFILTER, &sep->se_accf,
815 (socklen_t)sizeof(sep->se_accf)) < 0)
816 syslog(LOG_ERR, "setsockopt(SO_ACCEPTFILTER %s): %m",
817 sep->se_accf.af_name);
818
819 ev = allocchange();
820 EV_SET(ev, sep->se_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0,
821 (intptr_t)sep);
822 if (sep->se_fd > maxsock) {
823 maxsock = sep->se_fd;
824 if (maxsock > (int)(rlim_ofile_cur - FD_MARGIN))
825 bump_nofile();
826 }
827 DPRINTF(SERV_FMT ": registered on fd %d", SERV_PARAMS(sep), sep->se_fd);
828 }
829
830 /*
831 * Finish with a service and its socket.
832 */
833 void
834 close_sep(struct servtab *sep)
835 {
836
837 if (sep->se_fd >= 0) {
838 (void) close(sep->se_fd);
839 sep->se_fd = -1;
840 }
841 sep->se_count = 0;
842 if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) {
843 rl_clear_ip_list(sep);
844 }
845 }
846
847 void
848 register_rpc(struct servtab *sep)
849 {
850 #ifdef RPC
851 struct netbuf nbuf;
852 struct sockaddr_storage ss;
853 struct netconfig *nconf;
854 socklen_t socklen;
855 int n;
856
857 if ((nconf = getnetconfigent(sep->se_proto+4)) == NULL) {
858 syslog(LOG_ERR, "%s: getnetconfigent failed",
859 sep->se_proto);
860 return;
861 }
862 socklen = sizeof ss;
863 if (getsockname(sep->se_fd, (struct sockaddr *)(void *)&ss, &socklen) < 0) {
864 syslog(LOG_ERR, SERV_FMT ": getsockname: %m",
865 SERV_PARAMS(sep));
866 return;
867 }
868
869 nbuf.buf = &ss;
870 nbuf.len = ss.ss_len;
871 nbuf.maxlen = sizeof (struct sockaddr_storage);
872 for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
873 DPRINTF("rpcb_set: %u %d %s %s",
874 sep->se_rpcprog, n, nconf->nc_netid,
875 taddr2uaddr(nconf, &nbuf));
876 (void)rpcb_unset((unsigned int)sep->se_rpcprog, (unsigned int)n, nconf);
877 if (rpcb_set((unsigned int)sep->se_rpcprog, (unsigned int)n, nconf, &nbuf) == 0)
878 syslog(LOG_ERR, "rpcb_set: %u %d %s %s%s",
879 sep->se_rpcprog, n, nconf->nc_netid,
880 taddr2uaddr(nconf, &nbuf), clnt_spcreateerror(""));
881 }
882 #endif /* RPC */
883 }
884
885 void
886 unregister_rpc(struct servtab *sep)
887 {
888 #ifdef RPC
889 int n;
890 struct netconfig *nconf;
891
892 if ((nconf = getnetconfigent(sep->se_proto+4)) == NULL) {
893 syslog(LOG_ERR, "%s: getnetconfigent failed",
894 sep->se_proto);
895 return;
896 }
897
898 for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
899 DPRINTF("rpcb_unset(%u, %d, %s)",
900 sep->se_rpcprog, n, nconf->nc_netid);
901 if (rpcb_unset((unsigned int)sep->se_rpcprog, (unsigned int)n, nconf) == 0)
902 syslog(LOG_ERR, "rpcb_unset(%u, %d, %s) failed\n",
903 sep->se_rpcprog, n, nconf->nc_netid);
904 }
905 #endif /* RPC */
906 }
907
908 static void
909 inetd_setproctitle(char *a, int s)
910 {
911 socklen_t size;
912 struct sockaddr_storage ss;
913 char hbuf[NI_MAXHOST];
914 const char *hp;
915 struct sockaddr *sa;
916
917 size = sizeof(ss);
918 sa = (struct sockaddr *)(void *)&ss;
919 if (getpeername(s, sa, &size) == 0) {
920 if (getnameinfo(sa, size, hbuf, (socklen_t)sizeof(hbuf), NULL,
921 0, niflags) != 0)
922 hp = "?";
923 else
924 hp = hbuf;
925 setproctitle("-%s [%s]", a, hp);
926 } else
927 setproctitle("-%s", a);
928 }
929
930 static void
931 bump_nofile(void)
932 {
933 #define FD_CHUNK 32
934 struct rlimit rl;
935
936 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
937 syslog(LOG_ERR, "getrlimit: %m");
938 return;
939 }
940 rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
941 if (rl.rlim_cur <= rlim_ofile_cur) {
942 syslog(LOG_ERR,
943 "bump_nofile: cannot extend file limit, max = %d",
944 (int)rl.rlim_cur);
945 return;
946 }
947
948 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
949 syslog(LOG_ERR, "setrlimit: %m");
950 return;
951 }
952
953 rlim_ofile_cur = rl.rlim_cur;
954 return;
955 }
956
957 /*
958 * Internet services provided internally by inetd:
959 */
960 #define BUFSIZE 4096
961
962 /* ARGSUSED */
963 static void
964 echo_stream(int s, struct servtab *sep) /* Echo service -- echo data back */
965 {
966 char buffer[BUFSIZE];
967 ssize_t i;
968
969 inetd_setproctitle(sep->se_service, s);
970 while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
971 write(s, buffer, (size_t)i) > 0)
972 continue;
973 }
974
975 /* ARGSUSED */
976 static void
977 echo_dg(int s, struct servtab *sep) /* Echo service -- echo data back */
978 {
979 char buffer[BUFSIZE];
980 ssize_t i;
981 socklen_t size;
982 struct sockaddr_storage ss;
983 struct sockaddr *sa;
984
985 sa = (struct sockaddr *)(void *)&ss;
986 size = sizeof(ss);
987 if ((i = recvfrom(s, buffer, sizeof(buffer), 0, sa, &size)) < 0)
988 return;
989 if (port_good_dg(sa))
990 (void) sendto(s, buffer, (size_t)i, 0, sa, size);
991 }
992
993 /* ARGSUSED */
994 static void
995 discard_stream(int s, struct servtab *sep) /* Discard service -- ignore data */
996 {
997 char buffer[BUFSIZE];
998
999 inetd_setproctitle(sep->se_service, s);
1000 while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
1001 errno == EINTR)
1002 ;
1003 }
1004
1005 /* ARGSUSED */
1006 static void
1007 discard_dg(int s, struct servtab *sep) /* Discard service -- ignore data */
1008
1009 {
1010 char buffer[BUFSIZE];
1011
1012 (void) read(s, buffer, sizeof(buffer));
1013 }
1014
1015 #define LINESIZ 72
1016 char ring[128];
1017 char *endring;
1018
1019 static void
1020 initring(void)
1021 {
1022 int i;
1023
1024 endring = ring;
1025
1026 for (i = 0; i <= 128; ++i)
1027 if (isprint(i))
1028 *endring++ = (char)i;
1029 }
1030
1031 /* ARGSUSED */
1032 static void
1033 chargen_stream(int s, struct servtab *sep) /* Character generator */
1034 {
1035 size_t len;
1036 char *rs, text[LINESIZ+2];
1037
1038 inetd_setproctitle(sep->se_service, s);
1039
1040 if (endring == NULL) {
1041 initring();
1042 rs = ring;
1043 }
1044
1045 text[LINESIZ] = '\r';
1046 text[LINESIZ + 1] = '\n';
1047 for (rs = ring;;) {
1048 if ((len = (size_t)(endring - rs)) >= LINESIZ)
1049 memmove(text, rs, LINESIZ);
1050 else {
1051 memmove(text, rs, len);
1052 memmove(text + len, ring, LINESIZ - len);
1053 }
1054 if (++rs == endring)
1055 rs = ring;
1056 if (write(s, text, sizeof(text)) != sizeof(text))
1057 break;
1058 }
1059 }
1060
1061 /* ARGSUSED */
1062 static void
1063 chargen_dg(int s, struct servtab *sep) /* Character generator */
1064 {
1065 struct sockaddr_storage ss;
1066 struct sockaddr *sa;
1067 static char *rs;
1068 size_t len;
1069 socklen_t size;
1070 char text[LINESIZ+2];
1071
1072 if (endring == 0) {
1073 initring();
1074 rs = ring;
1075 }
1076
1077 sa = (struct sockaddr *)(void *)&ss;
1078 size = sizeof(ss);
1079 if (recvfrom(s, text, sizeof(text), 0, sa, &size) < 0)
1080 return;
1081
1082 if (!port_good_dg(sa))
1083 return;
1084
1085 if ((len = (size_t)(endring - rs)) >= LINESIZ)
1086 memmove(text, rs, LINESIZ);
1087 else {
1088 memmove(text, rs, len);
1089 memmove(text + len, ring, LINESIZ - len);
1090 }
1091 if (++rs == endring)
1092 rs = ring;
1093 text[LINESIZ] = '\r';
1094 text[LINESIZ + 1] = '\n';
1095 (void) sendto(s, text, sizeof(text), 0, sa, size);
1096 }
1097
1098 /*
1099 * Return a machine readable date and time, in the form of the
1100 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
1101 * returns the number of seconds since midnight, Jan 1, 1970,
1102 * we must add 2208988800 seconds to this figure to make up for
1103 * some seventy years Bell Labs was asleep.
1104 */
1105
1106 static uint32_t
1107 machtime(void)
1108 {
1109 struct timeval tv;
1110
1111 if (gettimeofday(&tv, NULL) < 0) {
1112 DPRINTF("Unable to get time of day");
1113 return (0);
1114 }
1115 #define OFFSET ((uint32_t)25567 * 24*60*60)
1116 return (htonl((uint32_t)(tv.tv_sec + OFFSET)));
1117 #undef OFFSET
1118 }
1119
1120 /* ARGSUSED */
1121 static void
1122 machtime_stream(int s, struct servtab *sep)
1123 {
1124 uint32_t result;
1125
1126 result = machtime();
1127 (void) write(s, &result, sizeof(result));
1128 }
1129
1130 /* ARGSUSED */
1131 void
1132 machtime_dg(int s, struct servtab *sep)
1133 {
1134 uint32_t result;
1135 struct sockaddr_storage ss;
1136 struct sockaddr *sa;
1137 socklen_t size;
1138
1139 sa = (struct sockaddr *)(void *)&ss;
1140 size = sizeof(ss);
1141 if (recvfrom(s, &result, sizeof(result), 0, sa, &size) < 0)
1142 return;
1143 if (!port_good_dg(sa))
1144 return;
1145 result = machtime();
1146 (void)sendto(s, &result, sizeof(result), 0, sa, size);
1147 }
1148
1149 /* ARGSUSED */
1150 static void
1151 daytime_stream(int s,struct servtab *sep)
1152 /* Return human-readable time of day */
1153 {
1154 char buffer[256];
1155 time_t clk;
1156 int len;
1157
1158 clk = time((time_t *) 0);
1159
1160 len = snprintf(buffer, sizeof buffer, "%.24s\r\n", ctime(&clk));
1161 (void) write(s, buffer, (size_t)len);
1162 }
1163
1164 /* ARGSUSED */
1165 void
1166 daytime_dg(int s, struct servtab *sep)
1167 /* Return human-readable time of day */
1168 {
1169 char buffer[256];
1170 time_t clk;
1171 struct sockaddr_storage ss;
1172 struct sockaddr *sa;
1173 socklen_t size;
1174 int len;
1175
1176 clk = time((time_t *) 0);
1177
1178 sa = (struct sockaddr *)(void *)&ss;
1179 size = sizeof(ss);
1180 if (recvfrom(s, buffer, sizeof(buffer), 0, sa, &size) < 0)
1181 return;
1182 if (!port_good_dg(sa))
1183 return;
1184 len = snprintf(buffer, sizeof buffer, "%.24s\r\n", ctime(&clk));
1185 (void) sendto(s, buffer, (size_t)len, 0, sa, size);
1186 }
1187
1188 static void
1189 usage(void)
1190 {
1191 #ifdef LIBWRAP
1192 (void)fprintf(stderr, "usage: %s [-dl] [conf]\n", getprogname());
1193 #else
1194 (void)fprintf(stderr, "usage: %s [-d] [conf]\n", getprogname());
1195 #endif
1196 exit(EXIT_FAILURE);
1197 }
1198
1199
1200 /*
1201 * Based on TCPMUX.C by Mark K. Lottor November 1988
1202 * sri-nic::ps:<mkl>tcpmux.c
1203 */
1204
1205 static int /* # of characters upto \r,\n or \0 */
1206 get_line(int fd, char *buf, int len)
1207 {
1208 int count = 0;
1209 ssize_t n;
1210
1211 do {
1212 n = read(fd, buf, (size_t)(len - count));
1213 if (n == 0)
1214 return (count);
1215 if (n < 0)
1216 return (-1);
1217 while (--n >= 0) {
1218 if (*buf == '\r' || *buf == '\n' || *buf == '\0')
1219 return (count);
1220 count++;
1221 buf++;
1222 }
1223 } while (count < len);
1224 return (count);
1225 }
1226
1227 #define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */
1228
1229 #define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1)
1230
1231 static void
1232 tcpmux(int ctrl, struct servtab *sep)
1233 {
1234 char service[MAX_SERV_LEN+1];
1235 int len;
1236
1237 /* Get requested service name */
1238 if ((len = get_line(ctrl, service, MAX_SERV_LEN)) < 0) {
1239 strwrite(ctrl, "-Error reading service name\r\n");
1240 goto reject;
1241 }
1242 service[len] = '\0';
1243
1244 DPRINTF("tcpmux: %s: service requested", service);
1245
1246 /*
1247 * Help is a required command, and lists available services,
1248 * one per line.
1249 */
1250 if (strcasecmp(service, "help") == 0) {
1251 strwrite(ctrl, "+Available services:\r\n");
1252 strwrite(ctrl, "help\r\n");
1253 for (sep = servtab; sep != NULL; sep = sep->se_next) {
1254 if (!ISMUX(sep))
1255 continue;
1256 (void)write(ctrl, sep->se_service,
1257 strlen(sep->se_service));
1258 strwrite(ctrl, "\r\n");
1259 }
1260 goto reject;
1261 }
1262
1263 /* Try matching a service in inetd.conf with the request */
1264 for (sep = servtab; sep != NULL; sep = sep->se_next) {
1265 if (!ISMUX(sep))
1266 continue;
1267 if (strcasecmp(service, sep->se_service) == 0) {
1268 if (ISMUXPLUS(sep))
1269 strwrite(ctrl, "+Go\r\n");
1270 run_service(ctrl, sep, true /* forked */);
1271 return;
1272 }
1273 }
1274 strwrite(ctrl, "-Service not available\r\n");
1275 reject:
1276 _exit(EXIT_FAILURE);
1277 }
1278
1279 /*
1280 * check if the address/port where send data to is one of the obvious ports
1281 * that are used for denial of service attacks like two echo ports
1282 * just echoing data between them
1283 */
1284 static int
1285 port_good_dg(struct sockaddr *sa)
1286 {
1287 struct in_addr in;
1288 struct sockaddr_in *sin;
1289 #ifdef INET6
1290 struct in6_addr *in6;
1291 struct sockaddr_in6 *sin6;
1292 #endif
1293 u_int16_t port;
1294 int i;
1295 char hbuf[NI_MAXHOST];
1296
1297 switch (sa->sa_family) {
1298 case AF_INET:
1299 sin = (struct sockaddr_in *)(void *)sa;
1300 in.s_addr = ntohl(sin->sin_addr.s_addr);
1301 port = ntohs(sin->sin_port);
1302 #ifdef INET6
1303 v4chk:
1304 #endif
1305 if (IN_MULTICAST(in.s_addr))
1306 goto bad;
1307 switch ((in.s_addr & 0xff000000) >> 24) {
1308 case 0: case 127: case 255:
1309 goto bad;
1310 }
1311 if (dg_broadcast(&in))
1312 goto bad;
1313 break;
1314 #ifdef INET6
1315 case AF_INET6:
1316 sin6 = (struct sockaddr_in6 *)(void *)sa;
1317 in6 = &sin6->sin6_addr;
1318 port = ntohs(sin6->sin6_port);
1319 if (IN6_IS_ADDR_MULTICAST(in6) || IN6_IS_ADDR_UNSPECIFIED(in6))
1320 goto bad;
1321 if (IN6_IS_ADDR_V4MAPPED(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
1322 memcpy(&in, &in6->s6_addr[12], sizeof(in));
1323 in.s_addr = ntohl(in.s_addr);
1324 goto v4chk;
1325 }
1326 break;
1327 #endif
1328 default:
1329 /* XXX unsupported af, is it safe to assume it to be safe? */
1330 return true;
1331 }
1332
1333 for (i = 0; bad_ports[i] != 0; i++) {
1334 if (port == bad_ports[i])
1335 goto bad;
1336 }
1337
1338 return true;
1339
1340 bad:
1341 if (getnameinfo(sa, sa->sa_len, hbuf, (socklen_t)sizeof(hbuf), NULL, 0,
1342 niflags) != 0)
1343 strlcpy(hbuf, "?", sizeof(hbuf));
1344 syslog(LOG_WARNING,"Possible DoS attack from %s, Port %d",
1345 hbuf, port);
1346 return false;
1347 }
1348
1349 /* XXX need optimization */
1350 static int
1351 dg_broadcast(struct in_addr *in)
1352 {
1353 struct ifaddrs *ifa, *ifap;
1354 struct sockaddr_in *sin;
1355
1356 if (getifaddrs(&ifap) < 0)
1357 return false;
1358 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
1359 if (ifa->ifa_addr->sa_family != AF_INET ||
1360 (ifa->ifa_flags & IFF_BROADCAST) == 0)
1361 continue;
1362 sin = (struct sockaddr_in *)(void *)ifa->ifa_broadaddr;
1363 if (sin->sin_addr.s_addr == in->s_addr) {
1364 freeifaddrs(ifap);
1365 return true;
1366 }
1367 }
1368 freeifaddrs(ifap);
1369 return false;
1370 }
1371
1372 static int
1373 my_kevent(const struct kevent *changelist, size_t nchanges,
1374 struct kevent *eventlist, size_t nevents)
1375 {
1376 int result;
1377
1378 while ((result = kevent(kq, changelist, nchanges, eventlist, nevents,
1379 NULL)) < 0)
1380 if (errno != EINTR) {
1381 syslog(LOG_ERR, "kevent: %m");
1382 exit(EXIT_FAILURE);
1383 }
1384
1385 return (result);
1386 }
1387
1388 static struct kevent *
1389 allocchange(void)
1390 {
1391 if (changes == __arraycount(changebuf)) {
1392 (void) my_kevent(changebuf, __arraycount(changebuf), NULL, 0);
1393 changes = 0;
1394 }
1395
1396 return (&changebuf[changes++]);
1397 }
1398
1399 bool
1400 try_biltin(struct servtab *sep)
1401 {
1402 for(size_t i = 0; i < __arraycount(biltins); i++) {
1403 if (biltins[i].bi_socktype == sep->se_socktype &&
1404 strcmp(biltins[i].bi_service, sep->se_service) == 0) {
1405 sep->se_bi = &biltins[i];
1406 sep->se_wait = biltins[i].bi_wait;
1407 return true;
1408 }
1409 }
1410 return false;
1411 }
1412