From: David van Moolenbroek Date: Wed, 15 Feb 2017 20:53:10 +0000 (+0000) Subject: Import NetBSD inetd(8) X-Git-Url: http://zhaoyanbai.com/repos/?a=commitdiff_plain;h=c3b6f8f269dded933641674db559d1ecea71d5f1;p=minix.git Import NetBSD inetd(8) Do not start it by default just yet. Change-Id: Id8d2dd33eb67ae74b3ef3060638e20c781e8e37d --- diff --git a/distrib/sets/lists/minix-base/mi b/distrib/sets/lists/minix-base/mi index f25b07396..5ec198f9e 100644 --- a/distrib/sets/lists/minix-base/mi +++ b/distrib/sets/lists/minix-base/mi @@ -102,6 +102,7 @@ ./etc/hostname.file minix-base ./etc/hosts minix-base ./etc/inet.conf minix-base obsolete +./etc/inetd.conf minix-base ./etc/kyua minix-tests kyua ./etc/man.conf minix-base ./etc/master.passwd minix-base @@ -135,6 +136,7 @@ ./etc/rc.d/bootconf.sh minix-base ./etc/rc.d/fsck minix-base ./etc/rc.d/ftpd minix-base +./etc/rc.d/inetd minix-base ./etc/rc.d/ipfilter minix-base ./etc/rc.d/ipsec minix-base ./etc/rc.d/local minix-base @@ -1029,6 +1031,7 @@ ./usr/sbin/groupinfo minix-base ./usr/sbin/groupmod minix-base ./usr/sbin/i2cscan minix-base +./usr/sbin/inetd minix-base ./usr/sbin/installboot_nbsd minix-base ./usr/sbin/kernel minix-base ./usr/sbin/link minix-base diff --git a/distrib/sets/lists/minix-debug/mi b/distrib/sets/lists/minix-debug/mi index e86139f64..811afe3f6 100644 --- a/distrib/sets/lists/minix-debug/mi +++ b/distrib/sets/lists/minix-debug/mi @@ -601,6 +601,7 @@ ./usr/libdata/debug/usr/sbin/diskctl.debug minix-debug debug ./usr/libdata/debug/usr/sbin/fbdctl.debug minix-debug debug ./usr/libdata/debug/usr/sbin/i2cscan.debug minix-debug debug +./usr/libdata/debug/usr/sbin/inetd.debug minix-debug debug ./usr/libdata/debug/usr/sbin/installboot_nbsd.debug minix-debug debug ./usr/libdata/debug/usr/sbin/kernel.debug minix-debug debug ./usr/libdata/debug/usr/sbin/link.debug minix-debug debug diff --git a/distrib/sets/lists/minix-man/mi b/distrib/sets/lists/minix-man/mi index 05205538e..21f4d0489 100644 --- a/distrib/sets/lists/minix-man/mi +++ b/distrib/sets/lists/minix-man/mi @@ -3297,6 +3297,7 @@ ./usr/man/man5/hosts_options.5 minix-man ./usr/man/man5/http_status.5 minix-man obsolete ./usr/man/man5/httpd.conf.5 minix-man obsolete +./usr/man/man5/inetd.conf.5 minix-man ./usr/man/man5/info.5 minix-man ./usr/man/man5/keymap.5 minix-man ./usr/man/man5/kyua-tester-list.5 minix-man kyua @@ -3415,6 +3416,7 @@ ./usr/man/man8/ifconfig.8 minix-man ./usr/man/man8/in.httpd.8 minix-man obsolete ./usr/man/man8/inet.8 minix-man obsolete +./usr/man/man8/inetd.8 minix-man ./usr/man/man8/init.8 minix-man ./usr/man/man8/installboot_nbsd.8 minix-man ./usr/man/man8/intr.8 minix-man diff --git a/etc/Makefile b/etc/Makefile index 55f92c404..d14c241d5 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -321,6 +321,7 @@ install-etc-files: .PHONY .MAKE check_DESTDIR MAKEDEV ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ group \ ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ hostname.file \ ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ hosts \ + ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ inetd.conf \ ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ mk.conf \ ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ motd \ ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ nsswitch.conf \ diff --git a/etc/defaults/minix.rc.conf b/etc/defaults/minix.rc.conf index e93ecb269..c95164129 100644 --- a/etc/defaults/minix.rc.conf +++ b/etc/defaults/minix.rc.conf @@ -2,7 +2,7 @@ # Override settings in NetBSD's default rc.conf with different default settings # for MINIX here, typically to disable scripts that NetBSD enables by default. -#(nothing yet) +inetd=NO # Where to find servers/drivers binaries PKG_SERVICE_DIR=/usr/pkg/service diff --git a/etc/inetd.conf b/etc/inetd.conf new file mode 100644 index 000000000..202ccb357 --- /dev/null +++ b/etc/inetd.conf @@ -0,0 +1,86 @@ +# $NetBSD: inetd.conf,v 1.58 2007/10/16 02:47:14 tls Exp $ +# +# Internet server configuration database +# +# @(#)inetd.conf 8.2 (Berkeley) 3/18/94 +# +#http stream tcp nowait:600 _httpd /usr/libexec/httpd httpd /var/www +#http stream tcp6 nowait:600 _httpd /usr/libexec/httpd httpd /var/www +#ftp stream tcp nowait root /usr/libexec/ftpd ftpd -ll +#ftp stream tcp6 nowait root /usr/libexec/ftpd ftpd -ll +#telnet stream tcp nowait root /usr/libexec/telnetd telnetd -a valid +#telnet stream tcp6 nowait root /usr/libexec/telnetd telnetd -a valid +#shell stream tcp nowait root /usr/libexec/rshd rshd -L +#shell stream tcp6 nowait root /usr/libexec/rshd rshd -L +#login stream tcp nowait root /usr/libexec/rlogind rlogind -L +#login stream tcp6 nowait root /usr/libexec/rlogind rlogind -L +#exec stream tcp nowait root /usr/libexec/rexecd rexecd +#exec stream tcp6 nowait root /usr/libexec/rexecd rexecd +#finger stream tcp nowait nobody /usr/libexec/fingerd fingerd -lsmu +#finger stream tcp6 nowait nobody /usr/libexec/fingerd fingerd -lsmu +#ident stream tcp nowait nobody /usr/libexec/identd identd -l -o OTHER -e -N +#ident stream tcp6 nowait nobody /usr/libexec/identd identd -l -o OTHER -e -N +#tftp dgram udp wait root /usr/libexec/tftpd tftpd -l -s /tftpboot +#tftp dgram udp6 wait root /usr/libexec/tftpd tftpd -l -s /tftpboot +#comsat dgram udp wait root /usr/libexec/comsat comsat +#comsat dgram udp6 wait root /usr/libexec/comsat comsat +#ntalk dgram udp wait nobody:tty /usr/libexec/ntalkd ntalkd +#bootps dgram udp wait root /usr/sbin/bootpd bootpd +# +# Games +# +#hunt dgram udp wait nobody /usr/games/huntd huntd +# +# Internal services +# +#tcpmux stream tcp nowait root internal +#echo stream tcp nowait nobody internal +#echo stream tcp6 nowait nobody internal +#discard stream tcp nowait nobody internal +#discard stream tcp6 nowait nobody internal +#chargen stream tcp nowait nobody internal +#chargen stream tcp6 nowait nobody internal +#daytime stream tcp nowait nobody internal +#daytime stream tcp6 nowait nobody internal +#time stream tcp nowait nobody internal +#time stream tcp6 nowait nobody internal +#echo dgram udp wait nobody internal +#echo dgram udp6 wait nobody internal +#discard dgram udp wait nobody internal +#discard dgram udp6 wait nobody internal +#chargen dgram udp wait nobody internal +#chargen dgram udp6 wait nobody internal +#daytime dgram udp wait nobody internal +#daytime dgram udp6 wait nobody internal +#time dgram udp wait nobody internal +#time dgram udp6 wait nobody internal +#qotd stream tcp nowait nobody /usr/games/fortune fortune +#qotd stream tcp6 nowait nobody /usr/games/fortune fortune +# +# Kerberos authenticated services +# +#klogin stream tcp nowait root /usr/libexec/rlogind rlogind -k +#eklogin stream tcp nowait root /usr/libexec/rlogind rlogind -k -x +#kshell stream tcp nowait root /usr/libexec/rshd rshd -k +# +# Services run ONLY on the Kerberos server +# +#kerberos-adm stream tcp nowait root /usr/libexec/kadmind kadmind +#kerberos-adm stream tcp6 nowait root /usr/libexec/kadmind kadmind +#kpasswd dgram udp wait root /usr/libexec/kpasswdd kpasswdd +#kpasswd dgram udp6 wait root /usr/libexec/kpasswdd kpasswdd +# +# The hprop service is run on slave KDCs to receive the database from +# the master KDC. +#hprop stream tcp nowait root /usr/libexec/hpropd hpropd +#hprop stream tcp6 nowait root /usr/libexec/hpropd hpropd +# +# RPC based services +# +#rstatd/1-3 dgram rpc/udp wait:100 nobody:kmem /usr/libexec/rpc.rstatd rpc.rstatd +#rstatd/1-3 dgram rpc/udp6 wait:100 nobody:kmem /usr/libexec/rpc.rstatd rpc.rstatd +#rusersd/2-3 dgram rpc/udp wait:100 nobody /usr/libexec/rpc.rusersd rpc.rusersd +#rusersd/2-3 dgram rpc/udp6 wait:100 nobody /usr/libexec/rpc.rusersd rpc.rusersd +#walld/1 dgram rpc/udp wait nobody:tty /usr/libexec/rpc.rwalld rpc.rwalld +#sprayd/1 dgram rpc/udp wait nobody /usr/libexec/rpc.sprayd rpc.sprayd +#rquotad/1-2 dgram rpc/udp wait root /usr/libexec/rpc.rquotad rpc.rquotad diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile index 783cd6975..d164b27a2 100755 --- a/etc/rc.d/Makefile +++ b/etc/rc.d/Makefile @@ -34,7 +34,7 @@ CONFIGFILES=\ fsck ftpd \ \ \ - ipfilter ipsec \ + inetd ipfilter ipsec \ \ \ local \ diff --git a/etc/rc.d/inetd b/etc/rc.d/inetd new file mode 100755 index 000000000..f63adbad8 --- /dev/null +++ b/etc/rc.d/inetd @@ -0,0 +1,20 @@ +#!/bin/sh +# +# $NetBSD: inetd,v 1.7 2004/08/13 18:08:03 mycroft Exp $ +# + +# PROVIDE: inetd +# REQUIRE: DAEMON LOGIN +# KEYWORD: shutdown + +$_rc_subr_loaded . /etc/rc.subr + +name="inetd" +rcvar=$name +command="/usr/sbin/${name}" +pidfile="/var/run/${name}.pid" +required_files="/etc/${name}.conf" +extra_commands="reload" + +load_rc_config $name +run_rc_command "$1" diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index cff8c7431..759cc4ebb 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -11,7 +11,7 @@ SUBDIR= arp \ \ \ \ - i2cscan installboot \ + i2cscan inetd installboot \ \ \ link \ diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile new file mode 100644 index 000000000..34e611f55 --- /dev/null +++ b/usr.sbin/inetd/Makefile @@ -0,0 +1,32 @@ +# from: @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $NetBSD: Makefile,v 1.23 2009/10/22 22:50:35 tsarna Exp $ + +.include + +USE_FORT?= yes # network server + +PROG= inetd +SRCS= inetd.c +MAN= inetd.8 +MLINKS= inetd.8 inetd.conf.5 + +CPPFLAGS+=-DLIBWRAP +# Use LIBWRAP_INTERNAL for libwrap checking of inetd's `internal' services. +#CPPFLAGS+=-DLIBWRAP_INTERNAL +LDADD+= -lwrap -lutil +DPADD+= ${LIBWRAP} ${LIBUTIL} + +.if (${USE_INET6} != "no") +CPPFLAGS+=-DINET6 +.endif + +.if !defined(__MINIX) +CPPFLAGS+=-DIPSEC +SRCS+= ipsec.c +LDADD+= -lipsec +DPADD+= ${LIBIPSEC} +.else # defined(__MINIX) +CPPFLAGS+=-DNO_RPC +.endif # defined(__MINIX) + +.include diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8 new file mode 100644 index 000000000..e20ca76e5 --- /dev/null +++ b/usr.sbin/inetd/inetd.8 @@ -0,0 +1,650 @@ +.\" $NetBSD: inetd.8,v 1.57 2011/04/25 22:12:05 wiz Exp $ +.\" +.\" Copyright (c) 1998 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, +.\" NASA Ames Research Center. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)inetd.8 8.4 (Berkeley) 6/1/94 +.\" +.Dd August 27, 2008 +.Dt INETD 8 +.Os +.Sh NAME +.Nm inetd , +.Nm inetd.conf +.Nd internet +.Dq super-server +.Sh SYNOPSIS +.Nm +.Op Fl d +.Op Fl l +.Op Ar configuration file +.Sh DESCRIPTION +.Nm +should be run at boot time by +.Pa /etc/rc +(see +.Xr rc 8 ) . +It then opens sockets according to its configuration and listens +for connections. +When a connection is found on one of its sockets, it decides what +service the socket corresponds to, and invokes a program to service +the request. +After the program is finished, it continues to listen on the socket +(except in some cases which will be described below). +Essentially, +.Nm +allows running one daemon to invoke several others, +reducing load on the system. +.Pp +The options available for +.Nm : +.Bl -tag -width Ds +.It Fl d +Turns on debugging. +.It Fl l +Turns on libwrap connection logging. +.El +.Pp +Upon execution, +.Nm +reads its configuration information from a configuration +file which, by default, is +.Pa /etc/inetd.conf . +The path given for this configuration file must be absolute, unless +the +.Fl d +option is also given on the command line. +There must be an entry for each field of the configuration +file, with entries for each field separated by a tab or +a space. +Comments are denoted by a ``#'' at the beginning of a line. +There must be an entry for each field (except for one +special case, described below). +The fields of the configuration file are as follows: +.Pp +.Bd -unfilled -offset indent -compact +[addr:]service-name +socket-type[:accept_filter] +protocol[,sndbuf=size][,rcvbuf=size] +wait/nowait[:max] +user[:group] +server-program +server program arguments +.Ed +.Pp +To specify an +.Em Sun-RPC +based service, the entry would contain these fields: +.Pp +.Bd -unfilled -offset indent -compact +service-name/version +socket-type +rpc/protocol[,sndbuf=size][,rcvbuf=size] +wait/nowait[:max] +user[:group] +server-program +server program arguments +.Ed +.Pp +To specify a UNIX-domain (local) socket, the entry would contain +these fields: +.Pp +.Bd -unfilled -offset indent -compact +path +socket-type +unix[,sndbuf=size][,rcvbuf=size] +wait/nowait[:max] +user[:group] +server-program +server program arguments +.Ed +.Pp +For Internet services, the first field of the line may also have a host +address specifier prefixed to it, separated from the service name by a colon. +If this is done, the string before the colon in the first field +indicates what local address +.Nm +should use when listening for that service, or the single character +.Dq \&* +to indicate +.Dv INADDR_ANY , +meaning +.Sq all local addresses . +To avoid repeating an address that occurs frequently, a line with a +host address specifier and colon, but no further fields, causes the +host address specifier to be remembered and used for all further lines +with no explicit host specifier (until another such line or the end of +the file). +A line +.Dl *: +is implicitly provided at the top of the file; thus, traditional +configuration files (which have no host address specifiers) will be +interpreted in the traditional manner, with all services listened for +on all local addresses. +.Pp +The +.Em service-name +entry is the name of a valid service in +the file +.Pa /etc/services . +For +.Dq internal +services (discussed below), the service +name +.Em must +be the official name of the service (that is, the first entry in +.Pa /etc/services ) . +When used to specify a +.Em Sun-RPC +based service, this field is a valid RPC service name in +the file +.Pa /etc/rpc . +The part on the right of the +.Dq / +is the RPC version number. +This can simply be a single numeric argument or a range of versions. +A range is bounded by the low version to the high version \- +.Dq rusers/1-3 . +.Pp +The +.Em socket-type +should be one of +.Dq stream , +.Dq dgram , +.Dq raw , +.Dq rdm , +or +.Dq seqpacket , +depending on whether the socket is a stream, datagram, raw, +reliably delivered message, or sequenced packet socket. +.Pp +Optionally, an +.Xr accept_filter 9 +can be specified by appending a colon to the socket-type, followed by +the name of the desired accept filter. +In this case +.Nm +will not see new connections for the specified service until the accept +filter decides they are ready to be handled. +.Pp +The +.Em protocol +must be a valid protocol as given in +.Pa /etc/protocols +or the string +.Dq unix . +Examples might be +.Dq tcp +and +.Dq udp . +Rpc based services are specified with the +.Dq rpc/tcp +or +.Dq rpc/udp +service type. +.Dq tcp +and +.Dq udp +will be recognized as +.Dq TCP or UDP over default IP version . +It is currently IPv4, but in the future it will be IPv6. +If you need to specify IPv4 or IPv6 explicitly, use something like +.Dq tcp4 +or +.Dq udp6 . +If you would like to enable special support for +.Xr faithd 8 , +prepend a keyword +.Dq faith +into +.Em protocol , +like +.Dq faith/tcp6 . +.Pp +In addition to the protocol, the configuration file may specify the +send and receive socket buffer sizes for the listening socket. +This is especially useful for +.Tn TCP +as the window scale factor, which is based on the receive socket +buffer size, is advertised when the connection handshake occurs, +thus the socket buffer size for the server must be set on the listen socket. +By increasing the socket buffer sizes, better +.Tn TCP +performance may be realized in some situations. +The socket buffer sizes are specified by appending their values to +the protocol specification as follows: +.Bd -literal -offset indent +tcp,rcvbuf=16384 +tcp,sndbuf=64k +tcp,rcvbuf=64k,sndbuf=1m +.Ed +.Pp +A literal value may be specified, or modified using +.Sq k +to indicate kilobytes or +.Sq m +to indicate megabytes. +Socket buffer sizes may be specified for all +services and protocols except for tcpmux services. +.Pp +The +.Em wait/nowait +entry is used to tell +.Nm +if it should wait for the server program to return, +or continue processing connections on the socket. +If a datagram server connects +to its peer, freeing the socket so +.Nm +can receive further messages on the socket, it is said to be +a +.Dq multi-threaded +server, and should use the +.Dq nowait +entry. +For datagram servers which process all incoming datagrams +on a socket and eventually time out, the server is said to be +.Dq single-threaded +and should use a +.Dq wait +entry. +.Xr comsat 8 +.Pq Xr biff 1 +and +.Xr ntalkd 8 +are both examples of the latter type of +datagram server. +.Xr tftpd 8 +is an exception; it is a datagram server that establishes pseudo-connections. +It must be listed as +.Dq wait +in order to avoid a race; +the server reads the first packet, creates a new socket, +and then forks and exits to allow +.Nm +to check for new service requests to spawn new servers. +The optional +.Dq max +suffix (separated from +.Dq wait +or +.Dq nowait +by a dot or a colon) specifies the maximum number of server instances that may +be spawned from +.Nm +within an interval of 60 seconds. +When omitted, +.Dq max +defaults to 40. +If it reaches this maximum spawn rate, +.Nm +will log the problem (via the syslogger using the +.Dv LOG_DAEMON +facility and +.Dv LOG_ERR +level) +and stop handling the specific service for ten minutes. +.Pp +Stream servers are usually marked as +.Dq nowait +but if a single server process is to handle multiple connections, it may be +marked as +.Dq wait . +The master socket will then be passed as fd 0 to the server, which will then +need to accept the incoming connection. +The server should eventually time +out and exit when no more connections are active. +.Nm +will continue to +listen on the master socket for connections, so the server should not close +it when it exits. +.Xr identd 8 +is usually the only stream server marked as wait. +.Pp +The +.Em user +entry should contain the user name of the user as whom the server should run. +This allows for servers to be given less permission than root. +Optionally, a group can be specified by appending a colon to the user name, +followed by the group name (it is possible to use a dot (``.'') in lieu of a +colon, however this feature is provided only for backward compatibility). +This allows for servers to run with a different (primary) group id than +specified in the password file. +If a group is specified and +.Em user +is not root, the supplementary groups associated with that user will still be +set. +.Pp +The +.Em server-program +entry should contain the pathname of the program which is to be +executed by +.Nm +when a request is found on its socket. +If +.Nm +provides this service internally, this entry should +be +.Dq internal . +.Pp +The +.Em server program arguments +should be just as arguments +normally are, starting with argv[0], which is the name of +the program. +If the service is provided internally, the +word +.Dq internal +should take the place of this entry. +It is possible to quote an argument using either single or double quotes. +This allows you to have, e.g., spaces in paths and parameters. +.Ss Internal Services +.Nm +provides several +.Qq trivial +services internally by use of routines within itself. +These services are +.Qq echo , +.Qq discard , +.Qq chargen +(character generator), +.Qq daytime +(human readable time), and +.Qq time +(machine readable time, +in the form of the number of seconds since midnight, January 1, 1900 GMT). +For details of these services, consult the appropriate +.Tn RFC . +.Pp +TCP services without official port numbers can be handled with the +RFC1078-based tcpmux internal service. +TCPmux listens on port 1 for requests. +When a connection is made from a foreign host, the service name +requested is passed to TCPmux, which performs a lookup in the +service name table provided by +.Pa /etc/inetd.conf +and returns the proper entry for the service. +TCPmux returns a negative reply if the service doesn't exist, +otherwise the invoked server is expected to return the positive +reply if the service type in +.Pa /etc/inetd.conf +file has the prefix +.Qq tcpmux/ . +If the service type has the +prefix +.Qq tcpmux/+ , +TCPmux will return the positive reply for the +process; this is for compatibility with older server code, and also +allows you to invoke programs that use stdin/stdout without putting any +special server code in them. +Services that use TCPmux are +.Qq nowait +because they do not have a well-known port number and hence cannot listen +for new requests. +.Pp +.Nm +rereads its configuration file when it receives a hangup signal, +.Dv SIGHUP . +Services may be added, deleted or modified when the configuration file +is reread. +.Nm +creates a file +.Em /var/run/inetd.pid +that contains its process identifier. +.Ss libwrap +Support for +.Tn TCP +wrappers is included with +.Nm +to provide internal tcpd-like access control functionality. +An external tcpd program is not needed. +You do not need to change the +.Pa /etc/inetd.conf +server-program entry to enable this capability. +.Nm +uses +.Pa /etc/hosts.allow +and +.Pa /etc/hosts.deny +for access control facility configurations, as described in +.Xr hosts_access 5 . +.Pp +.Em Nota Bene : +.Tn TCP +wrappers do not affect/restrict +.Tn UDP +or internal services. +.Ss IPsec +The implementation includes a tiny hack to support IPsec policy settings for +each socket. +A special form of the comment line, starting with +.Dq Li "#@" , +is used as a policy specifier. +The content of the above comment line will be treated as a IPsec policy string, +as described in +.Xr ipsec_set_policy 3 . +Multiple IPsec policy strings may be specified by using a semicolon +as a separator. +If conflicting policy strings are found in a single line, +the last string will take effect. +A +.Li "#@" +line affects all of the following lines in +.Pa /etc/inetd.conf , +so you may want to reset the IPsec policy by using a comment line containing +only +.Li "#@" +.Pq with no policy string . +.Pp +If an invalid IPsec policy string appears in +.Pa /etc/inetd.conf , +.Nm +logs an error message using +.Xr syslog 3 +and terminates itself. +.Ss IPv6 TCP/UDP behavior +If you wish to run a server for both IPv4 and IPv6 traffic, +you will need to run two separate processes for the same server program, +specified as two separate lines in +.Pa /etc/inetd.conf +using +.Dq tcp4 +and +.Dq tcp6 +respectively. +Plain +.Dq tcp +means TCP on top of the current default IP version, +which is, at this moment, IPv4. +.Pp +Under various combination of IPv4/v6 daemon settings, +.Nm +will behave as follows: +.Bl -bullet -compact +.It +If you have only one server on +.Dq tcp4 , +IPv4 traffic will be routed to the server. +IPv6 traffic will not be accepted. +.It +If you have two servers on +.Dq tcp4 +and +.Dq tcp6 , +IPv4 traffic will be routed to the server on +.Dq tcp4 , +and IPv6 traffic will go to server on +.Dq tcp6 . +.It +If you have only one server on +.Dq tcp6 , +only IPv6 traffic will be routed to the server. +The kernel may route to the server IPv4 traffic as well, +under certain configuration. +See +.Xr ip6 4 +for details. +.El +.Sh FILES +.Bl -tag -width /etc/hosts.allow -compact +.It Pa /etc/inetd.conf +configuration file for all +.Nm +provided services +.It Pa /etc/services +service name to protocol and port number mappings. +.It Pa /etc/protocols +protocol name to protocol number mappings +.It Pa /etc/rpc +.Tn Sun-RPC +service name to service number mappings. +.It Pa /etc/hosts.allow +explicit remote host access list. +.It Pa /etc/hosts.deny +explicit remote host denial of service list. +.El +.Sh SEE ALSO +.Xr hosts_access 5 , +.Xr hosts_options 5 , +.Xr protocols 5 , +.Xr rpc 5 , +.Xr services 5 , +.Xr comsat 8 , +.Xr fingerd 8 , +.Xr ftpd 8 , +.Xr rexecd 8 , +.Xr rlogind 8 , +.Xr rshd 8 , +.Xr telnetd 8 , +.Xr tftpd 8 +.Rs +.%A J. Postel +.%R RFC +.%N 862 +.%D May 1983 +.%T "Echo Protocol" +.Re +.Rs +.%A J. Postel +.%R RFC +.%N 863 +.%D May 1983 +.%T "Discard Protocol" +.Re +.Rs +.%A J. Postel +.%R RFC +.%N 864 +.%D May 1983 +.%T "Character Generator Protocol" +.Re +.Rs +.%A J. Postel +.%R RFC +.%N 867 +.%D May 1983 +.%T "Daytime Protocol" +.Re +.Rs +.%A J. Postel +.%A K. Harrenstien +.%R RFC +.%N 868 +.%D May 1983 +.%T "Time Protocol" +.Re +.Rs +.%A M. Lottor +.%R RFC +.%N 1078 +.%D November 1988 +.%T "TCP port service Multiplexer (TCPMUX)" +.Re +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . +Support for +.Em Sun-RPC +based services is modeled after that +provided by SunOS 4.1. +Support for specifying the socket buffer sizes was added in +.Nx 1.4 . +In November 1996, libwrap support was added to provide +internal tcpd-like access control functionality; +libwrap is based on Wietse Venema's tcp_wrappers. +IPv6 support and IPsec hack was made by KAME project, in 1999. +.Sh BUGS +Host address specifiers, while they make conceptual sense for RPC +services, do not work entirely correctly. +This is largely because the portmapper interface does not provide +a way to register different ports for the same service on different +local addresses. +Provided you never have more than one entry for a given RPC service, +everything should work correctly (Note that default host address +specifiers do apply to RPC lines with no explicit specifier.) +.Pp +.Dq tcpmux +on IPv6 is not tested enough. +.Sh SECURITY CONSIDERATIONS +Enabling the +.Dq echo , +.Dq discard , +and +.Dq chargen +built-in trivial services is not recommended because remote +users may abuse these to cause a denial of network service to +or from the local host. diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c new file mode 100644 index 000000000..92ff381c3 --- /dev/null +++ b/usr.sbin/inetd/inetd.c @@ -0,0 +1,2383 @@ +/* $NetBSD: inetd.c,v 1.122 2014/04/05 23:36:10 khorben Exp $ */ + +/*- + * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center and by Matthias Scheler. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1983, 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1983, 1991, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#if 0 +static char sccsid[] = "@(#)inetd.c 8.4 (Berkeley) 4/13/94"; +#else +__RCSID("$NetBSD: inetd.c,v 1.122 2014/04/05 23:36:10 khorben Exp $"); +#endif +#endif /* not lint */ + +/* + * Inetd - Internet super-server + * + * This program invokes all internet services as needed. Connection-oriented + * services are invoked each time a connection is made, by creating a process. + * This process is passed the connection as file descriptor 0 and is expected + * to do a getpeername to find out the source host and port. + * + * Datagram oriented services are invoked when a datagram + * arrives; a process is created and passed a pending message + * on file descriptor 0. Datagram servers may either connect + * to their peer, freeing up the original socket for inetd + * to receive further messages on, or ``take over the socket'', + * processing all arriving datagrams and, eventually, timing + * out. The first type of server is said to be ``multi-threaded''; + * the second type of server ``single-threaded''. + * + * Inetd uses a configuration file which is read at startup + * and, possibly, at some later time in response to a hangup signal. + * The configuration file is ``free format'' with fields given in the + * order shown below. Continuation lines for an entry must being with + * a space or tab. All fields must be present in each entry. + * + * service name must be in /etc/services or must + * name a tcpmux service + * socket type[:accf[,arg]] stream/dgram/raw/rdm/seqpacket, + only stream can name an accept filter + * protocol must be in /etc/protocols + * wait/nowait[:max] single-threaded/multi-threaded, max # + * user[:group] user/group to run daemon as + * server program full path name + * server program arguments maximum of MAXARGS (20) + * + * For RPC services + * service name/version must be in /etc/rpc + * socket type stream/dgram/raw/rdm/seqpacket + * protocol must be in /etc/protocols + * wait/nowait[:max] single-threaded/multi-threaded + * user[:group] user to run daemon as + * server program full path name + * server program arguments maximum of MAXARGS (20) + * + * For non-RPC services, the "service name" can be of the form + * hostaddress:servicename, in which case the hostaddress is used + * as the host portion of the address to listen on. If hostaddress + * consists of a single `*' character, INADDR_ANY is used. + * + * A line can also consist of just + * hostaddress: + * where hostaddress is as in the preceding paragraph. Such a line must + * have no further fields; the specified hostaddress is remembered and + * used for all further lines that have no hostaddress specified, + * until the next such line (or EOF). (This is why * is provided to + * allow explicit specification of INADDR_ANY.) A line + * *: + * is implicitly in effect at the beginning of the file. + * + * The hostaddress specifier may (and often will) contain dots; + * the service name must not. + * + * For RPC services, host-address specifiers are accepted and will + * work to some extent; however, because of limitations in the + * portmapper interface, it will not work to try to give more than + * one line for any given RPC service, even if the host-address + * specifiers are different. + * + * TCP services without official port numbers are handled with the + * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for + * requests. When a connection is made from a foreign host, the service + * requested is passed to tcpmux, which looks it up in the servtab list + * and returns the proper entry for the service. Tcpmux returns a + * negative reply if the service doesn't exist, otherwise the invoked + * server is expected to return the positive reply if the service type in + * inetd.conf file has the prefix "tcpmux/". If the service type has the + * prefix "tcpmux/+", tcpmux will return the positive reply for the + * process; this is for compatibility with older server code, and also + * allows you to invoke programs that use stdin/stdout without putting any + * special server code in them. Services that use tcpmux are "nowait" + * because they do not have a well-known port and hence cannot listen + * for new requests. + * + * Comment lines are indicated by a `#' in column 1. + * + * #ifdef IPSEC + * Comment lines that start with "#@" denote IPsec policy string, as described + * in ipsec_set_policy(3). This will affect all the following items in + * inetd.conf(8). To reset the policy, just use "#@" line. By default, + * there's no IPsec policy. + * #endif + */ + +/* + * Here's the scoop concerning the user:group feature: + * + * 1) set-group-option off. + * + * a) user = root: NO setuid() or setgid() is done + * + * b) other: setuid() + * setgid(primary group as found in passwd) + * initgroups(name, primary group) + * + * 2) set-group-option on. + * + * a) user = root: NO setuid() + * setgid(specified group) + * NO initgroups() + * + * b) other: setuid() + * setgid(specified group) + * initgroups(name, specified group) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NO_RPC +#define RPC +#endif + +#include + +#include +#include +#ifdef RPC +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +#ifdef IPSEC +#include +#ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */ +#undef IPSEC +#endif +#include "ipsec.h" +#endif + +#ifdef LIBWRAP +# include +#ifndef LIBWRAP_ALLOW_FACILITY +# define LIBWRAP_ALLOW_FACILITY LOG_AUTH +#endif +#ifndef LIBWRAP_ALLOW_SEVERITY +# define LIBWRAP_ALLOW_SEVERITY LOG_INFO +#endif +#ifndef LIBWRAP_DENY_FACILITY +# define LIBWRAP_DENY_FACILITY LOG_AUTH +#endif +#ifndef LIBWRAP_DENY_SEVERITY +# define LIBWRAP_DENY_SEVERITY LOG_WARNING +#endif +int allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY; +int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY; +#endif + +#define TOOMANY 40 /* don't start more than TOOMANY */ +#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ +#define RETRYTIME (60*10) /* retry after bind or server fail */ + +#define A_CNT(a) (sizeof (a) / sizeof (a[0])) + +int debug; +#ifdef LIBWRAP +int lflag; +#endif +int maxsock; +#ifndef __minix +int kq; +#else /* __minix */ +int sig_pipe[2]; +sigset_t sig_mask, old_mask; +#endif /* __minix */ +int options; +int timingout; +const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; + +#ifndef OPEN_MAX +#define OPEN_MAX 64 +#endif + +/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */ +#define FD_MARGIN (8) +rlim_t rlim_ofile_cur = OPEN_MAX; + +struct rlimit rlim_ofile; + +struct kevent changebuf[64]; +size_t changes; + +struct servtab { + char *se_hostaddr; /* host address to listen on */ + char *se_service; /* name of service */ + int se_socktype; /* type of socket to use */ + int se_family; /* address family */ + char *se_proto; /* protocol used */ + int se_sndbuf; /* sndbuf size */ + int se_rcvbuf; /* rcvbuf size */ + int se_rpcprog; /* rpc program number */ + int se_rpcversl; /* rpc program lowest version */ + int se_rpcversh; /* rpc program highest version */ +#define isrpcservice(sep) ((sep)->se_rpcversl != 0) + pid_t se_wait; /* single threaded server */ + short se_checked; /* looked at during merge */ + char *se_user; /* user name to run as */ + char *se_group; /* group name to run as */ + struct biltin *se_bi; /* if built-in, description */ + char *se_server; /* server program */ +#define MAXARGV 20 + char *se_argv[MAXARGV+1]; /* program arguments */ +#ifdef IPSEC + char *se_policy; /* IPsec poilcy string */ +#endif + struct accept_filter_arg se_accf; /* accept filter for stream service */ + int se_fd; /* open descriptor */ + int se_type; /* type */ + union { + struct sockaddr se_un_ctrladdr; + struct sockaddr_in se_un_ctrladdr_in; + struct sockaddr_in6 se_un_ctrladdr_in6; + struct sockaddr_un se_un_ctrladdr_un; + } se_un; /* bound address */ +#define se_ctrladdr se_un.se_un_ctrladdr +#define se_ctrladdr_in se_un.se_un_ctrladdr_in +#define se_ctrladdr_un se_un.se_un_ctrladdr_un + int se_ctrladdr_size; + int se_max; /* max # of instances of this service */ + int se_count; /* number started since se_time */ + struct timeval se_time; /* start of se_count */ + struct servtab *se_next; +} *servtab; + +#define NORM_TYPE 0 +#define MUX_TYPE 1 +#define MUXPLUS_TYPE 2 +#define FAITH_TYPE 3 +#define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \ + ((sep)->se_type == MUXPLUS_TYPE)) +#define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE) + + +static void chargen_dg(int, struct servtab *); +static void chargen_stream(int, struct servtab *); +static void close_sep(struct servtab *); +static void config(void); +static void daytime_dg(int, struct servtab *); +static void daytime_stream(int, struct servtab *); +static void discard_dg(int, struct servtab *); +static void discard_stream(int, struct servtab *); +static void echo_dg(int, struct servtab *); +static void echo_stream(int, struct servtab *); +static void endconfig(void); +static struct servtab *enter(struct servtab *); +static void freeconfig(struct servtab *); +static struct servtab *getconfigent(void); +__dead static void goaway(void); +static void machtime_dg(int, struct servtab *); +static void machtime_stream(int, struct servtab *); +static char *newstr(const char *); +static char *nextline(FILE *); +static void print_service(const char *, struct servtab *); +static void reapchild(void); +static void retry(void); +static void run_service(int, struct servtab *, int); +static int setconfig(void); +static void setup(struct servtab *); +static char *sskip(char **); +static char *skip(char **); +static void tcpmux(int, struct servtab *); +__dead static void usage(void); +static void register_rpc(struct servtab *); +static void unregister_rpc(struct servtab *); +static void bump_nofile(void); +static void inetd_setproctitle(char *, int); +static void initring(void); +static uint32_t machtime(void); +static int port_good_dg(struct sockaddr *); +static int dg_broadcast(struct in_addr *); +#ifndef __minix +static int my_kevent(const struct kevent *, size_t, struct kevent *, + size_t); +static struct kevent * allocchange(void); +#endif /* !__minix */ +static int get_line(int, char *, int); +static void spawn(struct servtab *, int); + +struct biltin { + const char *bi_service; /* internally provided service name */ + int bi_socktype; /* type of socket supported */ + short bi_fork; /* 1 if should fork before call */ + short bi_wait; /* 1 if should wait for child */ + void (*bi_fn)(int, struct servtab *); + /* function which performs it */ +} biltins[] = { + /* Echo received data */ + { "echo", SOCK_STREAM, 1, 0, echo_stream }, + { "echo", SOCK_DGRAM, 0, 0, echo_dg }, + + /* Internet /dev/null */ + { "discard", SOCK_STREAM, 1, 0, discard_stream }, + { "discard", SOCK_DGRAM, 0, 0, discard_dg }, + + /* Return 32 bit time since 1970 */ + { "time", SOCK_STREAM, 0, 0, machtime_stream }, + { "time", SOCK_DGRAM, 0, 0, machtime_dg }, + + /* Return human-readable time */ + { "daytime", SOCK_STREAM, 0, 0, daytime_stream }, + { "daytime", SOCK_DGRAM, 0, 0, daytime_dg }, + + /* Familiar character generator */ + { "chargen", SOCK_STREAM, 1, 0, chargen_stream }, + { "chargen", SOCK_DGRAM, 0, 0, chargen_dg }, + + { "tcpmux", SOCK_STREAM, 1, 0, tcpmux }, + + { NULL, 0, 0, 0, NULL } +}; + +/* list of "bad" ports. I.e. ports that are most obviously used for + * "cycling packets" denial of service attacks. See /etc/services. + * List must end with port number "0". + */ + +u_int16_t bad_ports[] = { 7, 9, 13, 19, 37, 0 }; + + +#define NUMINT (sizeof(intab) / sizeof(struct inent)) +const char *CONFIG = _PATH_INETDCONF; + +static int my_signals[] = + { SIGALRM, SIGHUP, SIGCHLD, SIGTERM, SIGINT, SIGPIPE }; + +#ifdef __minix +/* + * NetBSD uses kqueue to catch signals, while (explicitly) ignoring them at the + * process level. We (MINIX3) do catch the signals at the process level, + * instead sending them into select() using a pipe (djb's self-pipe trick). + * That is safe, except it may interrupt system calls other than our select(), + * so we also have to set appropriate signal masks (clearing them upon fork). + */ +static void +got_signal(int sig) +{ + + (void) write(sig_pipe[1], &sig, sizeof(sig)); +} +#endif /* __minix */ + +int +main(int argc, char *argv[]) +{ + int ch, n, reload = 1; + + while ((ch = getopt(argc, argv, +#ifdef LIBWRAP + "dl" +#else + "d" +#endif + )) != -1) + switch(ch) { + case 'd': + debug = 1; + options |= SO_DEBUG; + break; +#ifdef LIBWRAP + case 'l': + lflag = 1; + break; +#endif + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc > 0) + CONFIG = argv[0]; + + if (!debug) + daemon(0, 0); + openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); + pidfile(NULL); + +#ifndef __minix + kq = kqueue(); + if (kq < 0) { + syslog(LOG_ERR, "kqueue: %m"); + return (EXIT_FAILURE); + } +#else /* __minix */ + if (pipe2(sig_pipe, O_CLOEXEC | O_NONBLOCK) != 0) { + syslog(LOG_ERR, "pipe2: %m"); + return (EXIT_FAILURE); + } + + /* Block all signals until the first select() call.. just easier. */ + sigfillset(&sig_mask); + (void) sigprocmask(SIG_SETMASK, &sig_mask, &old_mask); + sig_mask = old_mask; +#endif /* __minix */ + + if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { + syslog(LOG_ERR, "getrlimit: %m"); + } else { + rlim_ofile_cur = rlim_ofile.rlim_cur; + if (rlim_ofile_cur == RLIM_INFINITY) /* ! */ + rlim_ofile_cur = OPEN_MAX; + } + + for (n = 0; n < (int)A_CNT(my_signals); n++) { + int signum; + + signum = my_signals[n]; +#ifndef __minix + if (signum != SIGCHLD) + (void) signal(signum, SIG_IGN); + + if (signum != SIGPIPE) { + struct kevent *ev; + + ev = allocchange(); + EV_SET(ev, signum, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, + 0, 0, 0); + } +#else /* __minix */ + /* The above code ignores but does not "catch" SIGPIPE. */ + if (signum != SIGPIPE) + (void) signal(signum, got_signal); + else + (void) signal(signum, SIG_IGN); + sigaddset(&sig_mask, signum); +#endif /* __minix */ + } + + for (;;) { + int ctrl; +#ifndef __minix + struct kevent eventbuf[64], *ev; +#else + fd_set fds; + int sig, highfd; +#endif /* !__minix */ + struct servtab *sep; + + if (reload) { + reload = 0; + config(); + } + +#ifdef __minix + FD_ZERO(&fds); + FD_SET(sig_pipe[0], &fds); + highfd = sig_pipe[0]; + + for (sep = servtab; sep != NULL; sep = sep->se_next) + if (sep->se_fd != -1 && (unsigned)sep->se_wait <= 1) { + FD_SET(sep->se_fd, &fds); + if (highfd < sep->se_fd) + highfd = sep->se_fd; + } + + /* + * Unblock all the signals we want to catch for the duration of + * the select() call. We do not yet have pselect(), but the + * lack of atomicity does not affect correctness here, because + * all the signals go through the pipe anyway--that is also why + * we reissue the select() even if we did catch a signal. + */ + (void) sigprocmask(SIG_SETMASK, &old_mask, NULL); + + while (select(highfd + 1, &fds, NULL, NULL, NULL) == -1 && + errno == EINTR); + + (void) sigprocmask(SIG_SETMASK, &sig_mask, NULL); + + if (FD_ISSET(sig_pipe[0], &fds)) { + while (read(sig_pipe[0], &sig, sizeof(sig)) != -1) { + switch (sig) { +#else /* !__minix */ + n = my_kevent(changebuf, changes, eventbuf, A_CNT(eventbuf)); + changes = 0; + + for (ev = eventbuf; n > 0; ev++, n--) { + if (ev->filter == EVFILT_SIGNAL) { + switch (ev->ident) { +#endif /* !__minix */ + case SIGALRM: + retry(); + break; + case SIGCHLD: + reapchild(); + break; + case SIGTERM: + case SIGINT: + goaway(); + break; + case SIGHUP: + reload = 1; + break; + } + continue; + } +#ifdef __minix + } + + for (sep = servtab; sep != NULL; sep = sep->se_next) { + if (sep->se_fd == -1 || (unsigned)sep->se_wait > 1 || + !FD_ISSET(sep->se_fd, &fds)) + continue; +#else /* !__minix */ + if (ev->filter != EVFILT_READ) + continue; + sep = (struct servtab *)ev->udata; + /* Paranoia */ + if ((int)ev->ident != sep->se_fd) + continue; +#endif /* !__minix */ + if (debug) + fprintf(stderr, "someone wants %s\n", + sep->se_service); + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { + /* XXX here do the libwrap check-before-accept*/ + ctrl = accept(sep->se_fd, NULL, NULL); + if (debug) + fprintf(stderr, "accept, ctrl %d\n", + ctrl); + if (ctrl < 0) { + if (errno != EINTR) + syslog(LOG_WARNING, + "accept (for %s): %m", + sep->se_service); + continue; + } + } else + ctrl = sep->se_fd; + spawn(sep, ctrl); + } + } +} + +static void +spawn(struct servtab *sep, int ctrl) +{ + int dofork; + pid_t pid; + + pid = 0; +#ifdef LIBWRAP_INTERNAL + dofork = 1; +#else + dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork); +#endif + if (dofork) { + if (sep->se_count++ == 0) + (void)gettimeofday(&sep->se_time, NULL); + else if (sep->se_count >= sep->se_max) { + struct timeval now; + + (void)gettimeofday(&now, NULL); + if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) { + sep->se_time = now; + sep->se_count = 1; + } else { + syslog(LOG_ERR, + "%s/%s max spawn rate (%d in %d seconds) " + "exceeded; service not started", + sep->se_service, sep->se_proto, + sep->se_max, CNT_INTVL); + if (!sep->se_wait && sep->se_socktype == + SOCK_STREAM) + close(ctrl); + close_sep(sep); + if (!timingout) { + timingout = 1; + alarm(RETRYTIME); + } + return; + } + } + pid = fork(); + if (pid < 0) { + syslog(LOG_ERR, "fork: %m"); + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) + close(ctrl); + sleep(1); + return; + } + if (pid != 0 && sep->se_wait) { +#ifndef __minix + struct kevent *ev; + + sep->se_wait = pid; + ev = allocchange(); + EV_SET(ev, sep->se_fd, EVFILT_READ, + EV_DELETE, 0, 0, 0); +#endif /* !__minix */ + } + if (pid == 0) { + size_t n; + + for (n = 0; n < A_CNT(my_signals); n++) + (void) signal(my_signals[n], SIG_DFL); +#ifdef __minix + close(sig_pipe[0]); + close(sig_pipe[1]); + + (void) sigprocmask(SIG_SETMASK, &old_mask, NULL); +#endif /* __minix */ + if (debug) + setsid(); + } + } + if (pid == 0) { + run_service(ctrl, sep, dofork); + if (dofork) + exit(0); + } + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) + close(ctrl); +} + +static void +run_service(int ctrl, struct servtab *sep, int didfork) +{ + struct passwd *pwd; + struct group *grp = NULL; /* XXX gcc */ + char buf[NI_MAXSERV]; + struct servtab *s; +#ifdef LIBWRAP + char abuf[BUFSIZ]; + struct request_info req; + int denied; + char *service = NULL; /* XXX gcc */ +#endif + +#ifdef LIBWRAP +#ifndef LIBWRAP_INTERNAL + if (sep->se_bi == 0) +#endif + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { + request_init(&req, RQ_DAEMON, sep->se_argv[0] ? + sep->se_argv[0] : sep->se_service, RQ_FILE, ctrl, NULL); + fromhost(&req); + denied = !hosts_access(&req); + if (denied || lflag) { + if (getnameinfo(&sep->se_ctrladdr, + (socklen_t)sep->se_ctrladdr.sa_len, NULL, 0, + buf, sizeof(buf), 0) != 0) { + /* shouldn't happen */ + (void)snprintf(buf, sizeof buf, "%d", + ntohs(sep->se_ctrladdr_in.sin_port)); + } + service = buf; + if (req.client->sin) { + sockaddr_snprintf(abuf, sizeof(abuf), "%a", + req.client->sin); + } else { + strcpy(abuf, "(null)"); + } + } + if (denied) { + syslog(deny_severity, + "refused connection from %.500s(%s), service %s (%s)", + eval_client(&req), abuf, service, sep->se_proto); + goto reject; + } + if (lflag) { + syslog(allow_severity, + "connection from %.500s(%s), service %s (%s)", + eval_client(&req), abuf, service, sep->se_proto); + } + } +#endif /* LIBWRAP */ + + if (sep->se_bi) { + if (didfork) { + for (s = servtab; s; s = s->se_next) + if (s->se_fd != -1 && s->se_fd != ctrl) { + close(s->se_fd); + s->se_fd = -1; + } + } + (*sep->se_bi->bi_fn)(ctrl, sep); + } else { + if ((pwd = getpwnam(sep->se_user)) == NULL) { + syslog(LOG_ERR, "%s/%s: %s: No such user", + sep->se_service, sep->se_proto, sep->se_user); + goto reject; + } + if (sep->se_group && + (grp = getgrnam(sep->se_group)) == NULL) { + syslog(LOG_ERR, "%s/%s: %s: No such group", + sep->se_service, sep->se_proto, sep->se_group); + goto reject; + } + if (pwd->pw_uid) { + if (sep->se_group) + pwd->pw_gid = grp->gr_gid; + if (setgid(pwd->pw_gid) < 0) { + syslog(LOG_ERR, + "%s/%s: can't set gid %d: %m", sep->se_service, + sep->se_proto, pwd->pw_gid); + goto reject; + } + (void) initgroups(pwd->pw_name, + pwd->pw_gid); + if (setuid(pwd->pw_uid) < 0) { + syslog(LOG_ERR, + "%s/%s: can't set uid %d: %m", sep->se_service, + sep->se_proto, pwd->pw_uid); + goto reject; + } + } else if (sep->se_group) { + (void) setgid((gid_t)grp->gr_gid); + } + if (debug) + fprintf(stderr, "%d execl %s\n", + getpid(), sep->se_server); + /* Set our control descriptor to not close-on-exec... */ + if (fcntl(ctrl, F_SETFD, 0) < 0) + syslog(LOG_ERR, "fcntl (%d, F_SETFD, 0): %m", ctrl); + /* ...and dup it to stdin, stdout, and stderr. */ + if (ctrl != 0) { + dup2(ctrl, 0); + close(ctrl); + ctrl = 0; + } + dup2(0, 1); + dup2(0, 2); + if (rlim_ofile.rlim_cur != rlim_ofile_cur && + setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) + syslog(LOG_ERR, "setrlimit: %m"); + execv(sep->se_server, sep->se_argv); + syslog(LOG_ERR, "cannot execute %s: %m", sep->se_server); + reject: + if (sep->se_socktype != SOCK_STREAM) + recv(ctrl, buf, sizeof (buf), 0); + _exit(1); + } +} + +static void +reapchild(void) +{ + int status; + pid_t pid; + struct servtab *sep; + + for (;;) { + pid = wait3(&status, WNOHANG, NULL); + if (pid <= 0) + break; + if (debug) + (void) fprintf(stderr, "%d reaped, status %#x\n", + pid, status); + for (sep = servtab; sep != NULL; sep = sep->se_next) + if (sep->se_wait == pid) { +#ifndef __minix + struct kevent *ev; +#endif /* !__minix */ + + if (WIFEXITED(status) && WEXITSTATUS(status)) + syslog(LOG_WARNING, + "%s: exit status %u", + sep->se_server, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + syslog(LOG_WARNING, + "%s: exit signal %u", + sep->se_server, WTERMSIG(status)); + sep->se_wait = 1; +#ifndef __minix + ev = allocchange(); + EV_SET(ev, sep->se_fd, EVFILT_READ, + EV_ADD | EV_ENABLE, 0, 0, (intptr_t)sep); +#endif /* !__minix */ + if (debug) + fprintf(stderr, "restored %s, fd %d\n", + sep->se_service, sep->se_fd); + } + } +} + +static void +config(void) +{ + struct servtab *sep, *cp, **sepp; + size_t n; + + if (!setconfig()) { + syslog(LOG_ERR, "%s: %m", CONFIG); + return; + } + for (sep = servtab; sep != NULL; sep = sep->se_next) + sep->se_checked = 0; + while ((cp = getconfigent()) != NULL) { + for (sep = servtab; sep != NULL; sep = sep->se_next) + if (strcmp(sep->se_service, cp->se_service) == 0 && + strcmp(sep->se_hostaddr, cp->se_hostaddr) == 0 && + strcmp(sep->se_proto, cp->se_proto) == 0 && + ISMUX(sep) == ISMUX(cp)) + break; + if (sep != NULL) { + int i; + +#define SWAP(type, a, b) {type c = a; a = b; b = c;} + + /* + * sep->se_wait may be holding the pid of a daemon + * that we're waiting for. If so, don't overwrite + * it unless the config file explicitly says don't + * wait. + */ + if (cp->se_bi == 0 && + (sep->se_wait == 1 || cp->se_wait == 0)) + sep->se_wait = cp->se_wait; + SWAP(char *, sep->se_user, cp->se_user); + SWAP(char *, sep->se_group, cp->se_group); + SWAP(char *, sep->se_server, cp->se_server); + for (i = 0; i < MAXARGV; i++) + SWAP(char *, sep->se_argv[i], cp->se_argv[i]); +#ifdef IPSEC + SWAP(char *, sep->se_policy, cp->se_policy); +#endif + SWAP(int, cp->se_type, sep->se_type); + SWAP(int, cp->se_max, sep->se_max); +#undef SWAP + if (isrpcservice(sep)) + unregister_rpc(sep); + sep->se_rpcversl = cp->se_rpcversl; + sep->se_rpcversh = cp->se_rpcversh; + freeconfig(cp); + if (debug) + print_service("REDO", sep); + } else { + sep = enter(cp); + if (debug) + print_service("ADD ", sep); + } + sep->se_checked = 1; + + switch (sep->se_family) { + case AF_LOCAL: + if (sep->se_fd != -1) + break; + n = strlen(sep->se_service); + if (n >= sizeof(sep->se_ctrladdr_un.sun_path)) { + syslog(LOG_ERR, "%s: address too long", + sep->se_service); + sep->se_checked = 0; + continue; + } + (void)unlink(sep->se_service); + strlcpy(sep->se_ctrladdr_un.sun_path, + sep->se_service, n); + sep->se_ctrladdr_un.sun_family = AF_LOCAL; + sep->se_ctrladdr_size = (int)(n + + sizeof(sep->se_ctrladdr_un) - + sizeof(sep->se_ctrladdr_un.sun_path)); + if (!ISMUX(sep)) + setup(sep); + break; + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + { + struct addrinfo hints, *res; + char *host; + const char *port; + int error; + int s; + + /* check if the family is supported */ + s = socket(sep->se_family, SOCK_DGRAM, 0); + if (s < 0) { + syslog(LOG_WARNING, + "%s/%s: %s: the address family is not " + "supported by the kernel", + sep->se_service, sep->se_proto, + sep->se_hostaddr); + sep->se_checked = 0; + continue; + } + close(s); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = sep->se_family; + hints.ai_socktype = sep->se_socktype; + hints.ai_flags = AI_PASSIVE; + if (!strcmp(sep->se_hostaddr, "*")) + host = NULL; + else + host = sep->se_hostaddr; + if (isrpcservice(sep) || ISMUX(sep)) + port = "0"; + else + port = sep->se_service; + error = getaddrinfo(host, port, &hints, &res); + if (error) { + if (error == EAI_SERVICE) { + /* gai_strerror not friendly enough */ + syslog(LOG_WARNING, "%s/%s: " + "unknown service", + sep->se_service, sep->se_proto); + } else { + syslog(LOG_ERR, "%s/%s: %s: %s", + sep->se_service, sep->se_proto, + sep->se_hostaddr, + gai_strerror(error)); + } + sep->se_checked = 0; + continue; + } + if (res->ai_next) { + syslog(LOG_ERR, + "%s/%s: %s: resolved to multiple addr", + sep->se_service, sep->se_proto, + sep->se_hostaddr); + sep->se_checked = 0; + freeaddrinfo(res); + continue; + } + memcpy(&sep->se_ctrladdr, res->ai_addr, + res->ai_addrlen); + if (ISMUX(sep)) { + sep->se_fd = -1; + freeaddrinfo(res); + continue; + } + sep->se_ctrladdr_size = res->ai_addrlen; + freeaddrinfo(res); +#ifdef RPC + if (isrpcservice(sep)) { + struct rpcent *rp; + + sep->se_rpcprog = atoi(sep->se_service); + if (sep->se_rpcprog == 0) { + rp = getrpcbyname(sep->se_service); + if (rp == 0) { + syslog(LOG_ERR, + "%s/%s: unknown service", + sep->se_service, + sep->se_proto); + sep->se_checked = 0; + continue; + } + sep->se_rpcprog = rp->r_number; + } + if (sep->se_fd == -1 && !ISMUX(sep)) + setup(sep); + if (sep->se_fd != -1) + register_rpc(sep); + } else +#endif + { + if (sep->se_fd >= 0) + close_sep(sep); + if (sep->se_fd == -1 && !ISMUX(sep)) + setup(sep); + } + } + } + } + endconfig(); + /* + * Purge anything not looked at above. + */ + sepp = &servtab; + while ((sep = *sepp) != NULL) { + if (sep->se_checked) { + sepp = &sep->se_next; + continue; + } + *sepp = sep->se_next; + if (sep->se_fd >= 0) + close_sep(sep); + if (isrpcservice(sep)) + unregister_rpc(sep); + if (sep->se_family == AF_LOCAL) + (void)unlink(sep->se_service); + if (debug) + print_service("FREE", sep); + freeconfig(sep); + free(sep); + } +} + +static void +retry(void) +{ + struct servtab *sep; + + timingout = 0; + for (sep = servtab; sep != NULL; sep = sep->se_next) { + if (sep->se_fd == -1 && !ISMUX(sep)) { + switch (sep->se_family) { + case AF_LOCAL: + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + setup(sep); + if (sep->se_fd >= 0 && isrpcservice(sep)) + register_rpc(sep); + break; + } + } + } +} + +static void +goaway(void) +{ + struct servtab *sep; + + for (sep = servtab; sep != NULL; sep = sep->se_next) { + if (sep->se_fd == -1) + continue; + + switch (sep->se_family) { + case AF_LOCAL: + (void)unlink(sep->se_service); + break; + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + if (sep->se_wait == 1 && isrpcservice(sep)) + unregister_rpc(sep); + break; + } + (void)close(sep->se_fd); + sep->se_fd = -1; + } + exit(0); +} + +static void +setup(struct servtab *sep) +{ + int on = 1; +#ifdef INET6 + int off = 0; +#endif +#ifndef __minix + struct kevent *ev; +#endif /* !__minix */ + + if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { + if (debug) + fprintf(stderr, "socket failed on %s/%s: %s\n", + sep->se_service, sep->se_proto, strerror(errno)); + syslog(LOG_ERR, "%s/%s: socket: %m", + sep->se_service, sep->se_proto); + return; + } + /* Set all listening sockets to close-on-exec. */ + if (fcntl(sep->se_fd, F_SETFD, FD_CLOEXEC) < 0) { + syslog(LOG_ERR, "%s/%s: fcntl(F_SETFD, FD_CLOEXEC): %m", + sep->se_service, sep->se_proto); + close(sep->se_fd); + sep->se_fd = -1; + return; + } + +#define turnon(fd, opt) \ +setsockopt(fd, SOL_SOCKET, opt, &on, (socklen_t)sizeof(on)) + if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && + turnon(sep->se_fd, SO_DEBUG) < 0) + syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); + if (turnon(sep->se_fd, SO_REUSEADDR) < 0) + syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); +#undef turnon + + /* Set the socket buffer sizes, if specified. */ + if (sep->se_sndbuf != 0 && setsockopt(sep->se_fd, SOL_SOCKET, + SO_SNDBUF, &sep->se_sndbuf, (socklen_t)sizeof(sep->se_sndbuf)) < 0) + syslog(LOG_ERR, "setsockopt (SO_SNDBUF %d): %m", + sep->se_sndbuf); + if (sep->se_rcvbuf != 0 && setsockopt(sep->se_fd, SOL_SOCKET, + SO_RCVBUF, &sep->se_rcvbuf, (socklen_t)sizeof(sep->se_rcvbuf)) < 0) + syslog(LOG_ERR, "setsockopt (SO_RCVBUF %d): %m", + sep->se_rcvbuf); +#ifdef INET6 + if (sep->se_family == AF_INET6) { + int *v; + v = (sep->se_type == FAITH_TYPE) ? &on : &off; + if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH, + v, (socklen_t)sizeof(*v)) < 0) + syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m"); + } +#endif +#ifdef IPSEC + if (ipsecsetup(sep->se_family, sep->se_fd, sep->se_policy) < 0 && + sep->se_policy) { + syslog(LOG_ERR, "%s/%s: ipsec setup failed", + sep->se_service, sep->se_proto); + (void)close(sep->se_fd); + sep->se_fd = -1; + return; + } +#endif + + if (bind(sep->se_fd, &sep->se_ctrladdr, + (socklen_t)sep->se_ctrladdr_size) < 0) { + if (debug) + fprintf(stderr, "bind failed on %s/%s: %s\n", + sep->se_service, sep->se_proto, strerror(errno)); + syslog(LOG_ERR, "%s/%s: bind: %m", + sep->se_service, sep->se_proto); + (void) close(sep->se_fd); + sep->se_fd = -1; + if (!timingout) { + timingout = 1; + alarm(RETRYTIME); + } + return; + } + if (sep->se_socktype == SOCK_STREAM) + listen(sep->se_fd, 10); + + /* Set the accept filter, if specified. To be done after listen.*/ + if (sep->se_accf.af_name[0] != 0 && setsockopt(sep->se_fd, SOL_SOCKET, + SO_ACCEPTFILTER, &sep->se_accf, + (socklen_t)sizeof(sep->se_accf)) < 0) + syslog(LOG_ERR, "setsockopt(SO_ACCEPTFILTER %s): %m", + sep->se_accf.af_name); + +#ifndef __minix + ev = allocchange(); + EV_SET(ev, sep->se_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, + (intptr_t)sep); +#endif /* !__minix */ + if (sep->se_fd > maxsock) { + maxsock = sep->se_fd; + if (maxsock > (int)(rlim_ofile_cur - FD_MARGIN)) + bump_nofile(); + } + if (debug) + fprintf(stderr, "registered %s on %d\n", + sep->se_server, sep->se_fd); +} + +/* + * Finish with a service and its socket. + */ +static void +close_sep(struct servtab *sep) +{ + if (sep->se_fd >= 0) { + (void) close(sep->se_fd); + sep->se_fd = -1; + } + sep->se_count = 0; +} + +static void +register_rpc(struct servtab *sep) +{ +#ifdef RPC + struct netbuf nbuf; + struct sockaddr_storage ss; + struct netconfig *nconf; + socklen_t socklen; + int n; + + if ((nconf = getnetconfigent(sep->se_proto+4)) == NULL) { + syslog(LOG_ERR, "%s: getnetconfigent failed", + sep->se_proto); + return; + } + socklen = sizeof ss; + if (getsockname(sep->se_fd, (struct sockaddr *)(void *)&ss, &socklen) < 0) { + syslog(LOG_ERR, "%s/%s: getsockname: %m", + sep->se_service, sep->se_proto); + return; + } + + nbuf.buf = &ss; + nbuf.len = ss.ss_len; + nbuf.maxlen = sizeof (struct sockaddr_storage); + for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) { + if (debug) + fprintf(stderr, "rpcb_set: %u %d %s %s\n", + sep->se_rpcprog, n, nconf->nc_netid, + taddr2uaddr(nconf, &nbuf)); + (void)rpcb_unset((unsigned int)sep->se_rpcprog, (unsigned int)n, nconf); + if (!rpcb_set((unsigned int)sep->se_rpcprog, (unsigned int)n, nconf, &nbuf)) + syslog(LOG_ERR, "rpcb_set: %u %d %s %s%s", + sep->se_rpcprog, n, nconf->nc_netid, + taddr2uaddr(nconf, &nbuf), clnt_spcreateerror("")); + } +#endif /* RPC */ +} + +static void +unregister_rpc(struct servtab *sep) +{ +#ifdef RPC + int n; + struct netconfig *nconf; + + if ((nconf = getnetconfigent(sep->se_proto+4)) == NULL) { + syslog(LOG_ERR, "%s: getnetconfigent failed", + sep->se_proto); + return; + } + + for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) { + if (debug) + fprintf(stderr, "rpcb_unset(%u, %d, %s)\n", + sep->se_rpcprog, n, nconf->nc_netid); + if (!rpcb_unset((unsigned int)sep->se_rpcprog, (unsigned int)n, nconf)) + syslog(LOG_ERR, "rpcb_unset(%u, %d, %s) failed\n", + sep->se_rpcprog, n, nconf->nc_netid); + } +#endif /* RPC */ +} + + +static struct servtab * +enter(struct servtab *cp) +{ + struct servtab *sep; + + sep = malloc(sizeof (*sep)); + if (sep == NULL) { + syslog(LOG_ERR, "Out of memory."); + exit(1); + } + *sep = *cp; + sep->se_fd = -1; + sep->se_rpcprog = -1; + sep->se_next = servtab; + servtab = sep; + return (sep); +} + +FILE *fconfig = NULL; +struct servtab serv; +char line[LINE_MAX]; +char *defhost; +#ifdef IPSEC +static char *policy = NULL; +#endif + +static int +setconfig(void) +{ + if (defhost) + free(defhost); + defhost = newstr("*"); +#ifdef IPSEC + if (policy) + free(policy); + policy = NULL; +#endif + if (fconfig != NULL) { + fseek(fconfig, 0L, SEEK_SET); + return (1); + } + fconfig = fopen(CONFIG, "r"); + return (fconfig != NULL); +} + +static void +endconfig(void) +{ + if (fconfig != NULL) { + (void) fclose(fconfig); + fconfig = NULL; + } + if (defhost != NULL) { + free(defhost); + defhost = NULL; + } +} + +static struct servtab * +getconfigent(void) +{ + struct servtab *sep = &serv; + int argc, val; + char *cp, *cp0, *arg, *buf0, *buf1, *sz0, *sz1; + static char TCPMUX_TOKEN[] = "tcpmux/"; +#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1) + char *hostdelim; + +more: + while ((cp = nextline(fconfig)) != NULL) { +#ifdef IPSEC + /* lines starting with #@ is not a comment, but the policy */ + if (cp[0] == '#' && cp[1] == '@') { + char *p; + for (p = cp + 2; p && *p && isspace((unsigned char)*p); p++) + ; + if (*p == '\0') { + if (policy) + free(policy); + policy = NULL; + } else { + if (ipsecsetup_test(p) < 0) { + syslog(LOG_ERR, + "%s: invalid ipsec policy \"%s\"", + CONFIG, p); + exit(1); + } else { + if (policy) + free(policy); + policy = newstr(p); + } + } + } +#endif + if (*cp == '#' || *cp == '\0') + continue; + break; + } + if (cp == NULL) + return (NULL); + /* + * clear the static buffer, since some fields (se_ctrladdr, + * for example) don't get initialized here. + */ + memset(sep, 0, sizeof *sep); + arg = skip(&cp); + if (cp == NULL) { + /* got an empty line containing just blanks/tabs. */ + goto more; + } + /* Check for a host name. */ + hostdelim = strrchr(arg, ':'); + if (hostdelim) { + *hostdelim = '\0'; + if (arg[0] == '[' && hostdelim > arg && hostdelim[-1] == ']') { + hostdelim[-1] = '\0'; + sep->se_hostaddr = newstr(arg + 1); + } else + sep->se_hostaddr = newstr(arg); + arg = hostdelim + 1; + /* + * If the line is of the form `host:', then just change the + * default host for the following lines. + */ + if (*arg == '\0') { + arg = skip(&cp); + if (cp == NULL) { + free(defhost); + defhost = sep->se_hostaddr; + goto more; + } + } + } else + sep->se_hostaddr = newstr(defhost); + if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { + char *c = arg + MUX_LEN; + if (*c == '+') { + sep->se_type = MUXPLUS_TYPE; + c++; + } else + sep->se_type = MUX_TYPE; + sep->se_service = newstr(c); + } else { + sep->se_service = newstr(arg); + sep->se_type = NORM_TYPE; + } + + arg = sskip(&cp); + if (strncmp(arg, "stream", sizeof("stream") - 1) == 0) { + char *accf, *accf_arg; + + sep->se_socktype = SOCK_STREAM; + + /* one and only one accept filter */ + accf = strchr(arg, ':'); + if (accf) { + if (accf != strrchr(arg, ':') ||/* more than one */ + *(accf + 1) == '\0') { /* nothing beyond */ + sep->se_socktype = -1; + } else { + accf++; /* skip delimiter */ + strlcpy(sep->se_accf.af_name, accf, + sizeof(sep->se_accf.af_name)); + accf_arg = strchr(accf, ','); + if (accf_arg) { /* zero or one arg, no more */ + if (strrchr(accf, ',') != accf_arg) { + sep->se_socktype = -1; + } else { + accf_arg++; + strlcpy(sep->se_accf.af_arg, + accf_arg, + sizeof(sep->se_accf.af_arg)); + } + } + } + } + } + + else if (strcmp(arg, "dgram") == 0) + sep->se_socktype = SOCK_DGRAM; + else if (strcmp(arg, "rdm") == 0) + sep->se_socktype = SOCK_RDM; + else if (strcmp(arg, "seqpacket") == 0) + sep->se_socktype = SOCK_SEQPACKET; + else if (strcmp(arg, "raw") == 0) + sep->se_socktype = SOCK_RAW; + else + sep->se_socktype = -1; + + arg = sskip(&cp); + if (sep->se_type == NORM_TYPE && + strncmp(arg, "faith/", strlen("faith/")) == 0) { + arg += strlen("faith/"); + sep->se_type = FAITH_TYPE; + } + sep->se_proto = newstr(arg); + +#define MALFORMED(arg) \ +do { \ + syslog(LOG_ERR, "%s: malformed buffer size option `%s'", \ + sep->se_service, (arg)); \ + goto more; \ + /*NOTREACHED*/ \ +} while (/*CONSTCOND*/0) + +#define GETVAL(arg) \ +do { \ + if (!isdigit((unsigned char)*(arg))) \ + MALFORMED(arg); \ + val = (int)strtol((arg), &cp0, 10); \ + if (cp0 != NULL) { \ + if (cp0[1] != '\0') \ + MALFORMED((arg)); \ + if (cp0[0] == 'k') \ + val *= 1024; \ + if (cp0[0] == 'm') \ + val *= 1024 * 1024; \ + } \ + if (val < 1) { \ + syslog(LOG_ERR, "%s: invalid buffer size `%s'", \ + sep->se_service, (arg)); \ + goto more; \ + } \ + /*NOTREACHED*/ \ +} while (/*CONSTCOND*/0) + +#define ASSIGN(arg) \ +do { \ + if (strcmp((arg), "sndbuf") == 0) \ + sep->se_sndbuf = val; \ + else if (strcmp((arg), "rcvbuf") == 0) \ + sep->se_rcvbuf = val; \ + else \ + MALFORMED((arg)); \ +} while (/*CONSTCOND*/0) + + /* + * Extract the send and receive buffer sizes before parsing + * the protocol. + */ + sep->se_sndbuf = sep->se_rcvbuf = 0; + buf0 = buf1 = sz0 = sz1 = NULL; + if ((buf0 = strchr(sep->se_proto, ',')) != NULL) { + /* Not meaningful for Tcpmux services. */ + if (ISMUX(sep)) { + syslog(LOG_ERR, "%s: can't specify buffer sizes for " + "tcpmux services", sep->se_service); + goto more; + } + + /* Skip the , */ + *buf0++ = '\0'; + + /* Check to see if another socket buffer size was specified. */ + if ((buf1 = strchr(buf0, ',')) != NULL) { + /* Skip the , */ + *buf1++ = '\0'; + + /* Make sure a 3rd one wasn't specified. */ + if (strchr(buf1, ',') != NULL) { + syslog(LOG_ERR, "%s: too many buffer sizes", + sep->se_service); + goto more; + } + + /* Locate the size. */ + if ((sz1 = strchr(buf1, '=')) == NULL) + MALFORMED(buf1); + + /* Skip the = */ + *sz1++ = '\0'; + } + + /* Locate the size. */ + if ((sz0 = strchr(buf0, '=')) == NULL) + MALFORMED(buf0); + + /* Skip the = */ + *sz0++ = '\0'; + + GETVAL(sz0); + ASSIGN(buf0); + + if (buf1 != NULL) { + GETVAL(sz1); + ASSIGN(buf1); + } + } + +#undef ASSIGN +#undef GETVAL +#undef MALFORMED + + if (strcmp(sep->se_proto, "unix") == 0) { + sep->se_family = AF_LOCAL; + } else { + val = (int)strlen(sep->se_proto); + if (!val) { + syslog(LOG_ERR, "%s: invalid protocol specified", + sep->se_service); + goto more; + } + val = sep->se_proto[val - 1]; + switch (val) { + case '4': /*tcp4 or udp4*/ + sep->se_family = AF_INET; + break; +#ifdef INET6 + case '6': /*tcp6 or udp6*/ + sep->se_family = AF_INET6; + break; +#endif + default: + sep->se_family = AF_INET; /*will become AF_INET6*/ + break; + } + if (strncmp(sep->se_proto, "rpc/", 4) == 0) { +#ifdef RPC + char *cp1, *ccp; + cp1 = strchr(sep->se_service, '/'); + if (cp1 == 0) { + syslog(LOG_ERR, "%s: no rpc version", + sep->se_service); + goto more; + } + *cp1++ = '\0'; + sep->se_rpcversl = sep->se_rpcversh = + (int)strtol(cp1, &ccp, 0); + if (ccp == cp1) { + badafterall: + syslog(LOG_ERR, "%s/%s: bad rpc version", + sep->se_service, cp1); + goto more; + } + if (*ccp == '-') { + cp1 = ccp + 1; + sep->se_rpcversh = (int)strtol(cp1, &ccp, 0); + if (ccp == cp1) + goto badafterall; + } +#else + syslog(LOG_ERR, "%s: rpc services not suported", + sep->se_service); + goto more; +#endif /* RPC */ + } + } + arg = sskip(&cp); + { + char *cp1; + if ((cp1 = strchr(arg, ':')) == NULL) + cp1 = strchr(arg, '.'); + if (cp1 != NULL) { + *cp1++ = '\0'; + sep->se_max = atoi(cp1); + } else + sep->se_max = TOOMANY; + } + sep->se_wait = strcmp(arg, "wait") == 0; + if (ISMUX(sep)) { + /* + * Silently enforce "nowait" for TCPMUX services since + * they don't have an assigned port to listen on. + */ + sep->se_wait = 0; + + if (strncmp(sep->se_proto, "tcp", 3)) { + syslog(LOG_ERR, + "%s: bad protocol for tcpmux service %s", + CONFIG, sep->se_service); + goto more; + } + if (sep->se_socktype != SOCK_STREAM) { + syslog(LOG_ERR, + "%s: bad socket type for tcpmux service %s", + CONFIG, sep->se_service); + goto more; + } + } + sep->se_user = newstr(sskip(&cp)); + if ((sep->se_group = strchr(sep->se_user, ':')) != NULL) + *sep->se_group++ = '\0'; + else if ((sep->se_group = strchr(sep->se_user, '.')) != NULL) + *sep->se_group++ = '\0'; + + sep->se_server = newstr(sskip(&cp)); + if (strcmp(sep->se_server, "internal") == 0) { + struct biltin *bi; + + for (bi = biltins; bi->bi_service; bi++) + if (bi->bi_socktype == sep->se_socktype && + strcmp(bi->bi_service, sep->se_service) == 0) + break; + if (bi->bi_service == 0) { + syslog(LOG_ERR, "internal service %s unknown", + sep->se_service); + goto more; + } + sep->se_bi = bi; + sep->se_wait = bi->bi_wait; + } else + sep->se_bi = NULL; + argc = 0; + for (arg = skip(&cp); cp; arg = skip(&cp)) { + if (argc < MAXARGV) + sep->se_argv[argc++] = newstr(arg); + } + while (argc <= MAXARGV) + sep->se_argv[argc++] = NULL; +#ifdef IPSEC + sep->se_policy = policy ? newstr(policy) : NULL; +#endif + return (sep); +} + +static void +freeconfig(struct servtab *cp) +{ + int i; + + if (cp->se_hostaddr) + free(cp->se_hostaddr); + if (cp->se_service) + free(cp->se_service); + if (cp->se_proto) + free(cp->se_proto); + if (cp->se_user) + free(cp->se_user); + /* Note: se_group is part of the newstr'ed se_user */ + if (cp->se_server) + free(cp->se_server); + for (i = 0; i < MAXARGV; i++) + if (cp->se_argv[i]) + free(cp->se_argv[i]); +#ifdef IPSEC + if (cp->se_policy) + free(cp->se_policy); +#endif +} + + +/* + * Safe skip - if skip returns null, log a syntax error in the + * configuration file and exit. + */ +static char * +sskip(char **cpp) +{ + char *cp; + + cp = skip(cpp); + if (cp == NULL) { + syslog(LOG_ERR, "%s: syntax error", CONFIG); + exit(1); + } + return (cp); +} + +static char * +skip(char **cpp) +{ + char *cp = *cpp; + char *start; + char quote; + + if (*cpp == NULL) + return (NULL); + +again: + while (*cp == ' ' || *cp == '\t') + cp++; + if (*cp == '\0') { + int c; + + c = getc(fconfig); + (void) ungetc(c, fconfig); + if (c == ' ' || c == '\t') + if ((cp = nextline(fconfig)) != NULL) + goto again; + *cpp = NULL; + return (NULL); + } + start = cp; + quote = '\0'; + while (*cp && (quote || (*cp != ' ' && *cp != '\t'))) { + if (*cp == '\'' || *cp == '"') { + if (quote && *cp != quote) + cp++; + else { + if (quote) + quote = '\0'; + else + quote = *cp; + memmove(cp, cp+1, strlen(cp)); + } + } else + cp++; + } + if (*cp != '\0') + *cp++ = '\0'; + *cpp = cp; + return (start); +} + +static char * +nextline(FILE *fd) +{ + char *cp; + + if (fgets(line, (int)sizeof(line), fd) == NULL) + return (NULL); + cp = strchr(line, '\n'); + if (cp) + *cp = '\0'; + return (line); +} + +static char * +newstr(const char *cp) +{ + char *dp; + if ((dp = strdup((cp != NULL) ? cp : "")) != NULL) + return (dp); + syslog(LOG_ERR, "strdup: %m"); + exit(1); + /*NOTREACHED*/ +} + +static void +inetd_setproctitle(char *a, int s) +{ + socklen_t size; + struct sockaddr_storage ss; + char hbuf[NI_MAXHOST]; + const char *hp; + struct sockaddr *sa; + + size = sizeof(ss); + sa = (struct sockaddr *)(void *)&ss; + if (getpeername(s, sa, &size) == 0) { + if (getnameinfo(sa, size, hbuf, (socklen_t)sizeof(hbuf), NULL, + 0, niflags) != 0) + hp = "?"; + else + hp = hbuf; + setproctitle("-%s [%s]", a, hp); + } else + setproctitle("-%s", a); +} + +static void +bump_nofile(void) +{ +#define FD_CHUNK 32 + struct rlimit rl; + + if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { + syslog(LOG_ERR, "getrlimit: %m"); + return; + } + rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK); + if (rl.rlim_cur <= rlim_ofile_cur) { + syslog(LOG_ERR, + "bump_nofile: cannot extend file limit, max = %d", + (int)rl.rlim_cur); + return; + } + + if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { + syslog(LOG_ERR, "setrlimit: %m"); + return; + } + + rlim_ofile_cur = rl.rlim_cur; + return; +} + +/* + * Internet services provided internally by inetd: + */ +#define BUFSIZE 4096 + +/* ARGSUSED */ +static void +echo_stream(int s, struct servtab *sep) /* Echo service -- echo data back */ +{ + char buffer[BUFSIZE]; + ssize_t i; + + inetd_setproctitle(sep->se_service, s); + while ((i = read(s, buffer, sizeof(buffer))) > 0 && + write(s, buffer, (size_t)i) > 0) + ; +} + +/* ARGSUSED */ +static void +echo_dg(int s, struct servtab *sep) /* Echo service -- echo data back */ +{ + char buffer[BUFSIZE]; + ssize_t i; + socklen_t size; + struct sockaddr_storage ss; + struct sockaddr *sa; + + sa = (struct sockaddr *)(void *)&ss; + size = sizeof(ss); + if ((i = recvfrom(s, buffer, sizeof(buffer), 0, sa, &size)) < 0) + return; + if (port_good_dg(sa)) + (void) sendto(s, buffer, (size_t)i, 0, sa, size); +} + +/* ARGSUSED */ +static void +discard_stream(int s, struct servtab *sep) /* Discard service -- ignore data */ +{ + char buffer[BUFSIZE]; + + inetd_setproctitle(sep->se_service, s); + while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) || + errno == EINTR) + ; +} + +/* ARGSUSED */ +static void +discard_dg(int s, struct servtab *sep) /* Discard service -- ignore data */ + +{ + char buffer[BUFSIZE]; + + (void) read(s, buffer, sizeof(buffer)); +} + +#define LINESIZ 72 +char ring[128]; +char *endring; + +static void +initring(void) +{ + int i; + + endring = ring; + + for (i = 0; i <= 128; ++i) + if (isprint(i)) + *endring++ = i; +} + +/* ARGSUSED */ +static void +chargen_stream(int s,struct servtab *sep) /* Character generator */ +{ + size_t len; + char *rs, text[LINESIZ+2]; + + inetd_setproctitle(sep->se_service, s); + + if (!endring) { + initring(); + rs = ring; + } + + text[LINESIZ] = '\r'; + text[LINESIZ + 1] = '\n'; + for (rs = ring;;) { + if ((len = endring - rs) >= LINESIZ) + memmove(text, rs, LINESIZ); + else { + memmove(text, rs, len); + memmove(text + len, ring, LINESIZ - len); + } + if (++rs == endring) + rs = ring; + if (write(s, text, sizeof(text)) != sizeof(text)) + break; + } +} + +/* ARGSUSED */ +static void +chargen_dg(int s, struct servtab *sep) /* Character generator */ +{ + struct sockaddr_storage ss; + struct sockaddr *sa; + static char *rs; + size_t len; + socklen_t size; + char text[LINESIZ+2]; + + if (endring == 0) { + initring(); + rs = ring; + } + + sa = (struct sockaddr *)(void *)&ss; + size = sizeof(ss); + if (recvfrom(s, text, sizeof(text), 0, sa, &size) < 0) + return; + + if (!port_good_dg(sa)) + return; + + if ((len = endring - rs) >= LINESIZ) + memmove(text, rs, LINESIZ); + else { + memmove(text, rs, len); + memmove(text + len, ring, LINESIZ - len); + } + if (++rs == endring) + rs = ring; + text[LINESIZ] = '\r'; + text[LINESIZ + 1] = '\n'; + (void) sendto(s, text, sizeof(text), 0, sa, size); +} + +/* + * Return a machine readable date and time, in the form of the + * number of seconds since midnight, Jan 1, 1900. Since gettimeofday + * returns the number of seconds since midnight, Jan 1, 1970, + * we must add 2208988800 seconds to this figure to make up for + * some seventy years Bell Labs was asleep. + */ + +static uint32_t +machtime(void) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) < 0) { + if (debug) + fprintf(stderr, "Unable to get time of day\n"); + return (0); + } +#define OFFSET ((uint32_t)25567 * 24*60*60) + return (htonl((uint32_t)(tv.tv_sec + OFFSET))); +#undef OFFSET +} + +/* ARGSUSED */ +static void +machtime_stream(int s, struct servtab *sep) +{ + uint32_t result; + + result = machtime(); + (void) write(s, &result, sizeof(result)); +} + +/* ARGSUSED */ +void +machtime_dg(int s, struct servtab *sep) +{ + uint32_t result; + struct sockaddr_storage ss; + struct sockaddr *sa; + socklen_t size; + + sa = (struct sockaddr *)(void *)&ss; + size = sizeof(ss); + if (recvfrom(s, &result, sizeof(result), 0, sa, &size) < 0) + return; + if (!port_good_dg(sa)) + return; + result = machtime(); + (void)sendto(s, &result, sizeof(result), 0, sa, size); +} + +/* ARGSUSED */ +static void +daytime_stream(int s,struct servtab *sep) +/* Return human-readable time of day */ +{ + char buffer[256]; + time_t clk; + int len; + + clk = time((time_t *) 0); + + len = snprintf(buffer, sizeof buffer, "%.24s\r\n", ctime(&clk)); + (void) write(s, buffer, len); +} + +/* ARGSUSED */ +void +daytime_dg(int s, struct servtab *sep) +/* Return human-readable time of day */ +{ + char buffer[256]; + time_t clk; + struct sockaddr_storage ss; + struct sockaddr *sa; + socklen_t size; + int len; + + clk = time((time_t *) 0); + + sa = (struct sockaddr *)(void *)&ss; + size = sizeof(ss); + if (recvfrom(s, buffer, sizeof(buffer), 0, sa, &size) < 0) + return; + if (!port_good_dg(sa)) + return; + len = snprintf(buffer, sizeof buffer, "%.24s\r\n", ctime(&clk)); + (void) sendto(s, buffer, len, 0, sa, size); +} + +/* + * print_service: + * Dump relevant information to stderr + */ +static void +print_service(const char *action, struct servtab *sep) +{ + + if (isrpcservice(sep)) + fprintf(stderr, + "%s: %s rpcprog=%d, rpcvers = %d/%d, proto=%s, wait.max=%d.%d, user:group=%s:%s builtin=%lx server=%s" +#ifdef IPSEC + " policy=\"%s\"" +#endif + "\n", + action, sep->se_service, + sep->se_rpcprog, sep->se_rpcversh, sep->se_rpcversl, sep->se_proto, + sep->se_wait, sep->se_max, sep->se_user, sep->se_group, + (long)sep->se_bi, sep->se_server +#ifdef IPSEC + , (sep->se_policy ? sep->se_policy : "") +#endif + ); + else + fprintf(stderr, + "%s: %s proto=%s%s, wait.max=%d.%d, user:group=%s:%s builtin=%lx server=%s" +#ifdef IPSEC + " policy=%s" +#endif + "\n", + action, sep->se_service, + sep->se_type == FAITH_TYPE ? "faith/" : "", + sep->se_proto, + sep->se_wait, sep->se_max, sep->se_user, sep->se_group, + (long)sep->se_bi, sep->se_server +#ifdef IPSEC + , (sep->se_policy ? sep->se_policy : "") +#endif + ); +} + +static void +usage(void) +{ +#ifdef LIBWRAP + (void)fprintf(stderr, "usage: %s [-dl] [conf]\n", getprogname()); +#else + (void)fprintf(stderr, "usage: %s [-d] [conf]\n", getprogname()); +#endif + exit(1); +} + + +/* + * Based on TCPMUX.C by Mark K. Lottor November 1988 + * sri-nic::ps:tcpmux.c + */ + +static int /* # of characters upto \r,\n or \0 */ +get_line(int fd, char *buf, int len) +{ + int count = 0; + ssize_t n; + + do { + n = read(fd, buf, len-count); + if (n == 0) + return (count); + if (n < 0) + return (-1); + while (--n >= 0) { + if (*buf == '\r' || *buf == '\n' || *buf == '\0') + return (count); + count++; + buf++; + } + } while (count < len); + return (count); +} + +#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ + +#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) + +static void +tcpmux(int ctrl, struct servtab *sep) +{ + char service[MAX_SERV_LEN+1]; + int len; + + /* Get requested service name */ + if ((len = get_line(ctrl, service, MAX_SERV_LEN)) < 0) { + strwrite(ctrl, "-Error reading service name\r\n"); + goto reject; + } + service[len] = '\0'; + + if (debug) + fprintf(stderr, "tcpmux: someone wants %s\n", service); + + /* + * Help is a required command, and lists available services, + * one per line. + */ + if (!strcasecmp(service, "help")) { + strwrite(ctrl, "+Available services:\r\n"); + strwrite(ctrl, "help\r\n"); + for (sep = servtab; sep != NULL; sep = sep->se_next) { + if (!ISMUX(sep)) + continue; + (void)write(ctrl, sep->se_service, + strlen(sep->se_service)); + strwrite(ctrl, "\r\n"); + } + goto reject; + } + + /* Try matching a service in inetd.conf with the request */ + for (sep = servtab; sep != NULL; sep = sep->se_next) { + if (!ISMUX(sep)) + continue; + if (!strcasecmp(service, sep->se_service)) { + if (ISMUXPLUS(sep)) + strwrite(ctrl, "+Go\r\n"); + run_service(ctrl, sep, 1 /* forked */); + return; + } + } + strwrite(ctrl, "-Service not available\r\n"); +reject: + _exit(1); +} + +/* + * check if the address/port where send data to is one of the obvious ports + * that are used for denial of service attacks like two echo ports + * just echoing data between them + */ +static int +port_good_dg(struct sockaddr *sa) +{ + struct in_addr in; + struct sockaddr_in *sin; +#ifdef INET6 + struct in6_addr *in6; + struct sockaddr_in6 *sin6; +#endif + u_int16_t port; + int i; + char hbuf[NI_MAXHOST]; + + switch (sa->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *)(void *)sa; + in.s_addr = ntohl(sin->sin_addr.s_addr); + port = ntohs(sin->sin_port); +#ifdef INET6 + v4chk: +#endif + if (IN_MULTICAST(in.s_addr)) + goto bad; + switch ((in.s_addr & 0xff000000) >> 24) { + case 0: case 127: case 255: + goto bad; + } + if (dg_broadcast(&in)) + goto bad; + break; +#ifdef INET6 + case AF_INET6: + sin6 = (struct sockaddr_in6 *)(void *)sa; + in6 = &sin6->sin6_addr; + port = ntohs(sin6->sin6_port); + if (IN6_IS_ADDR_MULTICAST(in6) || IN6_IS_ADDR_UNSPECIFIED(in6)) + goto bad; + if (IN6_IS_ADDR_V4MAPPED(in6) || IN6_IS_ADDR_V4COMPAT(in6)) { + memcpy(&in, &in6->s6_addr[12], sizeof(in)); + in.s_addr = ntohl(in.s_addr); + goto v4chk; + } + break; +#endif + default: + /* XXX unsupported af, is it safe to assume it to be safe? */ + return (1); + } + + for (i = 0; bad_ports[i] != 0; i++) { + if (port == bad_ports[i]) + goto bad; + } + + return (1); + +bad: + if (getnameinfo(sa, sa->sa_len, hbuf, (socklen_t)sizeof(hbuf), NULL, 0, + niflags) != 0) + strlcpy(hbuf, "?", sizeof(hbuf)); + syslog(LOG_WARNING,"Possible DoS attack from %s, Port %d", + hbuf, port); + return (0); +} + +/* XXX need optimization */ +static int +dg_broadcast(struct in_addr *in) +{ + struct ifaddrs *ifa, *ifap; + struct sockaddr_in *sin; + + if (getifaddrs(&ifap) < 0) + return (0); + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET || + (ifa->ifa_flags & IFF_BROADCAST) == 0) + continue; + sin = (struct sockaddr_in *)(void *)ifa->ifa_broadaddr; + if (sin->sin_addr.s_addr == in->s_addr) { + freeifaddrs(ifap); + return (1); + } + } + freeifaddrs(ifap); + return (0); +} + +#ifndef __minix +static int +my_kevent(const struct kevent *changelist, size_t nchanges, + struct kevent *eventlist, size_t nevents) +{ + int result; + + while ((result = kevent(kq, changelist, nchanges, eventlist, nevents, + NULL)) < 0) + if (errno != EINTR) { + syslog(LOG_ERR, "kevent: %m"); + exit(EXIT_FAILURE); + } + + return (result); +} + +static struct kevent * +allocchange(void) +{ + if (changes == A_CNT(changebuf)) { + (void) my_kevent(changebuf, A_CNT(changebuf), NULL, 0); + changes = 0; + } + + return (&changebuf[changes++]); +} +#endif /* !__minix */ diff --git a/usr.sbin/inetd/ipsec.c b/usr.sbin/inetd/ipsec.c new file mode 100644 index 000000000..bc3b0d0ed --- /dev/null +++ b/usr.sbin/inetd/ipsec.c @@ -0,0 +1,159 @@ +/* $NetBSD: ipsec.c,v 1.4 2012/01/04 16:09:43 drochner Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef IPSEC +#include +#ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */ +#undef IPSEC +#endif +#endif + +#include "ipsec.h" + +#ifdef IPSEC +int +ipsecsetup(int af, int fd, const char *policy) +{ + char *p0, *p; + int error; + + if (!policy || policy == '\0') + p0 = p = strdup("in entrust; out entrust"); + else + p0 = p = strdup(policy); + + error = 0; + for (;;) { + p = strtok(p, ";"); + if (p == NULL) + break; + while (*p && isspace((unsigned char)*p)) + p++; + if (!*p) { + p = NULL; + continue; + } + error = ipsecsetup0(af, fd, p, 1); + if (error < 0) + break; + p = NULL; + } + + free(p0); + return error; +} + +int +ipsecsetup_test(const char *policy) +{ + char *p0, *p; + char *buf; + int error; + + if (!policy) + return -1; + p0 = p = strdup(policy); + if (p == NULL) + return -1; + + error = 0; + for (;;) { + p = strtok(p, ";"); + if (p == NULL) + break; + while (*p && isspace((unsigned char)*p)) + p++; + if (!*p) { + p = NULL; + continue; + } + buf = ipsec_set_policy(p, (int)strlen(p)); + if (buf == NULL) { + error = -1; + break; + } + free(buf); + p = NULL; + } + + free(p0); + return error; +} + +int +ipsecsetup0(int af, int fd, const char *policy, int commit) +{ + int level; + int opt; + char *buf; + int error; + + switch (af) { + case AF_INET: + level = IPPROTO_IP; + opt = IP_IPSEC_POLICY; + break; +#ifdef INET6 + case AF_INET6: + level = IPPROTO_IPV6; + opt = IPV6_IPSEC_POLICY; + break; +#endif + default: + return -1; + } + + buf = ipsec_set_policy(policy, (int)strlen(policy)); + if (buf != NULL) { + error = 0; + if (commit && setsockopt(fd, level, opt, + buf, (socklen_t)ipsec_get_policylen(buf)) < 0) { + error = -1; + } + free(buf); + } else + error = -1; + return error; +} +#endif diff --git a/usr.sbin/inetd/ipsec.h b/usr.sbin/inetd/ipsec.h new file mode 100644 index 000000000..36e9c4b68 --- /dev/null +++ b/usr.sbin/inetd/ipsec.h @@ -0,0 +1,34 @@ +/* $NetBSD: ipsec.h,v 1.1 2000/01/31 14:28:20 itojun Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern int ipsecsetup __P((int, int, const char *)); +extern int ipsecsetup_test __P((const char *)); +extern int ipsecsetup0 __P((int, int, const char *, int)); diff --git a/usr.sbin/inetd/pathnames.h b/usr.sbin/inetd/pathnames.h new file mode 100644 index 000000000..0de14cfa0 --- /dev/null +++ b/usr.sbin/inetd/pathnames.h @@ -0,0 +1,36 @@ +/* $NetBSD: pathnames.h,v 1.7 2003/08/07 11:25:22 agc Exp $ */ + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#include + +#define _PATH_INETDCONF "/etc/inetd.conf"