inetd.c revision 127865
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1983, 1991, 1993, 1994
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes * Redistribution and use in source and binary forms, with or without
61553Srgrimes * modification, are permitted provided that the following conditions
71553Srgrimes * are met:
81553Srgrimes * 1. Redistributions of source code must retain the above copyright
91553Srgrimes *    notice, this list of conditions and the following disclaimer.
101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111553Srgrimes *    notice, this list of conditions and the following disclaimer in the
121553Srgrimes *    documentation and/or other materials provided with the distribution.
131553Srgrimes * 3. All advertising materials mentioning features or use of this software
141553Srgrimes *    must display the following acknowledgement:
151553Srgrimes *	This product includes software developed by the University of
161553Srgrimes *	California, Berkeley and its contributors.
171553Srgrimes * 4. Neither the name of the University nor the names of its contributors
181553Srgrimes *    may be used to endorse or promote products derived from this software
191553Srgrimes *    without specific prior written permission.
201553Srgrimes *
211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311553Srgrimes * SUCH DAMAGE.
321553Srgrimes */
331553Srgrimes
341553Srgrimes#ifndef lint
3529602Scharnierstatic const char copyright[] =
361553Srgrimes"@(#) Copyright (c) 1983, 1991, 1993, 1994\n\
371553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381553Srgrimes#endif /* not lint */
391553Srgrimes
401553Srgrimes#ifndef lint
4129602Scharnier#if 0
4229602Scharnierstatic char sccsid[] = "@(#)from: inetd.c	8.4 (Berkeley) 4/13/94";
4329602Scharnier#endif
441553Srgrimes#endif /* not lint */
451553Srgrimes
4698563Sjmallett#include <sys/cdefs.h>
4798563Sjmallett__FBSDID("$FreeBSD: head/usr.sbin/inetd/inetd.c 127865 2004-04-04 21:32:23Z dwmalone $");
4898563Sjmallett
491553Srgrimes/*
501553Srgrimes * Inetd - Internet super-server
511553Srgrimes *
521553Srgrimes * This program invokes all internet services as needed.  Connection-oriented
531553Srgrimes * services are invoked each time a connection is made, by creating a process.
541553Srgrimes * This process is passed the connection as file descriptor 0 and is expected
551553Srgrimes * to do a getpeername to find out the source host and port.
561553Srgrimes *
571553Srgrimes * Datagram oriented services are invoked when a datagram
581553Srgrimes * arrives; a process is created and passed a pending message
591553Srgrimes * on file descriptor 0.  Datagram servers may either connect
601553Srgrimes * to their peer, freeing up the original socket for inetd
611553Srgrimes * to receive further messages on, or ``take over the socket'',
621553Srgrimes * processing all arriving datagrams and, eventually, timing
631553Srgrimes * out.	 The first type of server is said to be ``multi-threaded'';
648857Srgrimes * the second type of server ``single-threaded''.
651553Srgrimes *
661553Srgrimes * Inetd uses a configuration file which is read at startup
671553Srgrimes * and, possibly, at some later time in response to a hangup signal.
681553Srgrimes * The configuration file is ``free format'' with fields given in the
6967514Sdwmalone * order shown below.  Continuation lines for an entry must begin with
701553Srgrimes * a space or tab.  All fields must be present in each entry.
711553Srgrimes *
7278356Sdwmalone *	service name			must be in /etc/services
7378356Sdwmalone *					or name a tcpmux service
7478356Sdwmalone *					or specify a unix domain socket
751553Srgrimes *	socket type			stream/dgram/raw/rdm/seqpacket
7678356Sdwmalone *	protocol			tcp[4][6][/faith,ttcp], udp[4][6], unix
771553Srgrimes *	wait/nowait			single-threaded/multi-threaded
781553Srgrimes *	user				user to run daemon as
791553Srgrimes *	server program			full path name
801553Srgrimes *	server program arguments	maximum of MAXARGS (20)
811553Srgrimes *
821553Srgrimes * TCP services without official port numbers are handled with the
831553Srgrimes * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
841553Srgrimes * requests. When a connection is made from a foreign host, the service
851553Srgrimes * requested is passed to tcpmux, which looks it up in the servtab list
861553Srgrimes * and returns the proper entry for the service. Tcpmux returns a
871553Srgrimes * negative reply if the service doesn't exist, otherwise the invoked
881553Srgrimes * server is expected to return the positive reply if the service type in
891553Srgrimes * inetd.conf file has the prefix "tcpmux/". If the service type has the
901553Srgrimes * prefix "tcpmux/+", tcpmux will return the positive reply for the
911553Srgrimes * process; this is for compatibility with older server code, and also
921553Srgrimes * allows you to invoke programs that use stdin/stdout without putting any
931553Srgrimes * special server code in them. Services that use tcpmux are "nowait"
941553Srgrimes * because they do not have a well-known port and hence cannot listen
951553Srgrimes * for new requests.
961553Srgrimes *
972657Scsgr * For RPC services
982657Scsgr *	service name/version		must be in /etc/rpc
992657Scsgr *	socket type			stream/dgram/raw/rdm/seqpacket
100100127Salfred *	protocol			rpc/tcp[4][6], rpc/udp[4][6]
1012657Scsgr *	wait/nowait			single-threaded/multi-threaded
1022657Scsgr *	user				user to run daemon as
1032657Scsgr *	server program			full path name
1042657Scsgr *	server program arguments	maximum of MAXARGS
1052657Scsgr *
1061553Srgrimes * Comment lines are indicated by a `#' in column 1.
10756590Sshin *
10856590Sshin * #ifdef IPSEC
10956590Sshin * Comment lines that start with "#@" denote IPsec policy string, as described
11056590Sshin * in ipsec_set_policy(3).  This will affect all the following items in
11156590Sshin * inetd.conf(8).  To reset the policy, just use "#@" line.  By default,
11256590Sshin * there's no IPsec policy.
11356590Sshin * #endif
1141553Srgrimes */
1151553Srgrimes#include <sys/param.h>
1161553Srgrimes#include <sys/ioctl.h>
1171553Srgrimes#include <sys/wait.h>
1181553Srgrimes#include <sys/time.h>
1191553Srgrimes#include <sys/resource.h>
12078356Sdwmalone#include <sys/stat.h>
12178356Sdwmalone#include <sys/un.h>
1221553Srgrimes
1231553Srgrimes#include <netinet/in.h>
12436042Sguido#include <netinet/tcp.h>
1251553Srgrimes#include <arpa/inet.h>
1262657Scsgr#include <rpc/rpc.h>
12719617Sjulian#include <rpc/pmap_clnt.h>
1281553Srgrimes
129106054Swollman#include <ctype.h>
1301553Srgrimes#include <errno.h>
13129602Scharnier#include <err.h>
1321553Srgrimes#include <fcntl.h>
13330807Sache#include <grp.h>
134106054Swollman#include <libutil.h>
135106054Swollman#include <limits.h>
1361553Srgrimes#include <netdb.h>
1371553Srgrimes#include <pwd.h>
1381553Srgrimes#include <signal.h>
1391553Srgrimes#include <stdio.h>
1401553Srgrimes#include <stdlib.h>
1411553Srgrimes#include <string.h>
142106054Swollman#include <sysexits.h>
1431553Srgrimes#include <syslog.h>
14448279Ssheldonh#include <tcpd.h>
1451553Srgrimes#include <unistd.h>
1461553Srgrimes
14748981Ssheldonh#include "inetd.h"
14848981Ssheldonh#include "pathnames.h"
14948981Ssheldonh
15056590Sshin#ifdef IPSEC
15156590Sshin#include <netinet6/ipsec.h>
15256590Sshin#ifndef IPSEC_POLICY_IPSEC	/* no ipsec support on old ipsec */
15356590Sshin#undef IPSEC
15456590Sshin#endif
15556590Sshin#endif
15656590Sshin
15756590Sshin/* wrapper for KAME-special getnameinfo() */
15856590Sshin#ifndef NI_WITHSCOPEID
15956590Sshin#define NI_WITHSCOPEID	0
16056590Sshin#endif
16156590Sshin
16245089Smarkm#ifndef LIBWRAP_ALLOW_FACILITY
16345089Smarkm# define LIBWRAP_ALLOW_FACILITY LOG_AUTH
16445089Smarkm#endif
16545089Smarkm#ifndef LIBWRAP_ALLOW_SEVERITY
16645089Smarkm# define LIBWRAP_ALLOW_SEVERITY LOG_INFO
16745089Smarkm#endif
16845089Smarkm#ifndef LIBWRAP_DENY_FACILITY
16945089Smarkm# define LIBWRAP_DENY_FACILITY LOG_AUTH
17045089Smarkm#endif
17145089Smarkm#ifndef LIBWRAP_DENY_SEVERITY
17245089Smarkm# define LIBWRAP_DENY_SEVERITY LOG_WARNING
17345089Smarkm#endif
17445089Smarkm
17548382Ssheldonh#define ISWRAP(sep)	\
17648697Ssheldonh	   ( ((wrap_ex && !(sep)->se_bi) || (wrap_bi && (sep)->se_bi)) \
17778356Sdwmalone	&& (sep->se_family == AF_INET || sep->se_family == AF_INET6) \
17848382Ssheldonh	&& ( ((sep)->se_accept && (sep)->se_socktype == SOCK_STREAM) \
17948382Ssheldonh	    || (sep)->se_socktype == SOCK_DGRAM))
18048382Ssheldonh
18121640Speter#ifdef LOGIN_CAP
18221640Speter#include <login_cap.h>
18330792Sache
18430792Sache/* see init.c */
18530792Sache#define RESOURCE_RC "daemon"
18630792Sache
18721640Speter#endif
18821640Speter
18933794Spst#ifndef	MAXCHILD
19033794Spst#define	MAXCHILD	-1		/* maximum number of this service
19133794Spst					   < 0 = no limit */
19233794Spst#endif
19333794Spst
19433794Spst#ifndef	MAXCPM
19533794Spst#define	MAXCPM		-1		/* rate limit invocations from a
19633794Spst					   single remote address,
19733794Spst					   < 0 = no limit */
19833794Spst#endif
19933794Spst
200101474Sume#ifndef	MAXPERIP
201101474Sume#define	MAXPERIP	-1		/* maximum number of this service
202101474Sume					   from a single remote address,
203101474Sume					   < 0 = no limit */
204101474Sume#endif
205101474Sume
20664197Sdwmalone#ifndef TOOMANY
2072659Scsgr#define	TOOMANY		256		/* don't start more than TOOMANY */
20864197Sdwmalone#endif
2091553Srgrimes#define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
2101553Srgrimes#define	RETRYTIME	(60*10)		/* retry after bind or server fail */
21119618Sjulian#define MAX_MAXCHLD	32767		/* max allowable max children */
2121553Srgrimes
2131553Srgrimes#define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
2141553Srgrimes
21598562Sjmallettvoid		close_sep(struct servtab *);
21698562Sjmallettvoid		flag_signal(int);
21798562Sjmallettvoid		flag_config(int);
21898562Sjmallettvoid		config(void);
21998562Sjmallettint		cpmip(const struct servtab *, int);
22098562Sjmallettvoid		endconfig(void);
22198562Sjmallettstruct servtab *enter(struct servtab *);
22298562Sjmallettvoid		freeconfig(struct servtab *);
22398562Sjmallettstruct servtab *getconfigent(void);
22498562Sjmallettint		matchservent(const char *, const char *, const char *);
22598562Sjmallettchar	       *nextline(FILE *);
22698562Sjmallettvoid		addchild(struct servtab *, int);
22798562Sjmallettvoid		flag_reapchild(int);
22898562Sjmallettvoid		reapchild(void);
22998562Sjmallettvoid		enable(struct servtab *);
23098562Sjmallettvoid		disable(struct servtab *);
23198562Sjmallettvoid		flag_retry(int);
23298562Sjmallettvoid		retry(void);
23398562Sjmallettint		setconfig(void);
23498562Sjmallettvoid		setup(struct servtab *);
23578694Sdwmalone#ifdef IPSEC
23698562Sjmallettvoid		ipsecsetup(struct servtab *);
23778694Sdwmalone#endif
23898562Sjmallettvoid		unregisterrpc(register struct servtab *sep);
239101474Sumestatic struct conninfo *search_conn(struct servtab *sep, int ctrl);
240101474Sumestatic int	room_conn(struct servtab *sep, struct conninfo *conn);
241101474Sumestatic void	addchild_conn(struct conninfo *conn, pid_t pid);
242101474Sumestatic void	reapchild_conn(pid_t pid);
243101474Sumestatic void	free_conn(struct conninfo *conn);
244101474Sumestatic void	resize_conn(struct servtab *sep, int maxperip);
245101474Sumestatic void	free_connlist(struct servtab *sep);
246101474Sumestatic void	free_proc(struct procinfo *);
247101474Sumestatic struct procinfo *search_proc(pid_t pid, int add);
248101474Sumestatic int	hashval(char *p, int len);
24978694Sdwmalone
25048279Ssheldonhint	allow_severity;
25148279Ssheldonhint	deny_severity;
25248697Ssheldonhint	wrap_ex = 0;
25348279Ssheldonhint	wrap_bi = 0;
2541553Srgrimesint	debug = 0;
255121766Speterint	dolog = 0;
25648988Ssheldonhint	maxsock;			/* highest-numbered descriptor */
2571553Srgrimesfd_set	allsock;
2581553Srgrimesint	options;
2591553Srgrimesint	timingout;
2601553Srgrimesint	toomany = TOOMANY;
26148069Ssheldonhint	maxchild = MAXCHILD;
26248069Ssheldonhint	maxcpm = MAXCPM;
263101474Sumeint	maxperip = MAXPERIP;
2641553Srgrimesstruct	servent *sp;
2652657Scsgrstruct	rpcent *rpc;
26656590Sshinchar	*hostname = NULL;
26756590Sshinstruct	sockaddr_in *bind_sa4;
268102938Sdwmaloneint	v4bind_ok = 0;
26956590Sshin#ifdef INET6
27056590Sshinstruct	sockaddr_in6 *bind_sa6;
271102938Sdwmaloneint	v6bind_ok = 0;
27256590Sshin#endif
27342122Sdesint	signalpipe[2];
27448991Ssheldonh#ifdef SANITY_CHECK
27548991Ssheldonhint	nsock;
27648991Ssheldonh#endif
27778356Sdwmaloneuid_t	euid;
27878356Sdwmalonegid_t	egid;
27978356Sdwmalonemode_t	mask;
2801553Srgrimes
28148981Ssheldonhstruct	servtab *servtab;
2821553Srgrimes
28348981Ssheldonhextern struct biltin biltins[];
2841553Srgrimes
28578694Sdwmaloneconst char	*CONFIG = _PATH_INETDCONF;
28678694Sdwmaloneconst char	*pid_file = _PATH_INETDPID;
28713142Speter
288100127Salfredstruct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
289100127Salfred
290101474Sumestatic LIST_HEAD(, procinfo) proctable[PERIPSIZE];
291101474Sume
2921553Srgrimesint
29398558Sjmallettgetvalue(const char *arg, int *value, const char *whine)
29433794Spst{
29533794Spst	int  tmp;
29633794Spst	char *p;
29733794Spst
29833794Spst	tmp = strtol(arg, &p, 0);
29964197Sdwmalone	if (tmp < 0 || *p) {
30033794Spst		syslog(LOG_ERR, whine, arg);
30133794Spst		return 1;			/* failure */
30233794Spst	}
30333794Spst	*value = tmp;
30433794Spst	return 0;				/* success */
30533794Spst}
30633794Spst
307110802Sumestatic sa_family_t
308110802Sumewhichaf(struct request_info *req)
309110802Sume{
310110802Sume	struct sockaddr *sa;
311110802Sume
312110802Sume	sa = (struct sockaddr *)req->client->sin;
313110802Sume	if (sa == NULL)
314110802Sume		return AF_UNSPEC;
315110802Sume	if (sa->sa_family == AF_INET6 &&
316110802Sume	    IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr))
317110802Sume		return AF_INET;
318110802Sume	return sa->sa_family;
319110802Sume}
320110802Sume
32133794Spstint
32298558Sjmallettmain(int argc, char **argv)
3231553Srgrimes{
3241553Srgrimes	struct servtab *sep;
3251553Srgrimes	struct passwd *pwd;
32630807Sache	struct group *grp;
32748962Ssheldonh	struct sigaction sa, saalrm, sachld, sahup, sapipe;
328121555Speter	int ch, dofork;
3291553Srgrimes	pid_t pid;
3301553Srgrimes	char buf[50];
33121640Speter#ifdef LOGIN_CAP
33221640Speter	login_cap_t *lc = NULL;
33321640Speter#endif
33445089Smarkm	struct request_info req;
33545089Smarkm	int denied;
33645089Smarkm	char *service = NULL;
33756590Sshin	union {
33856590Sshin		struct sockaddr peer_un;
33956590Sshin		struct sockaddr_in peer_un4;
34056590Sshin		struct sockaddr_in6 peer_un6;
34156590Sshin		struct sockaddr_storage peer_max;
34256590Sshin	} p_un;
34356590Sshin#define peer	p_un.peer_un
34456590Sshin#define peer4	p_un.peer_un4
34556590Sshin#define peer6	p_un.peer_un6
34656590Sshin#define peermax	p_un.peer_max
34747972Ssheldonh	int i;
34856590Sshin	struct addrinfo hints, *res;
34978694Sdwmalone	const char *servname;
35056590Sshin	int error;
351101474Sume	struct conninfo *conn;
3521553Srgrimes
35397293Sjwd	openlog("inetd", LOG_PID | LOG_NOWAIT | LOG_PERROR, LOG_DAEMON);
3541553Srgrimes
355101474Sume	while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:s:")) != -1)
3561553Srgrimes		switch(ch) {
3571553Srgrimes		case 'd':
3581553Srgrimes			debug = 1;
3591553Srgrimes			options |= SO_DEBUG;
3601553Srgrimes			break;
3612659Scsgr		case 'l':
362121766Speter			dolog = 1;
3632659Scsgr			break;
36433794Spst		case 'R':
36533794Spst			getvalue(optarg, &toomany,
36633794Spst				"-R %s: bad value for service invocation rate");
3671553Srgrimes			break;
36833794Spst		case 'c':
36933794Spst			getvalue(optarg, &maxchild,
37033794Spst				"-c %s: bad value for maximum children");
37133794Spst			break;
37233794Spst		case 'C':
37333794Spst			getvalue(optarg, &maxcpm,
37433794Spst				"-C %s: bad value for maximum children/minute");
37533794Spst			break;
37617482Sjulian		case 'a':
37756590Sshin			hostname = optarg;
37817482Sjulian			break;
37917482Sjulian		case 'p':
38017482Sjulian			pid_file = optarg;
38117482Sjulian			break;
382101474Sume		case 's':
383101474Sume			getvalue(optarg, &maxperip,
384101474Sume				"-s %s: bad value for maximum children per source address");
385101474Sume			break;
38648279Ssheldonh		case 'w':
38748697Ssheldonh			wrap_ex++;
38848279Ssheldonh			break;
38948697Ssheldonh		case 'W':
39048697Ssheldonh			wrap_bi++;
39148697Ssheldonh			break;
3921553Srgrimes		case '?':
3931553Srgrimes		default:
3941553Srgrimes			syslog(LOG_ERR,
39548697Ssheldonh				"usage: inetd [-dlwW] [-a address] [-R rate]"
39633794Spst				" [-c maximum] [-C rate]"
39717482Sjulian				" [-p pidfile] [conf-file]");
39819617Sjulian			exit(EX_USAGE);
3991553Srgrimes		}
40056590Sshin	/*
40156590Sshin	 * Initialize Bind Addrs.
40256590Sshin	 *   When hostname is NULL, wild card bind addrs are obtained from
40356590Sshin	 *   getaddrinfo(). But getaddrinfo() requires at least one of
40456590Sshin	 *   hostname or servname is non NULL.
40556590Sshin	 *   So when hostname is NULL, set dummy value to servname.
40656590Sshin	 */
40756590Sshin	servname = (hostname == NULL) ? "discard" /* dummy */ : NULL;
40856590Sshin
40956590Sshin	bzero(&hints, sizeof(struct addrinfo));
41056590Sshin	hints.ai_flags = AI_PASSIVE;
41156590Sshin	hints.ai_family = AF_UNSPEC;
41256590Sshin	error = getaddrinfo(hostname, servname, &hints, &res);
41356590Sshin	if (error != 0) {
41456590Sshin		syslog(LOG_ERR, "-a %s: %s", hostname, gai_strerror(error));
41556590Sshin		if (error == EAI_SYSTEM)
41656590Sshin			syslog(LOG_ERR, "%s", strerror(errno));
41756590Sshin		exit(EX_USAGE);
41856590Sshin	}
41956590Sshin	do {
42056590Sshin		if (res->ai_addr == NULL) {
42156590Sshin			syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname);
42256590Sshin			exit(EX_USAGE);
42356590Sshin		}
42456590Sshin		switch (res->ai_addr->sa_family) {
42556590Sshin		case AF_INET:
426102938Sdwmalone			if (v4bind_ok)
42756590Sshin				continue;
42856590Sshin			bind_sa4 = (struct sockaddr_in *)res->ai_addr;
42956590Sshin			/* init port num in case servname is dummy */
43056590Sshin			bind_sa4->sin_port = 0;
431102938Sdwmalone			v4bind_ok = 1;
43256590Sshin			continue;
43356590Sshin#ifdef INET6
43456590Sshin		case AF_INET6:
435102938Sdwmalone			if (v6bind_ok)
43656590Sshin				continue;
43756590Sshin			bind_sa6 = (struct sockaddr_in6 *)res->ai_addr;
43856590Sshin			/* init port num in case servname is dummy */
43956590Sshin			bind_sa6->sin6_port = 0;
440102938Sdwmalone			v6bind_ok = 1;
44156590Sshin			continue;
44256590Sshin#endif
44356590Sshin		}
444102938Sdwmalone		if (v4bind_ok
44556590Sshin#ifdef INET6
446102938Sdwmalone		    && v6bind_ok
44756590Sshin#endif
44856590Sshin		    )
44956590Sshin			break;
45056590Sshin	} while ((res = res->ai_next) != NULL);
451102938Sdwmalone	if (!v4bind_ok
45256590Sshin#ifdef INET6
453102938Sdwmalone	    && !v6bind_ok
45456590Sshin#endif
45556590Sshin	    ) {
45656590Sshin		syslog(LOG_ERR, "-a %s: unknown address family", hostname);
45756590Sshin		exit(EX_USAGE);
45856590Sshin	}
45956590Sshin
46078356Sdwmalone	euid = geteuid();
46178356Sdwmalone	egid = getegid();
46278356Sdwmalone	umask(mask = umask(0777));
46378356Sdwmalone
4641553Srgrimes	argc -= optind;
4651553Srgrimes	argv += optind;
4661553Srgrimes
4671553Srgrimes	if (argc > 0)
4681553Srgrimes		CONFIG = argv[0];
469127301Sdwmalone	if (access(CONFIG, R_OK) < 0)
470127301Sdwmalone		syslog(LOG_ERR, "Accessing %s: %m, continuing anyway.", CONFIG);
4711553Srgrimes	if (debug == 0) {
47211447Swollman		FILE *fp;
47312024Speter		if (daemon(0, 0) < 0) {
47412024Speter			syslog(LOG_WARNING, "daemon(0,0) failed: %m");
47512024Speter		}
47697293Sjwd		/* From now on we don't want syslog messages going to stderr. */
47797293Sjwd		closelog();
47897293Sjwd		openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
47912024Speter		/*
48012024Speter		 * In case somebody has started inetd manually, we need to
48112024Speter		 * clear the logname, so that old servers run as root do not
48212024Speter		 * get the user's logname..
48312024Speter		 */
48412024Speter		if (setlogin("") < 0) {
48512024Speter			syslog(LOG_WARNING, "cannot clear logname: %m");
48612024Speter			/* no big deal if it fails.. */
48712024Speter		}
48811447Swollman		pid = getpid();
48917482Sjulian		fp = fopen(pid_file, "w");
49011447Swollman		if (fp) {
49111447Swollman			fprintf(fp, "%ld\n", (long)pid);
49211447Swollman			fclose(fp);
49311447Swollman		} else {
49417482Sjulian			syslog(LOG_WARNING, "%s: %m", pid_file);
49511447Swollman		}
4961553Srgrimes	}
497100127Salfred
498101474Sume	for (i = 0; i < PERIPSIZE; ++i)
499101474Sume		LIST_INIT(&proctable[i]);
500101474Sume
501102938Sdwmalone	if (v4bind_ok) {
502100127Salfred		udpconf = getnetconfigent("udp");
503100127Salfred		tcpconf = getnetconfigent("tcp");
504100127Salfred		if (udpconf == NULL || tcpconf == NULL) {
505102860Sdwmalone			syslog(LOG_ERR, "unknown rpc/udp or rpc/tcp");
506100127Salfred			exit(EX_USAGE);
507100127Salfred		}
508100127Salfred	}
509100127Salfred#ifdef INET6
510102938Sdwmalone	if (v6bind_ok) {
511100127Salfred		udp6conf = getnetconfigent("udp6");
512100127Salfred		tcp6conf = getnetconfigent("tcp6");
513100127Salfred		if (udp6conf == NULL || tcp6conf == NULL) {
514102860Sdwmalone			syslog(LOG_ERR, "unknown rpc/udp6 or rpc/tcp6");
515100127Salfred			exit(EX_USAGE);
516100127Salfred		}
517100127Salfred	}
518100127Salfred#endif
519100127Salfred
52035948Sbde	sa.sa_flags = 0;
52135948Sbde	sigemptyset(&sa.sa_mask);
52235948Sbde	sigaddset(&sa.sa_mask, SIGALRM);
52335948Sbde	sigaddset(&sa.sa_mask, SIGCHLD);
52435948Sbde	sigaddset(&sa.sa_mask, SIGHUP);
52542122Sdes	sa.sa_handler = flag_retry;
52648962Ssheldonh	sigaction(SIGALRM, &sa, &saalrm);
52742122Sdes	config();
52842122Sdes	sa.sa_handler = flag_config;
52948962Ssheldonh	sigaction(SIGHUP, &sa, &sahup);
53042122Sdes	sa.sa_handler = flag_reapchild;
53148962Ssheldonh	sigaction(SIGCHLD, &sa, &sachld);
53235848Sguido	sa.sa_handler = SIG_IGN;
53335948Sbde	sigaction(SIGPIPE, &sa, &sapipe);
5341553Srgrimes
5351553Srgrimes	{
5361553Srgrimes		/* space for daemons to overwrite environment for ps */
5371553Srgrimes#define	DUMMYSIZE	100
5381553Srgrimes		char dummy[DUMMYSIZE];
5391553Srgrimes
54019298Salex		(void)memset(dummy, 'x', DUMMYSIZE - 1);
5411553Srgrimes		dummy[DUMMYSIZE - 1] = '\0';
5421553Srgrimes		(void)setenv("inetd_dummy", dummy, 1);
5431553Srgrimes	}
5441553Srgrimes
54542250Sdes	if (pipe(signalpipe) != 0) {
54652219Scharnier		syslog(LOG_ERR, "pipe: %m");
54742250Sdes		exit(EX_OSERR);
54842122Sdes	}
549111324Sdwmalone	if (fcntl(signalpipe[0], F_SETFD, FD_CLOEXEC) < 0 ||
550111324Sdwmalone	    fcntl(signalpipe[1], F_SETFD, FD_CLOEXEC) < 0) {
551111324Sdwmalone		syslog(LOG_ERR, "signalpipe: fcntl (F_SETFD, FD_CLOEXEC): %m");
552111324Sdwmalone		exit(EX_OSERR);
553111324Sdwmalone	}
55442122Sdes	FD_SET(signalpipe[0], &allsock);
55548991Ssheldonh#ifdef SANITY_CHECK
55647015Sdes	nsock++;
55748991Ssheldonh#endif
55848989Ssheldonh	if (signalpipe[0] > maxsock)
55948989Ssheldonh	    maxsock = signalpipe[0];
56048989Ssheldonh	if (signalpipe[1] > maxsock)
56148989Ssheldonh	    maxsock = signalpipe[1];
56241685Sdillon
5631553Srgrimes	for (;;) {
5641553Srgrimes	    int n, ctrl;
5651553Srgrimes	    fd_set readable;
5661553Srgrimes
56748991Ssheldonh#ifdef SANITY_CHECK
5681553Srgrimes	    if (nsock == 0) {
56947015Sdes		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
57047015Sdes		exit(EX_SOFTWARE);
5711553Srgrimes	    }
57248991Ssheldonh#endif
5731553Srgrimes	    readable = allsock;
57442122Sdes	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
57542122Sdes		(fd_set *)0, (struct timeval *)0)) <= 0) {
57642122Sdes		    if (n < 0 && errno != EINTR) {
5771553Srgrimes			syslog(LOG_WARNING, "select: %m");
57828907Simp			sleep(1);
57928907Simp		    }
5801553Srgrimes		    continue;
5811553Srgrimes	    }
58242122Sdes	    /* handle any queued signal flags */
58342250Sdes	    if (FD_ISSET(signalpipe[0], &readable)) {
58471399Sdwmalone		int nsig;
58571399Sdwmalone		if (ioctl(signalpipe[0], FIONREAD, &nsig) != 0) {
58642122Sdes		    syslog(LOG_ERR, "ioctl: %m");
58742122Sdes		    exit(EX_OSERR);
58842122Sdes		}
58971399Sdwmalone		while (--nsig >= 0) {
59042250Sdes		    char c;
59142250Sdes		    if (read(signalpipe[0], &c, 1) != 1) {
59242250Sdes			syslog(LOG_ERR, "read: %m");
59342250Sdes			exit(EX_OSERR);
59442250Sdes		    }
59542250Sdes		    if (debug)
59656482Scharnier			warnx("handling signal flag %c", c);
59742250Sdes		    switch(c) {
59842250Sdes		    case 'A': /* sigalrm */
59942250Sdes			retry();
60042250Sdes			break;
60142250Sdes		    case 'C': /* sigchld */
60242250Sdes			reapchild();
60342250Sdes			break;
60442250Sdes		    case 'H': /* sighup */
60542250Sdes			config();
60642250Sdes			break;
60742250Sdes		    }
60842250Sdes		}
60942122Sdes	    }
6101553Srgrimes	    for (sep = servtab; n && sep; sep = sep->se_next)
6111553Srgrimes	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
6121553Srgrimes		    n--;
6131553Srgrimes		    if (debug)
61429602Scharnier			    warnx("someone wants %s", sep->se_service);
615101474Sume		    dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep);
616101474Sume		    conn = NULL;
61719618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
61853256Speter			    i = 1;
61953256Speter			    if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
62053256Speter				    syslog(LOG_ERR, "ioctl (FIONBIO, 1): %m");
6211553Srgrimes			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
62271399Sdwmalone				(socklen_t *)0);
6231553Srgrimes			    if (debug)
62429602Scharnier				    warnx("accept, ctrl %d", ctrl);
6251553Srgrimes			    if (ctrl < 0) {
6261553Srgrimes				    if (errno != EINTR)
6271553Srgrimes					    syslog(LOG_WARNING,
6281553Srgrimes						"accept (for %s): %m",
62937844Sphk						sep->se_service);
63037816Sphk                                      if (sep->se_accept &&
63137816Sphk                                          sep->se_socktype == SOCK_STREAM)
63237816Sphk                                              close(ctrl);
6331553Srgrimes				    continue;
6341553Srgrimes			    }
63553256Speter			    i = 0;
63653256Speter			    if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
63753256Speter				    syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m");
63853256Speter			    if (ioctl(ctrl, FIONBIO, &i) < 0)
63953256Speter				    syslog(LOG_ERR, "ioctl2(FIONBIO, 0): %m");
64030847Sdima			    if (cpmip(sep, ctrl) < 0) {
64130847Sdima				close(ctrl);
64230847Sdima				continue;
64330847Sdima			    }
644101474Sume			    if (dofork &&
645101474Sume				(conn = search_conn(sep, ctrl)) != NULL &&
646101474Sume				!room_conn(sep, conn)) {
647101474Sume				close(ctrl);
648101474Sume				continue;
649101474Sume			    }
6501553Srgrimes		    } else
6511553Srgrimes			    ctrl = sep->se_fd;
652121766Speter		    if (dolog && !ISWRAP(sep)) {
65371399Sdwmalone			    char pname[INET6_ADDRSTRLEN] = "unknown";
65471399Sdwmalone			    socklen_t sl;
65571399Sdwmalone			    sl = sizeof peermax;
65648382Ssheldonh			    if (getpeername(ctrl, (struct sockaddr *)
65771399Sdwmalone					    &peermax, &sl)) {
65871399Sdwmalone				    sl = sizeof peermax;
65948382Ssheldonh				    if (recvfrom(ctrl, buf, sizeof(buf),
66048382Ssheldonh					MSG_PEEK,
66156590Sshin					(struct sockaddr *)&peermax,
66271399Sdwmalone					&sl) >= 0) {
66356590Sshin				      getnameinfo((struct sockaddr *)&peermax,
66457383Sshin						  peer.sa_len,
66556590Sshin						  pname, sizeof(pname),
66656590Sshin						  NULL, 0,
66756590Sshin						  NI_NUMERICHOST|
66856590Sshin						  NI_WITHSCOPEID);
66956590Sshin				    }
67056590Sshin			    } else {
67156590Sshin			            getnameinfo((struct sockaddr *)&peermax,
67257383Sshin						peer.sa_len,
67356590Sshin						pname, sizeof(pname),
67456590Sshin						NULL, 0,
67556590Sshin						NI_NUMERICHOST|
67656590Sshin						NI_WITHSCOPEID);
67748382Ssheldonh			    }
67871399Sdwmalone			    syslog(LOG_INFO,"%s from %s", sep->se_service, pname);
67948382Ssheldonh		    }
68042122Sdes		    (void) sigblock(SIGBLOCK);
6811553Srgrimes		    pid = 0;
68247972Ssheldonh		    /*
68348958Ssheldonh		     * Fork for all external services, builtins which need to
68448958Ssheldonh		     * fork and anything we're wrapping (as wrapping might
68548958Ssheldonh		     * block or use hosts_options(5) twist).
68647972Ssheldonh		     */
6871553Srgrimes		    if (dofork) {
6881553Srgrimes			    if (sep->se_count++ == 0)
68937856Sache				(void)gettimeofday(&sep->se_time, (struct timezone *)NULL);
69064197Sdwmalone			    else if (toomany > 0 && sep->se_count >= toomany) {
6911553Srgrimes				struct timeval now;
6921553Srgrimes
69337856Sache				(void)gettimeofday(&now, (struct timezone *)NULL);
6941553Srgrimes				if (now.tv_sec - sep->se_time.tv_sec >
6951553Srgrimes				    CNT_INTVL) {
6961553Srgrimes					sep->se_time = now;
6971553Srgrimes					sep->se_count = 1;
6981553Srgrimes				} else {
6991553Srgrimes					syslog(LOG_ERR,
7001553Srgrimes			"%s/%s server failing (looping), service terminated",
7011553Srgrimes					    sep->se_service, sep->se_proto);
70267415Sdwmalone					if (sep->se_accept &&
70367415Sdwmalone					    sep->se_socktype == SOCK_STREAM)
70467415Sdwmalone						close(ctrl);
7051553Srgrimes					close_sep(sep);
706101474Sume					free_conn(conn);
70742122Sdes					sigsetmask(0L);
7081553Srgrimes					if (!timingout) {
7091553Srgrimes						timingout = 1;
7101553Srgrimes						alarm(RETRYTIME);
7111553Srgrimes					}
7121553Srgrimes					continue;
7131553Srgrimes				}
7141553Srgrimes			    }
7151553Srgrimes			    pid = fork();
7161553Srgrimes		    }
7171553Srgrimes		    if (pid < 0) {
7181553Srgrimes			    syslog(LOG_ERR, "fork: %m");
71919618Sjulian			    if (sep->se_accept &&
7201553Srgrimes				sep->se_socktype == SOCK_STREAM)
7211553Srgrimes				    close(ctrl);
722101474Sume			    free_conn(conn);
72342122Sdes			    sigsetmask(0L);
7241553Srgrimes			    sleep(1);
7251553Srgrimes			    continue;
7261553Srgrimes		    }
727101474Sume		    if (pid) {
728101474Sume			addchild_conn(conn, pid);
72919618Sjulian			addchild(sep, pid);
730101474Sume		    }
73142122Sdes		    sigsetmask(0L);
7321553Srgrimes		    if (pid == 0) {
7331553Srgrimes			    if (dofork) {
73448962Ssheldonh				sigaction(SIGALRM, &saalrm, (struct sigaction *)0);
73548962Ssheldonh				sigaction(SIGCHLD, &sachld, (struct sigaction *)0);
73648962Ssheldonh				sigaction(SIGHUP, &sahup, (struct sigaction *)0);
73748962Ssheldonh				/* SIGPIPE reset before exec */
7381553Srgrimes			    }
73935829Sguido			    /*
74035829Sguido			     * Call tcpmux to find the real service to exec.
74135829Sguido			     */
74235829Sguido			    if (sep->se_bi &&
74378694Sdwmalone				sep->se_bi->bi_fn == (bi_fn_t *) tcpmux) {
74435829Sguido				    sep = tcpmux(ctrl);
74535829Sguido				    if (sep == NULL) {
74635829Sguido					    close(ctrl);
74735829Sguido					    _exit(0);
74835829Sguido				    }
74935829Sguido			    }
75048382Ssheldonh			    if (ISWRAP(sep)) {
75148698Ssheldonh				inetd_setproctitle("wrapping", ctrl);
75247972Ssheldonh				service = sep->se_server_name ?
75347972Ssheldonh				    sep->se_server_name : sep->se_service;
754127865Sdwmalone				request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, 0);
75545089Smarkm				fromhost(&req);
75647972Ssheldonh				deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
75747972Ssheldonh				allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
75845089Smarkm				denied = !hosts_access(&req);
75945089Smarkm				if (denied) {
76045089Smarkm				    syslog(deny_severity,
76196224Sume				        "refused connection from %.500s, service %s (%s%s)",
76296224Sume				        eval_client(&req), service, sep->se_proto,
763110802Sume					(whichaf(&req) == AF_INET6) ? "6" : "");
76448382Ssheldonh				    if (sep->se_socktype != SOCK_STREAM)
76548382Ssheldonh					recv(ctrl, buf, sizeof (buf), 0);
76664059Sdwmalone				    if (dofork) {
76764059Sdwmalone					sleep(1);
76848382Ssheldonh					_exit(0);
76964059Sdwmalone				    }
77045089Smarkm				}
771121766Speter				if (dolog) {
77245089Smarkm				    syslog(allow_severity,
77396224Sume				        "connection from %.500s, service %s (%s%s)",
77496224Sume					eval_client(&req), service, sep->se_proto,
775110802Sume					(whichaf(&req) == AF_INET6) ? "6" : "");
77645089Smarkm				}
77745089Smarkm			    }
77819617Sjulian			    if (sep->se_bi) {
7791553Srgrimes				(*sep->se_bi->bi_fn)(ctrl, sep);
78019617Sjulian			    } else {
7811553Srgrimes				if (debug)
78229602Scharnier					warnx("%d execl %s",
78329602Scharnier						getpid(), sep->se_server);
784111324Sdwmalone				/* Clear close-on-exec. */
785111324Sdwmalone				if (fcntl(ctrl, F_SETFD, 0) < 0) {
786111324Sdwmalone					syslog(LOG_ERR,
787111324Sdwmalone					    "%s/%s: fcntl (F_SETFD, 0): %m",
788111324Sdwmalone						sep->se_service, sep->se_proto);
789111324Sdwmalone					_exit(EX_OSERR);
790111324Sdwmalone				}
791111324Sdwmalone				if (ctrl != 0) {
792111324Sdwmalone					dup2(ctrl, 0);
793111324Sdwmalone					close(ctrl);
794111324Sdwmalone				}
7951553Srgrimes				dup2(0, 1);
7961553Srgrimes				dup2(0, 2);
7971553Srgrimes				if ((pwd = getpwnam(sep->se_user)) == NULL) {
7981553Srgrimes					syslog(LOG_ERR,
79956482Scharnier					    "%s/%s: %s: no such user",
8001553Srgrimes						sep->se_service, sep->se_proto,
8011553Srgrimes						sep->se_user);
8021553Srgrimes					if (sep->se_socktype != SOCK_STREAM)
8031553Srgrimes						recv(0, buf, sizeof (buf), 0);
80419617Sjulian					_exit(EX_NOUSER);
8051553Srgrimes				}
80630807Sache				grp = NULL;
80730807Sache				if (   sep->se_group != NULL
80830807Sache				    && (grp = getgrnam(sep->se_group)) == NULL
80930807Sache				   ) {
81030807Sache					syslog(LOG_ERR,
81156482Scharnier					    "%s/%s: %s: no such group",
81230807Sache						sep->se_service, sep->se_proto,
81330807Sache						sep->se_group);
81430807Sache					if (sep->se_socktype != SOCK_STREAM)
81530807Sache						recv(0, buf, sizeof (buf), 0);
81630807Sache					_exit(EX_NOUSER);
81730807Sache				}
81830807Sache				if (grp != NULL)
81930807Sache					pwd->pw_gid = grp->gr_gid;
82021640Speter#ifdef LOGIN_CAP
82130792Sache				if ((lc = login_getclass(sep->se_class)) == NULL) {
82230792Sache					/* error syslogged by getclass */
82330792Sache					syslog(LOG_ERR,
82430792Sache					    "%s/%s: %s: login class error",
82537850Sache						sep->se_service, sep->se_proto,
82637850Sache						sep->se_class);
82730792Sache					if (sep->se_socktype != SOCK_STREAM)
82830792Sache						recv(0, buf, sizeof (buf), 0);
82930792Sache					_exit(EX_NOUSER);
83030792Sache				}
83121640Speter#endif
83212024Speter				if (setsid() < 0) {
83312024Speter					syslog(LOG_ERR,
83412024Speter						"%s: can't setsid(): %m",
83512024Speter						 sep->se_service);
83619617Sjulian					/* _exit(EX_OSERR); not fatal yet */
83712024Speter				}
83821640Speter#ifdef LOGIN_CAP
83921640Speter				if (setusercontext(lc, pwd, pwd->pw_uid,
840109349Srwatson				    LOGIN_SETALL & ~LOGIN_SETMAC)
841108951Srwatson				    != 0) {
84221640Speter					syslog(LOG_ERR,
84321640Speter					 "%s: can't setusercontext(..%s..): %m",
84421640Speter					 sep->se_service, sep->se_user);
84521640Speter					_exit(EX_OSERR);
84621640Speter				}
847111323Sdwmalone				login_close(lc);
84821640Speter#else
8491553Srgrimes				if (pwd->pw_uid) {
85012024Speter					if (setlogin(sep->se_user) < 0) {
85112024Speter						syslog(LOG_ERR,
85212024Speter						 "%s: can't setlogin(%s): %m",
85312024Speter						 sep->se_service, sep->se_user);
85419617Sjulian						/* _exit(EX_OSERR); not yet */
85512024Speter					}
8561553Srgrimes					if (setgid(pwd->pw_gid) < 0) {
8571553Srgrimes						syslog(LOG_ERR,
8588857Srgrimes						  "%s: can't set gid %d: %m",
8591553Srgrimes						  sep->se_service, pwd->pw_gid);
86019617Sjulian						_exit(EX_OSERR);
8611553Srgrimes					}
8621553Srgrimes					(void) initgroups(pwd->pw_name,
8631553Srgrimes							pwd->pw_gid);
8641553Srgrimes					if (setuid(pwd->pw_uid) < 0) {
8651553Srgrimes						syslog(LOG_ERR,
8668857Srgrimes						  "%s: can't set uid %d: %m",
8671553Srgrimes						  sep->se_service, pwd->pw_uid);
86819617Sjulian						_exit(EX_OSERR);
8691553Srgrimes					}
8701553Srgrimes				}
87121640Speter#endif
87235948Sbde				sigaction(SIGPIPE, &sapipe,
87335948Sbde				    (struct sigaction *)0);
8741553Srgrimes				execv(sep->se_server, sep->se_argv);
87545089Smarkm				syslog(LOG_ERR,
87645089Smarkm				    "cannot execute %s: %m", sep->se_server);
8771553Srgrimes				if (sep->se_socktype != SOCK_STREAM)
8781553Srgrimes					recv(0, buf, sizeof (buf), 0);
8791553Srgrimes			    }
88047972Ssheldonh			    if (dofork)
88147972Ssheldonh				_exit(0);
8821553Srgrimes		    }
88319618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
8841553Srgrimes			    close(ctrl);
8851553Srgrimes		}
8861553Srgrimes	}
8871553Srgrimes}
8881553Srgrimes
88919618Sjulian/*
89042122Sdes * Add a signal flag to the signal flag queue for later handling
89142122Sdes */
89242122Sdes
89378694Sdwmalonevoid
89498558Sjmallettflag_signal(int c)
89542122Sdes{
89669546Sdwmalone	char ch = c;
89769546Sdwmalone
89869546Sdwmalone	if (write(signalpipe[1], &ch, 1) != 1) {
89942250Sdes		syslog(LOG_ERR, "write: %m");
90048985Ssheldonh		_exit(EX_OSERR);
90142250Sdes	}
90242122Sdes}
90342122Sdes
90442122Sdes/*
90519618Sjulian * Record a new child pid for this service. If we've reached the
90619618Sjulian * limit on children, then stop accepting incoming requests.
90719618Sjulian */
90819618Sjulian
9091553Srgrimesvoid
91019618Sjulianaddchild(struct servtab *sep, pid_t pid)
91119618Sjulian{
91264197Sdwmalone	if (sep->se_maxchild <= 0)
91364197Sdwmalone		return;
91419618Sjulian#ifdef SANITY_CHECK
91519618Sjulian	if (sep->se_numchild >= sep->se_maxchild) {
91619618Sjulian		syslog(LOG_ERR, "%s: %d >= %d",
91719618Sjulian		    __FUNCTION__, sep->se_numchild, sep->se_maxchild);
91819618Sjulian		exit(EX_SOFTWARE);
91919618Sjulian	}
92019618Sjulian#endif
92119618Sjulian	sep->se_pids[sep->se_numchild++] = pid;
92219618Sjulian	if (sep->se_numchild == sep->se_maxchild)
92319618Sjulian		disable(sep);
92419618Sjulian}
92519618Sjulian
92619618Sjulian/*
92719618Sjulian * Some child process has exited. See if it's on somebody's list.
92819618Sjulian */
92919618Sjulian
93019618Sjulianvoid
93198561Sjmallettflag_reapchild(int signo __unused)
9321553Srgrimes{
93342250Sdes	flag_signal('C');
93442122Sdes}
93542122Sdes
93642122Sdesvoid
93798558Sjmallettreapchild(void)
93842122Sdes{
93919618Sjulian	int k, status;
9401553Srgrimes	pid_t pid;
9411553Srgrimes	struct servtab *sep;
9421553Srgrimes
9431553Srgrimes	for (;;) {
9441553Srgrimes		pid = wait3(&status, WNOHANG, (struct rusage *)0);
9451553Srgrimes		if (pid <= 0)
9461553Srgrimes			break;
9471553Srgrimes		if (debug)
948102939Sdwmalone			warnx("%d reaped, %s %u", pid,
949102939Sdwmalone			    WIFEXITED(status) ? "status" : "signal",
950102939Sdwmalone			    WIFEXITED(status) ? WEXITSTATUS(status)
951102939Sdwmalone				: WTERMSIG(status));
95219618Sjulian		for (sep = servtab; sep; sep = sep->se_next) {
95319618Sjulian			for (k = 0; k < sep->se_numchild; k++)
95419618Sjulian				if (sep->se_pids[k] == pid)
95519618Sjulian					break;
95619618Sjulian			if (k == sep->se_numchild)
95719618Sjulian				continue;
95819618Sjulian			if (sep->se_numchild == sep->se_maxchild)
95919618Sjulian				enable(sep);
96019618Sjulian			sep->se_pids[k] = sep->se_pids[--sep->se_numchild];
961102939Sdwmalone			if (WIFSIGNALED(status) || WEXITSTATUS(status))
96219618Sjulian				syslog(LOG_WARNING,
963102939Sdwmalone				    "%s[%d]: exited, %s %u",
964102939Sdwmalone				    sep->se_server, pid,
965102939Sdwmalone				    WIFEXITED(status) ? "status" : "signal",
966102939Sdwmalone				    WIFEXITED(status) ? WEXITSTATUS(status)
967102939Sdwmalone					: WTERMSIG(status));
96819618Sjulian			break;
96919618Sjulian		}
970101474Sume		reapchild_conn(pid);
9711553Srgrimes	}
9721553Srgrimes}
9731553Srgrimes
9741553Srgrimesvoid
97598561Sjmallettflag_config(int signo __unused)
9761553Srgrimes{
97742250Sdes	flag_signal('H');
97842122Sdes}
97942122Sdes
98078694Sdwmalonevoid
98198558Sjmallettconfig(void)
98242122Sdes{
98319618Sjulian	struct servtab *sep, *new, **sepp;
98442122Sdes	long omask;
985100127Salfred	int new_nomapped;
986111323Sdwmalone#ifdef LOGIN_CAP
987111323Sdwmalone	login_cap_t *lc = NULL;
988111323Sdwmalone#endif
9891553Srgrimes
9901553Srgrimes	if (!setconfig()) {
9911553Srgrimes		syslog(LOG_ERR, "%s: %m", CONFIG);
9921553Srgrimes		return;
9931553Srgrimes	}
9941553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
9951553Srgrimes		sep->se_checked = 0;
99619618Sjulian	while ((new = getconfigent())) {
99730807Sache		if (getpwnam(new->se_user) == NULL) {
9981553Srgrimes			syslog(LOG_ERR,
99956482Scharnier				"%s/%s: no such user '%s', service ignored",
100019618Sjulian				new->se_service, new->se_proto, new->se_user);
10011553Srgrimes			continue;
10021553Srgrimes		}
100330807Sache		if (new->se_group && getgrnam(new->se_group) == NULL) {
100430807Sache			syslog(LOG_ERR,
100556482Scharnier				"%s/%s: no such group '%s', service ignored",
100630807Sache				new->se_service, new->se_proto, new->se_group);
100730807Sache			continue;
100830807Sache		}
100930792Sache#ifdef LOGIN_CAP
1010111323Sdwmalone		if ((lc = login_getclass(new->se_class)) == NULL) {
101130792Sache			/* error syslogged by getclass */
101230792Sache			syslog(LOG_ERR,
101337850Sache				"%s/%s: %s: login class error, service ignored",
101437850Sache				new->se_service, new->se_proto, new->se_class);
101530792Sache			continue;
101630792Sache		}
1017111323Sdwmalone		login_close(lc);
101830792Sache#endif
1019100127Salfred		new_nomapped = new->se_nomapped;
10201553Srgrimes		for (sep = servtab; sep; sep = sep->se_next)
102119618Sjulian			if (strcmp(sep->se_service, new->se_service) == 0 &&
102256590Sshin			    strcmp(sep->se_proto, new->se_proto) == 0 &&
1023100127Salfred			    sep->se_rpc == new->se_rpc &&
102478356Sdwmalone			    sep->se_socktype == new->se_socktype &&
102556590Sshin			    sep->se_family == new->se_family)
10261553Srgrimes				break;
10271553Srgrimes		if (sep != 0) {
10281553Srgrimes			int i;
10291553Srgrimes
103098611Sjmallett#define SWAP(t,a, b) { t c = a; a = b; b = c; }
103142122Sdes			omask = sigblock(SIGBLOCK);
103256590Sshin			if (sep->se_nomapped != new->se_nomapped) {
1033100127Salfred				/* for rpc keep old nommaped till unregister */
1034100127Salfred				if (!sep->se_rpc)
1035100127Salfred					sep->se_nomapped = new->se_nomapped;
103656590Sshin				sep->se_reset = 1;
103756590Sshin			}
103819618Sjulian			/* copy over outstanding child pids */
103964197Sdwmalone			if (sep->se_maxchild > 0 && new->se_maxchild > 0) {
104019618Sjulian				new->se_numchild = sep->se_numchild;
104119618Sjulian				if (new->se_numchild > new->se_maxchild)
104219618Sjulian					new->se_numchild = new->se_maxchild;
104319618Sjulian				memcpy(new->se_pids, sep->se_pids,
104419618Sjulian				    new->se_numchild * sizeof(*new->se_pids));
104519618Sjulian			}
104698611Sjmallett			SWAP(pid_t *, sep->se_pids, new->se_pids);
104719618Sjulian			sep->se_maxchild = new->se_maxchild;
104819618Sjulian			sep->se_numchild = new->se_numchild;
104930847Sdima			sep->se_maxcpm = new->se_maxcpm;
1050101474Sume			resize_conn(sep, new->se_maxperip);
1051101474Sume			sep->se_maxperip = new->se_maxperip;
105266544Sdwmalone			sep->se_bi = new->se_bi;
105319618Sjulian			/* might need to turn on or off service now */
105419618Sjulian			if (sep->se_fd >= 0) {
105564197Sdwmalone			      if (sep->se_maxchild > 0
105619618Sjulian				  && sep->se_numchild == sep->se_maxchild) {
105719618Sjulian				      if (FD_ISSET(sep->se_fd, &allsock))
105819618Sjulian					  disable(sep);
105919618Sjulian			      } else {
106019618Sjulian				      if (!FD_ISSET(sep->se_fd, &allsock))
106119618Sjulian					  enable(sep);
106219618Sjulian			      }
106319618Sjulian			}
106419618Sjulian			sep->se_accept = new->se_accept;
106598611Sjmallett			SWAP(char *, sep->se_user, new->se_user);
106698611Sjmallett			SWAP(char *, sep->se_group, new->se_group);
106730792Sache#ifdef LOGIN_CAP
106898611Sjmallett			SWAP(char *, sep->se_class, new->se_class);
106930792Sache#endif
107098611Sjmallett			SWAP(char *, sep->se_server, new->se_server);
107198611Sjmallett			SWAP(char *, sep->se_server_name, new->se_server_name);
10721553Srgrimes			for (i = 0; i < MAXARGV; i++)
107398611Sjmallett				SWAP(char *, sep->se_argv[i], new->se_argv[i]);
107456590Sshin#ifdef IPSEC
107598611Sjmallett			SWAP(char *, sep->se_policy, new->se_policy);
107656590Sshin			ipsecsetup(sep);
107756590Sshin#endif
107842122Sdes			sigsetmask(omask);
107919618Sjulian			freeconfig(new);
10801553Srgrimes			if (debug)
10811553Srgrimes				print_service("REDO", sep);
10821553Srgrimes		} else {
108319618Sjulian			sep = enter(new);
10841553Srgrimes			if (debug)
10851553Srgrimes				print_service("ADD ", sep);
10861553Srgrimes		}
10871553Srgrimes		sep->se_checked = 1;
10881553Srgrimes		if (ISMUX(sep)) {
10891553Srgrimes			sep->se_fd = -1;
10901553Srgrimes			continue;
10911553Srgrimes		}
109256590Sshin		switch (sep->se_family) {
109356590Sshin		case AF_INET:
1094102938Sdwmalone			if (!v4bind_ok) {
109556590Sshin				sep->se_fd = -1;
109656590Sshin				continue;
109756590Sshin			}
109856590Sshin			break;
109956590Sshin#ifdef INET6
110056590Sshin		case AF_INET6:
1101102938Sdwmalone			if (!v6bind_ok) {
110256590Sshin				sep->se_fd = -1;
110356590Sshin				continue;
110456590Sshin			}
110556590Sshin			break;
110656590Sshin#endif
110756590Sshin		}
11082657Scsgr		if (!sep->se_rpc) {
110978356Sdwmalone			if (sep->se_family != AF_UNIX) {
111078356Sdwmalone				sp = getservbyname(sep->se_service, sep->se_proto);
111178356Sdwmalone				if (sp == 0) {
111278356Sdwmalone					syslog(LOG_ERR, "%s/%s: unknown service",
111378356Sdwmalone					sep->se_service, sep->se_proto);
111478356Sdwmalone					sep->se_checked = 0;
111578356Sdwmalone					continue;
111678356Sdwmalone				}
11172657Scsgr			}
111856590Sshin			switch (sep->se_family) {
111956590Sshin			case AF_INET:
112056590Sshin				if (sp->s_port != sep->se_ctrladdr4.sin_port) {
112156590Sshin					sep->se_ctrladdr4.sin_port =
112256590Sshin						sp->s_port;
112356590Sshin					sep->se_reset = 1;
112456590Sshin				}
112556590Sshin				break;
112656590Sshin#ifdef INET6
112756590Sshin			case AF_INET6:
112856590Sshin				if (sp->s_port !=
112956590Sshin				    sep->se_ctrladdr6.sin6_port) {
113056590Sshin					sep->se_ctrladdr6.sin6_port =
113156590Sshin						sp->s_port;
113256590Sshin					sep->se_reset = 1;
113356590Sshin				}
113456590Sshin				break;
113556590Sshin#endif
11362657Scsgr			}
113756590Sshin			if (sep->se_reset != 0 && sep->se_fd >= 0)
113856590Sshin				close_sep(sep);
11392657Scsgr		} else {
11402657Scsgr			rpc = getrpcbyname(sep->se_service);
11412657Scsgr			if (rpc == 0) {
114252219Scharnier				syslog(LOG_ERR, "%s/%s unknown RPC service",
11432657Scsgr					sep->se_service, sep->se_proto);
11442657Scsgr				if (sep->se_fd != -1)
11452657Scsgr					(void) close(sep->se_fd);
11462657Scsgr				sep->se_fd = -1;
11472657Scsgr					continue;
11482657Scsgr			}
1149100127Salfred			if (sep->se_reset != 0 ||
1150100127Salfred			    rpc->r_number != sep->se_rpc_prog) {
11512657Scsgr				if (sep->se_rpc_prog)
11522657Scsgr					unregisterrpc(sep);
11532657Scsgr				sep->se_rpc_prog = rpc->r_number;
11542657Scsgr				if (sep->se_fd != -1)
11552657Scsgr					(void) close(sep->se_fd);
11562657Scsgr				sep->se_fd = -1;
11572657Scsgr			}
1158100127Salfred			sep->se_nomapped = new_nomapped;
11591553Srgrimes		}
1160100127Salfred		sep->se_reset = 0;
11611553Srgrimes		if (sep->se_fd == -1)
11621553Srgrimes			setup(sep);
11631553Srgrimes	}
11641553Srgrimes	endconfig();
11651553Srgrimes	/*
11661553Srgrimes	 * Purge anything not looked at above.
11671553Srgrimes	 */
116842122Sdes	omask = sigblock(SIGBLOCK);
11691553Srgrimes	sepp = &servtab;
117019617Sjulian	while ((sep = *sepp)) {
11711553Srgrimes		if (sep->se_checked) {
11721553Srgrimes			sepp = &sep->se_next;
11731553Srgrimes			continue;
11741553Srgrimes		}
11751553Srgrimes		*sepp = sep->se_next;
11761553Srgrimes		if (sep->se_fd >= 0)
11771553Srgrimes			close_sep(sep);
11781553Srgrimes		if (debug)
11791553Srgrimes			print_service("FREE", sep);
11802657Scsgr		if (sep->se_rpc && sep->se_rpc_prog > 0)
11812657Scsgr			unregisterrpc(sep);
11821553Srgrimes		freeconfig(sep);
118371399Sdwmalone		free(sep);
11841553Srgrimes	}
118542122Sdes	(void) sigsetmask(omask);
11861553Srgrimes}
11871553Srgrimes
11881553Srgrimesvoid
118998558Sjmallettunregisterrpc(struct servtab *sep)
11902657Scsgr{
119178694Sdwmalone        u_int i;
11922657Scsgr        struct servtab *sepp;
119342122Sdes	long omask;
1194100127Salfred	struct netconfig *netid4, *netid6;
11952657Scsgr
119642122Sdes	omask = sigblock(SIGBLOCK);
1197100127Salfred	netid4 = sep->se_socktype == SOCK_DGRAM ? udpconf : tcpconf;
1198100127Salfred	netid6 = sep->se_socktype == SOCK_DGRAM ? udp6conf : tcp6conf;
1199100127Salfred	if (sep->se_family == AF_INET)
1200100127Salfred		netid6 = NULL;
1201100127Salfred	else if (sep->se_nomapped)
1202100127Salfred		netid4 = NULL;
1203100127Salfred	/*
1204100127Salfred	 * Conflict if same prog and protocol - In that case one should look
1205100127Salfred	 * to versions, but it is not interesting: having separate servers for
1206100127Salfred	 * different versions does not work well.
1207100127Salfred	 * Therefore one do not unregister if there is a conflict.
1208100127Salfred	 * There is also transport conflict if destroying INET when INET46
1209100127Salfred	 * exists, or destroying INET46 when INET exists
1210100127Salfred	 */
12112657Scsgr        for (sepp = servtab; sepp; sepp = sepp->se_next) {
12122657Scsgr                if (sepp == sep)
12132657Scsgr                        continue;
1214100127Salfred		if (sepp->se_checked == 0 ||
12152657Scsgr                    !sepp->se_rpc ||
1216100127Salfred		    strcmp(sep->se_proto, sepp->se_proto) != 0 ||
12172657Scsgr                    sep->se_rpc_prog != sepp->se_rpc_prog)
12182657Scsgr			continue;
1219100127Salfred		if (sepp->se_family == AF_INET)
1220100127Salfred			netid4 = NULL;
1221100127Salfred		if (sepp->se_family == AF_INET6) {
1222100127Salfred			netid6 = NULL;
1223100127Salfred			if (!sep->se_nomapped)
1224100127Salfred				netid4 = NULL;
1225100127Salfred		}
1226100127Salfred		if (netid4 == NULL && netid6 == NULL)
1227100127Salfred			return;
12282657Scsgr        }
12292657Scsgr        if (debug)
12302657Scsgr                print_service("UNREG", sep);
1231100127Salfred        for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
1232100127Salfred		if (netid4)
1233100127Salfred			rpcb_unset(sep->se_rpc_prog, i, netid4);
1234100127Salfred		if (netid6)
1235100127Salfred			rpcb_unset(sep->se_rpc_prog, i, netid6);
1236100127Salfred	}
12372657Scsgr        if (sep->se_fd != -1)
12382657Scsgr                (void) close(sep->se_fd);
12392657Scsgr        sep->se_fd = -1;
124042122Sdes	(void) sigsetmask(omask);
12412657Scsgr}
12422657Scsgr
12432657Scsgrvoid
124498561Sjmallettflag_retry(int signo __unused)
12451553Srgrimes{
124642250Sdes	flag_signal('A');
124742122Sdes}
124842122Sdes
124942122Sdesvoid
125098558Sjmallettretry(void)
125142122Sdes{
12521553Srgrimes	struct servtab *sep;
12531553Srgrimes
12541553Srgrimes	timingout = 0;
12551553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
125619617Sjulian		if (sep->se_fd == -1 && !ISMUX(sep))
12571553Srgrimes			setup(sep);
12581553Srgrimes}
12591553Srgrimes
12601553Srgrimesvoid
126198558Sjmallettsetup(struct servtab *sep)
12621553Srgrimes{
12631553Srgrimes	int on = 1;
12641553Srgrimes
126556590Sshin	if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
12661553Srgrimes		if (debug)
126729602Scharnier			warn("socket failed on %s/%s",
126829602Scharnier				sep->se_service, sep->se_proto);
12691553Srgrimes		syslog(LOG_ERR, "%s/%s: socket: %m",
12701553Srgrimes		    sep->se_service, sep->se_proto);
12711553Srgrimes		return;
12721553Srgrimes	}
1273111324Sdwmalone	/* Set all listening sockets to close-on-exec. */
1274111324Sdwmalone	if (fcntl(sep->se_fd, F_SETFD, FD_CLOEXEC) < 0) {
1275111324Sdwmalone		syslog(LOG_ERR, "%s/%s: fcntl (F_SETFD, FD_CLOEXEC): %m",
1276111324Sdwmalone		    sep->se_service, sep->se_proto);
1277111324Sdwmalone		close(sep->se_fd);
1278111324Sdwmalone		return;
1279111324Sdwmalone	}
12801553Srgrimes#define	turnon(fd, opt) \
12811553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
12821553Srgrimes	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
12831553Srgrimes	    turnon(sep->se_fd, SO_DEBUG) < 0)
12841553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
12851553Srgrimes	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
12861553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
128725253Swollman#ifdef SO_PRIVSTATE
128813956Swollman	if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
128913956Swollman		syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
129025253Swollman#endif
129156590Sshin	/* tftpd opens a new connection then needs more infos */
129256590Sshin	if ((sep->se_family == AF_INET6) &&
129356590Sshin	    (strcmp(sep->se_proto, "udp") == 0) &&
129456590Sshin	    (sep->se_accept == 0) &&
1295121559Sume	    (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
129656590Sshin			(char *)&on, sizeof (on)) < 0))
129756590Sshin		syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m");
129858935Sume	if (sep->se_family == AF_INET6) {
129958935Sume		int flag = sep->se_nomapped ? 1 : 0;
1300100505Sume		if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_V6ONLY,
130158935Sume			       (char *)&flag, sizeof (flag)) < 0)
1302100505Sume			syslog(LOG_ERR, "setsockopt (IPV6_V6ONLY): %m");
130358935Sume	}
13041553Srgrimes#undef turnon
130536042Sguido	if (sep->se_type == TTCP_TYPE)
130636042Sguido		if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH,
130736042Sguido		    (char *)&on, sizeof (on)) < 0)
130836042Sguido			syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m");
130956590Sshin#ifdef IPV6_FAITH
131056590Sshin	if (sep->se_type == FAITH_TYPE) {
131156590Sshin		if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH, &on,
131256590Sshin				sizeof(on)) < 0) {
131356590Sshin			syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m");
131456590Sshin		}
131556590Sshin	}
131656590Sshin#endif
131756590Sshin#ifdef IPSEC
131856590Sshin	ipsecsetup(sep);
131956590Sshin#endif
132078356Sdwmalone	if (sep->se_family == AF_UNIX) {
132178356Sdwmalone		(void) unlink(sep->se_ctrladdr_un.sun_path);
132278356Sdwmalone		umask(0777); /* Make socket with conservative permissions */
132378356Sdwmalone	}
13241553Srgrimes	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
132556590Sshin	    sep->se_ctrladdr_size) < 0) {
13261553Srgrimes		if (debug)
132729602Scharnier			warn("bind failed on %s/%s",
132829602Scharnier				sep->se_service, sep->se_proto);
13291553Srgrimes		syslog(LOG_ERR, "%s/%s: bind: %m",
13301553Srgrimes		    sep->se_service, sep->se_proto);
13311553Srgrimes		(void) close(sep->se_fd);
13321553Srgrimes		sep->se_fd = -1;
13331553Srgrimes		if (!timingout) {
13341553Srgrimes			timingout = 1;
13351553Srgrimes			alarm(RETRYTIME);
13361553Srgrimes		}
133778356Sdwmalone		if (sep->se_family == AF_UNIX)
133878356Sdwmalone			umask(mask);
13391553Srgrimes		return;
13401553Srgrimes	}
134178356Sdwmalone	if (sep->se_family == AF_UNIX) {
134278356Sdwmalone		/* Ick - fch{own,mod} don't work on Unix domain sockets */
134378356Sdwmalone		if (chown(sep->se_service, sep->se_sockuid, sep->se_sockgid) < 0)
134478356Sdwmalone			syslog(LOG_ERR, "chown socket: %m");
134578356Sdwmalone		if (chmod(sep->se_service, sep->se_sockmode) < 0)
134678356Sdwmalone			syslog(LOG_ERR, "chmod socket: %m");
134778356Sdwmalone		umask(mask);
134878356Sdwmalone	}
13492657Scsgr        if (sep->se_rpc) {
135078694Sdwmalone		u_int i;
135171399Sdwmalone		socklen_t len = sep->se_ctrladdr_size;
1352100127Salfred		struct netconfig *netid, *netid2 = NULL;
1353100127Salfred		struct sockaddr_in sock;
1354100127Salfred		struct netbuf nbuf, nbuf2;
13552657Scsgr
13568857Srgrimes                if (getsockname(sep->se_fd,
13572657Scsgr				(struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
13582657Scsgr                        syslog(LOG_ERR, "%s/%s: getsockname: %m",
13592657Scsgr                               sep->se_service, sep->se_proto);
13602657Scsgr                        (void) close(sep->se_fd);
13612657Scsgr                        sep->se_fd = -1;
13628857Srgrimes                        return;
13632657Scsgr                }
1364100127Salfred		nbuf.buf = &sep->se_ctrladdr;
1365100127Salfred		nbuf.len = sep->se_ctrladdr.sa_len;
1366100127Salfred		if (sep->se_family == AF_INET)
1367100127Salfred			netid = sep->se_socktype==SOCK_DGRAM? udpconf:tcpconf;
1368100127Salfred		else  {
1369100127Salfred			netid = sep->se_socktype==SOCK_DGRAM? udp6conf:tcp6conf;
1370100127Salfred			if (!sep->se_nomapped) { /* INET and INET6 */
1371100127Salfred				netid2 = netid==udp6conf? udpconf:tcpconf;
1372100127Salfred				memset(&sock, 0, sizeof sock);	/* ADDR_ANY */
1373100127Salfred				nbuf2.buf = &sock;
1374100127Salfred				nbuf2.len = sock.sin_len = sizeof sock;
1375100127Salfred				sock.sin_family = AF_INET;
1376100127Salfred				sock.sin_port = sep->se_ctrladdr6.sin6_port;
1377100127Salfred			}
1378100127Salfred		}
13792657Scsgr                if (debug)
13802657Scsgr                        print_service("REG ", sep);
13812657Scsgr                for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
1382100127Salfred			rpcb_unset(sep->se_rpc_prog, i, netid);
1383100127Salfred			rpcb_set(sep->se_rpc_prog, i, netid, &nbuf);
1384100127Salfred			if (netid2) {
1385100127Salfred				rpcb_unset(sep->se_rpc_prog, i, netid2);
1386100127Salfred				rpcb_set(sep->se_rpc_prog, i, netid2, &nbuf2);
1387100127Salfred			}
13882657Scsgr                }
13892657Scsgr        }
13901553Srgrimes	if (sep->se_socktype == SOCK_STREAM)
139117197Sdg		listen(sep->se_fd, 64);
139219618Sjulian	enable(sep);
13931553Srgrimes	if (debug) {
139429602Scharnier		warnx("registered %s on %d",
13951553Srgrimes			sep->se_server, sep->se_fd);
13961553Srgrimes	}
13971553Srgrimes}
13981553Srgrimes
139956590Sshin#ifdef IPSEC
140056590Sshinvoid
140156590Sshinipsecsetup(sep)
140256590Sshin	struct servtab *sep;
140356590Sshin{
140456590Sshin	char *buf;
140556590Sshin	char *policy_in = NULL;
140656590Sshin	char *policy_out = NULL;
140756590Sshin	int level;
140856590Sshin	int opt;
140956590Sshin
141056590Sshin	switch (sep->se_family) {
141156590Sshin	case AF_INET:
141256590Sshin		level = IPPROTO_IP;
141356590Sshin		opt = IP_IPSEC_POLICY;
141456590Sshin		break;
141556590Sshin#ifdef INET6
141656590Sshin	case AF_INET6:
141756590Sshin		level = IPPROTO_IPV6;
141856590Sshin		opt = IPV6_IPSEC_POLICY;
141956590Sshin		break;
142056590Sshin#endif
142156590Sshin	default:
142256590Sshin		return;
142356590Sshin	}
142456590Sshin
142556590Sshin	if (!sep->se_policy || sep->se_policy[0] == '\0') {
142678694Sdwmalone		static char def_in[] = "in entrust", def_out[] = "out entrust";
142778694Sdwmalone		policy_in = def_in;
142878694Sdwmalone		policy_out = def_out;
142956590Sshin	} else {
143056590Sshin		if (!strncmp("in", sep->se_policy, 2))
143156590Sshin			policy_in = sep->se_policy;
143256590Sshin		else if (!strncmp("out", sep->se_policy, 3))
143356590Sshin			policy_out = sep->se_policy;
143456590Sshin		else {
143556590Sshin			syslog(LOG_ERR, "invalid security policy \"%s\"",
143656590Sshin				sep->se_policy);
143756590Sshin			return;
143856590Sshin		}
143956590Sshin	}
144056590Sshin
144156590Sshin	if (policy_in != NULL) {
144256590Sshin		buf = ipsec_set_policy(policy_in, strlen(policy_in));
144356590Sshin		if (buf != NULL) {
144456590Sshin			if (setsockopt(sep->se_fd, level, opt,
144556675Sshin					buf, ipsec_get_policylen(buf)) < 0 &&
144656759Sshin			    debug != 0)
144756759Sshin				warnx("%s/%s: ipsec initialization failed; %s",
144856759Sshin				      sep->se_service, sep->se_proto,
144956759Sshin				      policy_in);
145056590Sshin			free(buf);
145156590Sshin		} else
145256590Sshin			syslog(LOG_ERR, "invalid security policy \"%s\"",
145356590Sshin				policy_in);
145456590Sshin	}
145556590Sshin	if (policy_out != NULL) {
145656590Sshin		buf = ipsec_set_policy(policy_out, strlen(policy_out));
145756590Sshin		if (buf != NULL) {
145856590Sshin			if (setsockopt(sep->se_fd, level, opt,
145956675Sshin					buf, ipsec_get_policylen(buf)) < 0 &&
146056759Sshin			    debug != 0)
146156759Sshin				warnx("%s/%s: ipsec initialization failed; %s",
146256759Sshin				      sep->se_service, sep->se_proto,
146356759Sshin				      policy_out);
146456590Sshin			free(buf);
146556590Sshin		} else
146656590Sshin			syslog(LOG_ERR, "invalid security policy \"%s\"",
146756590Sshin				policy_out);
146856590Sshin	}
146956590Sshin}
147056590Sshin#endif
147156590Sshin
14721553Srgrimes/*
14731553Srgrimes * Finish with a service and its socket.
14741553Srgrimes */
14751553Srgrimesvoid
147698558Sjmallettclose_sep(struct servtab *sep)
14771553Srgrimes{
14781553Srgrimes	if (sep->se_fd >= 0) {
147919618Sjulian		if (FD_ISSET(sep->se_fd, &allsock))
148019618Sjulian			disable(sep);
14811553Srgrimes		(void) close(sep->se_fd);
14821553Srgrimes		sep->se_fd = -1;
14831553Srgrimes	}
14841553Srgrimes	sep->se_count = 0;
148519618Sjulian	sep->se_numchild = 0;	/* forget about any existing children */
14861553Srgrimes}
14871553Srgrimes
148848467Ssheldonhint
148998558Sjmallettmatchservent(const char *name1, const char *name2, const char *proto)
149048467Ssheldonh{
149178356Sdwmalone	char **alias, *p;
149248467Ssheldonh	struct servent *se;
149348467Ssheldonh
149478356Sdwmalone	if (strcmp(proto, "unix") == 0) {
149578356Sdwmalone		if ((p = strrchr(name1, '/')) != NULL)
149678356Sdwmalone			name1 = p + 1;
149778356Sdwmalone		if ((p = strrchr(name2, '/')) != NULL)
149878356Sdwmalone			name2 = p + 1;
149978356Sdwmalone	}
150049026Sdes	if (strcmp(name1, name2) == 0)
150149026Sdes		return(1);
150248467Ssheldonh	if ((se = getservbyname(name1, proto)) != NULL) {
150348467Ssheldonh		if (strcmp(name2, se->s_name) == 0)
150448467Ssheldonh			return(1);
150548467Ssheldonh		for (alias = se->s_aliases; *alias; alias++)
150648467Ssheldonh			if (strcmp(name2, *alias) == 0)
150748467Ssheldonh				return(1);
150848467Ssheldonh	}
150948467Ssheldonh	return(0);
151048467Ssheldonh}
151148467Ssheldonh
15121553Srgrimesstruct servtab *
151398558Sjmallettenter(struct servtab *cp)
15141553Srgrimes{
15151553Srgrimes	struct servtab *sep;
151642122Sdes	long omask;
15171553Srgrimes
15181553Srgrimes	sep = (struct servtab *)malloc(sizeof (*sep));
15191553Srgrimes	if (sep == (struct servtab *)0) {
152049102Ssheldonh		syslog(LOG_ERR, "malloc: %m");
152119617Sjulian		exit(EX_OSERR);
15221553Srgrimes	}
15231553Srgrimes	*sep = *cp;
15241553Srgrimes	sep->se_fd = -1;
152542122Sdes	omask = sigblock(SIGBLOCK);
15261553Srgrimes	sep->se_next = servtab;
15271553Srgrimes	servtab = sep;
152842122Sdes	sigsetmask(omask);
15291553Srgrimes	return (sep);
15301553Srgrimes}
15311553Srgrimes
153219618Sjulianvoid
153398558Sjmallettenable(struct servtab *sep)
153419618Sjulian{
153519618Sjulian	if (debug)
153629602Scharnier		warnx(
153719618Sjulian		    "enabling %s, fd %d", sep->se_service, sep->se_fd);
153819618Sjulian#ifdef SANITY_CHECK
153919618Sjulian	if (sep->se_fd < 0) {
154019618Sjulian		syslog(LOG_ERR,
154119618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
154219618Sjulian		exit(EX_SOFTWARE);
154319618Sjulian	}
154419618Sjulian	if (ISMUX(sep)) {
154519618Sjulian		syslog(LOG_ERR,
154619618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
154719618Sjulian		exit(EX_SOFTWARE);
154819618Sjulian	}
154919618Sjulian	if (FD_ISSET(sep->se_fd, &allsock)) {
155019618Sjulian		syslog(LOG_ERR,
155119618Sjulian		    "%s: %s: not off", __FUNCTION__, sep->se_service);
155219618Sjulian		exit(EX_SOFTWARE);
155319618Sjulian	}
155448991Ssheldonh	nsock++;
155519618Sjulian#endif
155619618Sjulian	FD_SET(sep->se_fd, &allsock);
155719618Sjulian	if (sep->se_fd > maxsock)
155819618Sjulian		maxsock = sep->se_fd;
155919618Sjulian}
156019618Sjulian
156119618Sjulianvoid
156298558Sjmallettdisable(struct servtab *sep)
156319618Sjulian{
156419618Sjulian	if (debug)
156529602Scharnier		warnx(
156619618Sjulian		    "disabling %s, fd %d", sep->se_service, sep->se_fd);
156719618Sjulian#ifdef SANITY_CHECK
156819618Sjulian	if (sep->se_fd < 0) {
156919618Sjulian		syslog(LOG_ERR,
157019618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
157119618Sjulian		exit(EX_SOFTWARE);
157219618Sjulian	}
157319618Sjulian	if (ISMUX(sep)) {
157419618Sjulian		syslog(LOG_ERR,
157519618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
157619618Sjulian		exit(EX_SOFTWARE);
157719618Sjulian	}
157819618Sjulian	if (!FD_ISSET(sep->se_fd, &allsock)) {
157919618Sjulian		syslog(LOG_ERR,
158019618Sjulian		    "%s: %s: not on", __FUNCTION__, sep->se_service);
158119618Sjulian		exit(EX_SOFTWARE);
158219618Sjulian	}
158319618Sjulian	if (nsock == 0) {
158419618Sjulian		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
158519618Sjulian		exit(EX_SOFTWARE);
158619618Sjulian	}
158748991Ssheldonh	nsock--;
158819618Sjulian#endif
158919618Sjulian	FD_CLR(sep->se_fd, &allsock);
159019618Sjulian	if (sep->se_fd == maxsock)
159119618Sjulian		maxsock--;
159219618Sjulian}
159319618Sjulian
15941553SrgrimesFILE	*fconfig = NULL;
15951553Srgrimesstruct	servtab serv;
15961553Srgrimeschar	line[LINE_MAX];
15971553Srgrimes
15981553Srgrimesint
159998558Sjmallettsetconfig(void)
16001553Srgrimes{
16011553Srgrimes
16021553Srgrimes	if (fconfig != NULL) {
16031553Srgrimes		fseek(fconfig, 0L, SEEK_SET);
16041553Srgrimes		return (1);
16051553Srgrimes	}
16061553Srgrimes	fconfig = fopen(CONFIG, "r");
16071553Srgrimes	return (fconfig != NULL);
16081553Srgrimes}
16091553Srgrimes
16101553Srgrimesvoid
161198558Sjmallettendconfig(void)
16121553Srgrimes{
16131553Srgrimes	if (fconfig) {
16141553Srgrimes		(void) fclose(fconfig);
16151553Srgrimes		fconfig = NULL;
16161553Srgrimes	}
16171553Srgrimes}
16181553Srgrimes
16191553Srgrimesstruct servtab *
162098558Sjmallettgetconfigent(void)
16211553Srgrimes{
16221553Srgrimes	struct servtab *sep = &serv;
16231553Srgrimes	int argc;
162419618Sjulian	char *cp, *arg, *s;
16252657Scsgr	char *versp;
16261553Srgrimes	static char TCPMUX_TOKEN[] = "tcpmux/";
16271553Srgrimes#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
162856590Sshin#ifdef IPSEC
1629102861Sdwmalone	char *policy;
163056590Sshin#endif
1631102861Sdwmalone	int v4bind;
163256590Sshin#ifdef INET6
1633102861Sdwmalone	int v6bind;
163456590Sshin#endif
1635101474Sume	int i;
16361553Srgrimes
1637102861Sdwmalone#ifdef IPSEC
1638102861Sdwmalone	policy = NULL;
1639102861Sdwmalone#endif
16401553Srgrimesmore:
1641102861Sdwmalone	v4bind = 0;
1642102861Sdwmalone#ifdef INET6
1643102861Sdwmalone	v6bind = 0;
1644102861Sdwmalone#endif
164556590Sshin	while ((cp = nextline(fconfig)) != NULL) {
164656590Sshin#ifdef IPSEC
164756590Sshin		/* lines starting with #@ is not a comment, but the policy */
164856590Sshin		if (cp[0] == '#' && cp[1] == '@') {
164956590Sshin			char *p;
165056590Sshin			for (p = cp + 2; p && *p && isspace(*p); p++)
165156590Sshin				;
165256590Sshin			if (*p == '\0') {
165356590Sshin				if (policy)
165456590Sshin					free(policy);
165556590Sshin				policy = NULL;
165656590Sshin			} else if (ipsec_get_policylen(p) >= 0) {
165756590Sshin				if (policy)
165856590Sshin					free(policy);
165956590Sshin				policy = newstr(p);
166056590Sshin			} else {
166156590Sshin				syslog(LOG_ERR,
166256590Sshin					"%s: invalid ipsec policy \"%s\"",
166356590Sshin					CONFIG, p);
166456590Sshin				exit(EX_CONFIG);
166556590Sshin			}
166656590Sshin		}
166756590Sshin#endif
166856590Sshin		if (*cp == '#' || *cp == '\0')
166956590Sshin			continue;
167056590Sshin		break;
167156590Sshin	}
16721553Srgrimes	if (cp == NULL)
16731553Srgrimes		return ((struct servtab *)0);
16741553Srgrimes	/*
16751553Srgrimes	 * clear the static buffer, since some fields (se_ctrladdr,
16761553Srgrimes	 * for example) don't get initialized here.
16771553Srgrimes	 */
167871399Sdwmalone	memset(sep, 0, sizeof *sep);
16791553Srgrimes	arg = skip(&cp);
16801553Srgrimes	if (cp == NULL) {
16811553Srgrimes		/* got an empty line containing just blanks/tabs. */
16821553Srgrimes		goto more;
16831553Srgrimes	}
168478356Sdwmalone	if (arg[0] == ':') { /* :user:group:perm: */
168578356Sdwmalone		char *user, *group, *perm;
168678356Sdwmalone		struct passwd *pw;
168778356Sdwmalone		struct group *gr;
168878356Sdwmalone		user = arg+1;
168978356Sdwmalone		if ((group = strchr(user, ':')) == NULL) {
169078356Sdwmalone			syslog(LOG_ERR, "no group after user '%s'", user);
169178356Sdwmalone			goto more;
169278356Sdwmalone		}
169378356Sdwmalone		*group++ = '\0';
169478356Sdwmalone		if ((perm = strchr(group, ':')) == NULL) {
169578356Sdwmalone			syslog(LOG_ERR, "no mode after group '%s'", group);
169678356Sdwmalone			goto more;
169778356Sdwmalone		}
169878356Sdwmalone		*perm++ = '\0';
169978356Sdwmalone		if ((pw = getpwnam(user)) == NULL) {
170078356Sdwmalone			syslog(LOG_ERR, "no such user '%s'", user);
170178356Sdwmalone			goto more;
170278356Sdwmalone		}
170378356Sdwmalone		sep->se_sockuid = pw->pw_uid;
170478356Sdwmalone		if ((gr = getgrnam(group)) == NULL) {
170578356Sdwmalone			syslog(LOG_ERR, "no such user '%s'", group);
170678356Sdwmalone			goto more;
170778356Sdwmalone		}
170878356Sdwmalone		sep->se_sockgid = gr->gr_gid;
170978356Sdwmalone		sep->se_sockmode = strtol(perm, &arg, 8);
171078356Sdwmalone		if (*arg != ':') {
171178356Sdwmalone			syslog(LOG_ERR, "bad mode '%s'", perm);
171278356Sdwmalone			goto more;
171378356Sdwmalone		}
171478356Sdwmalone		*arg++ = '\0';
171578356Sdwmalone	} else {
171678356Sdwmalone		sep->se_sockuid = euid;
171778356Sdwmalone		sep->se_sockgid = egid;
171878356Sdwmalone		sep->se_sockmode = 0200;
171978356Sdwmalone	}
17201553Srgrimes	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
17211553Srgrimes		char *c = arg + MUX_LEN;
17221553Srgrimes		if (*c == '+') {
17231553Srgrimes			sep->se_type = MUXPLUS_TYPE;
17241553Srgrimes			c++;
17251553Srgrimes		} else
17261553Srgrimes			sep->se_type = MUX_TYPE;
17271553Srgrimes		sep->se_service = newstr(c);
17281553Srgrimes	} else {
17291553Srgrimes		sep->se_service = newstr(arg);
17301553Srgrimes		sep->se_type = NORM_TYPE;
17311553Srgrimes	}
17321553Srgrimes	arg = sskip(&cp);
17331553Srgrimes	if (strcmp(arg, "stream") == 0)
17341553Srgrimes		sep->se_socktype = SOCK_STREAM;
17351553Srgrimes	else if (strcmp(arg, "dgram") == 0)
17361553Srgrimes		sep->se_socktype = SOCK_DGRAM;
17371553Srgrimes	else if (strcmp(arg, "rdm") == 0)
17381553Srgrimes		sep->se_socktype = SOCK_RDM;
17391553Srgrimes	else if (strcmp(arg, "seqpacket") == 0)
17401553Srgrimes		sep->se_socktype = SOCK_SEQPACKET;
17411553Srgrimes	else if (strcmp(arg, "raw") == 0)
17421553Srgrimes		sep->se_socktype = SOCK_RAW;
17431553Srgrimes	else
17441553Srgrimes		sep->se_socktype = -1;
174536042Sguido
174636042Sguido	arg = sskip(&cp);
174756590Sshin	if (strncmp(arg, "tcp", 3) == 0) {
174856590Sshin		sep->se_proto = newstr(strsep(&arg, "/"));
174956590Sshin		if (arg != NULL) {
175056590Sshin			if (strcmp(arg, "ttcp") == 0)
175156590Sshin				sep->se_type = TTCP_TYPE;
175256590Sshin			else if (strcmp(arg, "faith") == 0)
175356590Sshin				sep->se_type = FAITH_TYPE;
175456590Sshin		}
175577518Sume	} else {
175677518Sume		if (sep->se_type == NORM_TYPE &&
175777518Sume		    strncmp(arg, "faith/", 6) == 0) {
175877518Sume			arg += 6;
175977518Sume			sep->se_type = FAITH_TYPE;
176077518Sume		}
176136042Sguido		sep->se_proto = newstr(arg);
176277518Sume	}
17632657Scsgr        if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
176419237Sjoerg                memmove(sep->se_proto, sep->se_proto + 4,
176519237Sjoerg                    strlen(sep->se_proto) + 1 - 4);
17662657Scsgr                sep->se_rpc = 1;
17672657Scsgr                sep->se_rpc_prog = sep->se_rpc_lowvers =
17682657Scsgr			sep->se_rpc_lowvers = 0;
176956590Sshin		memcpy(&sep->se_ctrladdr4, bind_sa4,
177056590Sshin		       sizeof(sep->se_ctrladdr4));
17712657Scsgr                if ((versp = rindex(sep->se_service, '/'))) {
17722657Scsgr                        *versp++ = '\0';
1773102859Sdwmalone                        switch (sscanf(versp, "%u-%u",
17742657Scsgr                                       &sep->se_rpc_lowvers,
17752657Scsgr                                       &sep->se_rpc_highvers)) {
17762657Scsgr                        case 2:
17772657Scsgr                                break;
17782657Scsgr                        case 1:
17792657Scsgr                                sep->se_rpc_highvers =
17802657Scsgr                                        sep->se_rpc_lowvers;
17812657Scsgr                                break;
17822657Scsgr                        default:
17838857Srgrimes                                syslog(LOG_ERR,
178452219Scharnier					"bad RPC version specifier; %s",
17852657Scsgr					sep->se_service);
17862657Scsgr                                freeconfig(sep);
17872657Scsgr                                goto more;
17882657Scsgr                        }
17892657Scsgr                }
17902657Scsgr                else {
17912657Scsgr                        sep->se_rpc_lowvers =
17922657Scsgr                                sep->se_rpc_highvers = 1;
17932657Scsgr                }
17942657Scsgr        }
179556590Sshin	sep->se_nomapped = 0;
179678356Sdwmalone	if (strcmp(sep->se_proto, "unix") == 0) {
179778356Sdwmalone	        sep->se_family = AF_UNIX;
1798102937Sdwmalone	} else {
1799102937Sdwmalone		while (isdigit(sep->se_proto[strlen(sep->se_proto) - 1])) {
180056590Sshin#ifdef INET6
1801102937Sdwmalone			if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') {
1802102937Sdwmalone				sep->se_proto[strlen(sep->se_proto) - 1] = '\0';
1803102937Sdwmalone				v6bind = 1;
1804102937Sdwmalone				continue;
1805102937Sdwmalone			}
1806102937Sdwmalone#endif
1807102937Sdwmalone			if (sep->se_proto[strlen(sep->se_proto) - 1] == '4') {
1808102937Sdwmalone				sep->se_proto[strlen(sep->se_proto) - 1] = '\0';
1809102937Sdwmalone				v4bind = 1;
1810102937Sdwmalone				continue;
1811102937Sdwmalone			}
1812102937Sdwmalone			/* illegal version num */
1813102937Sdwmalone			syslog(LOG_ERR,	"bad IP version for %s", sep->se_proto);
1814100127Salfred			freeconfig(sep);
1815100127Salfred			goto more;
1816100127Salfred		}
1817102937Sdwmalone#ifdef INET6
1818102938Sdwmalone		if (v6bind && !v6bind_ok) {
1819102937Sdwmalone			syslog(LOG_INFO, "IPv6 bind is ignored for %s",
182056590Sshin			       sep->se_service);
1821102938Sdwmalone			if (v4bind && v4bind_ok)
1822102937Sdwmalone				v6bind = 0;
1823102937Sdwmalone			else {
1824102937Sdwmalone				freeconfig(sep);
1825102937Sdwmalone				goto more;
1826102937Sdwmalone			}
182756590Sshin		}
1828102938Sdwmalone		if (v6bind) {
1829102937Sdwmalone			sep->se_family = AF_INET6;
1830102938Sdwmalone			if (!v4bind || !v4bind_ok)
1831102937Sdwmalone				sep->se_nomapped = 1;
1832102937Sdwmalone		} else
1833102937Sdwmalone#endif
1834102937Sdwmalone		{ /* default to v4 bind if not v6 bind */
1835102938Sdwmalone			if (!v4bind_ok) {
1836102937Sdwmalone				syslog(LOG_NOTICE, "IPv4 bind is ignored for %s",
1837102937Sdwmalone				       sep->se_service);
1838102937Sdwmalone				freeconfig(sep);
1839102937Sdwmalone				goto more;
1840102937Sdwmalone			}
1841102937Sdwmalone			sep->se_family = AF_INET;
1842102937Sdwmalone		}
184356590Sshin	}
184456590Sshin	/* init ctladdr */
184556590Sshin	switch(sep->se_family) {
184656590Sshin	case AF_INET:
184756590Sshin		memcpy(&sep->se_ctrladdr4, bind_sa4,
184856590Sshin		       sizeof(sep->se_ctrladdr4));
184956590Sshin		sep->se_ctrladdr_size =	sizeof(sep->se_ctrladdr4);
185056590Sshin		break;
185156590Sshin#ifdef INET6
185256590Sshin	case AF_INET6:
185356590Sshin		memcpy(&sep->se_ctrladdr6, bind_sa6,
185456590Sshin		       sizeof(sep->se_ctrladdr6));
185556590Sshin		sep->se_ctrladdr_size =	sizeof(sep->se_ctrladdr6);
185656590Sshin		break;
185756590Sshin#endif
185878356Sdwmalone	case AF_UNIX:
185978356Sdwmalone		if (strlen(sep->se_service) >= sizeof(sep->se_ctrladdr_un.sun_path)) {
186078356Sdwmalone			syslog(LOG_ERR,
186178356Sdwmalone			    "domain socket pathname too long for service %s",
186278356Sdwmalone			    sep->se_service);
186378356Sdwmalone			goto more;
186478356Sdwmalone		}
186578356Sdwmalone		memset(&sep->se_ctrladdr, 0, sizeof(sep->se_ctrladdr));
186678356Sdwmalone		sep->se_ctrladdr_un.sun_family = sep->se_family;
186778356Sdwmalone		sep->se_ctrladdr_un.sun_len = strlen(sep->se_service);
186878356Sdwmalone		strcpy(sep->se_ctrladdr_un.sun_path, sep->se_service);
186978356Sdwmalone		sep->se_ctrladdr_size = SUN_LEN(&sep->se_ctrladdr_un);
187056590Sshin	}
18711553Srgrimes	arg = sskip(&cp);
187219618Sjulian	if (!strncmp(arg, "wait", 4))
187319618Sjulian		sep->se_accept = 0;
187419618Sjulian	else if (!strncmp(arg, "nowait", 6))
187519618Sjulian		sep->se_accept = 1;
187619618Sjulian	else {
187719618Sjulian		syslog(LOG_ERR,
187819618Sjulian			"%s: bad wait/nowait for service %s",
187919618Sjulian			CONFIG, sep->se_service);
188019618Sjulian		goto more;
188119618Sjulian	}
188248069Ssheldonh	sep->se_maxchild = -1;
188348069Ssheldonh	sep->se_maxcpm = -1;
1884101474Sume	sep->se_maxperip = -1;
188519618Sjulian	if ((s = strchr(arg, '/')) != NULL) {
188619618Sjulian		char *eptr;
188719618Sjulian		u_long val;
188819618Sjulian
188919618Sjulian		val = strtoul(s + 1, &eptr, 10);
189030847Sdima		if (eptr == s + 1 || val > MAX_MAXCHLD) {
189119618Sjulian			syslog(LOG_ERR,
189219618Sjulian				"%s: bad max-child for service %s",
189319618Sjulian				CONFIG, sep->se_service);
189419618Sjulian			goto more;
189519618Sjulian		}
189648069Ssheldonh		if (debug)
189748069Ssheldonh			if (!sep->se_accept && val != 1)
189848069Ssheldonh				warnx("maxchild=%lu for wait service %s"
189948069Ssheldonh				    " not recommended", val, sep->se_service);
190019618Sjulian		sep->se_maxchild = val;
190130847Sdima		if (*eptr == '/')
190230847Sdima			sep->se_maxcpm = strtol(eptr + 1, &eptr, 10);
1903101474Sume		if (*eptr == '/')
1904101474Sume			sep->se_maxperip = strtol(eptr + 1, &eptr, 10);
190530847Sdima		/*
190630847Sdima		 * explicitly do not check for \0 for future expansion /
190730847Sdima		 * backwards compatibility
190830847Sdima		 */
190919618Sjulian	}
19101553Srgrimes	if (ISMUX(sep)) {
19111553Srgrimes		/*
191219618Sjulian		 * Silently enforce "nowait" mode for TCPMUX services
191319618Sjulian		 * since they don't have an assigned port to listen on.
19141553Srgrimes		 */
191519618Sjulian		sep->se_accept = 1;
19161553Srgrimes		if (strcmp(sep->se_proto, "tcp")) {
19178857Srgrimes			syslog(LOG_ERR,
19181553Srgrimes				"%s: bad protocol for tcpmux service %s",
19191553Srgrimes				CONFIG, sep->se_service);
19201553Srgrimes			goto more;
19211553Srgrimes		}
19221553Srgrimes		if (sep->se_socktype != SOCK_STREAM) {
19238857Srgrimes			syslog(LOG_ERR,
19241553Srgrimes				"%s: bad socket type for tcpmux service %s",
19251553Srgrimes				CONFIG, sep->se_service);
19261553Srgrimes			goto more;
19271553Srgrimes		}
19281553Srgrimes	}
19291553Srgrimes	sep->se_user = newstr(sskip(&cp));
193030792Sache#ifdef LOGIN_CAP
193130792Sache	if ((s = strrchr(sep->se_user, '/')) != NULL) {
193230792Sache		*s = '\0';
193330792Sache		sep->se_class = newstr(s + 1);
193430792Sache	} else
193530792Sache		sep->se_class = newstr(RESOURCE_RC);
193630792Sache#endif
193730807Sache	if ((s = strrchr(sep->se_user, ':')) != NULL) {
193830807Sache		*s = '\0';
193930807Sache		sep->se_group = newstr(s + 1);
194030807Sache	} else
194130807Sache		sep->se_group = NULL;
19421553Srgrimes	sep->se_server = newstr(sskip(&cp));
194345588Smarkm	if ((sep->se_server_name = rindex(sep->se_server, '/')))
194445588Smarkm		sep->se_server_name++;
19451553Srgrimes	if (strcmp(sep->se_server, "internal") == 0) {
19461553Srgrimes		struct biltin *bi;
19471553Srgrimes
19481553Srgrimes		for (bi = biltins; bi->bi_service; bi++)
194949026Sdes			if (bi->bi_socktype == sep->se_socktype &&
195048467Ssheldonh			    matchservent(bi->bi_service, sep->se_service,
195148467Ssheldonh			    sep->se_proto))
19521553Srgrimes				break;
19531553Srgrimes		if (bi->bi_service == 0) {
19541553Srgrimes			syslog(LOG_ERR, "internal service %s unknown",
19551553Srgrimes				sep->se_service);
19561553Srgrimes			goto more;
19571553Srgrimes		}
195819618Sjulian		sep->se_accept = 1;	/* force accept mode for built-ins */
19591553Srgrimes		sep->se_bi = bi;
19601553Srgrimes	} else
19611553Srgrimes		sep->se_bi = NULL;
1962101474Sume	if (sep->se_maxperip < 0)
1963101474Sume		sep->se_maxperip = maxperip;
196448069Ssheldonh	if (sep->se_maxcpm < 0)
196548069Ssheldonh		sep->se_maxcpm = maxcpm;
196645588Smarkm	if (sep->se_maxchild < 0) {	/* apply default max-children */
196748069Ssheldonh		if (sep->se_bi && sep->se_bi->bi_maxchild >= 0)
196819618Sjulian			sep->se_maxchild = sep->se_bi->bi_maxchild;
196948069Ssheldonh		else if (sep->se_accept)
197048069Ssheldonh			sep->se_maxchild = maxchild > 0 ? maxchild : 0;
197119618Sjulian		else
197248069Ssheldonh			sep->se_maxchild = 1;
197345588Smarkm	}
197464197Sdwmalone	if (sep->se_maxchild > 0) {
197519618Sjulian		sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
197619618Sjulian		if (sep->se_pids == NULL) {
197749102Ssheldonh			syslog(LOG_ERR, "malloc: %m");
197819618Sjulian			exit(EX_OSERR);
197919618Sjulian		}
198019618Sjulian	}
19811553Srgrimes	argc = 0;
19821553Srgrimes	for (arg = skip(&cp); cp; arg = skip(&cp))
198319618Sjulian		if (argc < MAXARGV) {
19841553Srgrimes			sep->se_argv[argc++] = newstr(arg);
198519618Sjulian		} else {
198619618Sjulian			syslog(LOG_ERR,
198719618Sjulian				"%s: too many arguments for service %s",
198819618Sjulian				CONFIG, sep->se_service);
198919618Sjulian			goto more;
199019618Sjulian		}
19911553Srgrimes	while (argc <= MAXARGV)
19921553Srgrimes		sep->se_argv[argc++] = NULL;
1993101474Sume	for (i = 0; i < PERIPSIZE; ++i)
1994101474Sume		LIST_INIT(&sep->se_conn[i]);
199556590Sshin#ifdef IPSEC
199656590Sshin	sep->se_policy = policy ? newstr(policy) : NULL;
199756590Sshin#endif
19981553Srgrimes	return (sep);
19991553Srgrimes}
20001553Srgrimes
20011553Srgrimesvoid
200298558Sjmallettfreeconfig(struct servtab *cp)
20031553Srgrimes{
20041553Srgrimes	int i;
20051553Srgrimes
20061553Srgrimes	if (cp->se_service)
20071553Srgrimes		free(cp->se_service);
20081553Srgrimes	if (cp->se_proto)
20091553Srgrimes		free(cp->se_proto);
20101553Srgrimes	if (cp->se_user)
20111553Srgrimes		free(cp->se_user);
201230807Sache	if (cp->se_group)
201330807Sache		free(cp->se_group);
201430792Sache#ifdef LOGIN_CAP
201530792Sache	if (cp->se_class)
201630792Sache		free(cp->se_class);
201730792Sache#endif
20181553Srgrimes	if (cp->se_server)
20191553Srgrimes		free(cp->se_server);
202019618Sjulian	if (cp->se_pids)
202119618Sjulian		free(cp->se_pids);
20221553Srgrimes	for (i = 0; i < MAXARGV; i++)
20231553Srgrimes		if (cp->se_argv[i])
20241553Srgrimes			free(cp->se_argv[i]);
2025101474Sume	free_connlist(cp);
202656590Sshin#ifdef IPSEC
202756590Sshin	if (cp->se_policy)
202856590Sshin		free(cp->se_policy);
202956590Sshin#endif
20301553Srgrimes}
20311553Srgrimes
20321553Srgrimes
20331553Srgrimes/*
20341553Srgrimes * Safe skip - if skip returns null, log a syntax error in the
20351553Srgrimes * configuration file and exit.
20361553Srgrimes */
20371553Srgrimeschar *
203898558Sjmallettsskip(char **cpp)
20391553Srgrimes{
20401553Srgrimes	char *cp;
20411553Srgrimes
20421553Srgrimes	cp = skip(cpp);
20431553Srgrimes	if (cp == NULL) {
20441553Srgrimes		syslog(LOG_ERR, "%s: syntax error", CONFIG);
204519617Sjulian		exit(EX_DATAERR);
20461553Srgrimes	}
20471553Srgrimes	return (cp);
20481553Srgrimes}
20491553Srgrimes
20501553Srgrimeschar *
205198558Sjmallettskip(char **cpp)
20521553Srgrimes{
20531553Srgrimes	char *cp = *cpp;
20541553Srgrimes	char *start;
205511933Sadam	char quote = '\0';
20561553Srgrimes
20571553Srgrimesagain:
20581553Srgrimes	while (*cp == ' ' || *cp == '\t')
20591553Srgrimes		cp++;
20601553Srgrimes	if (*cp == '\0') {
20611553Srgrimes		int c;
20621553Srgrimes
20631553Srgrimes		c = getc(fconfig);
20641553Srgrimes		(void) ungetc(c, fconfig);
20651553Srgrimes		if (c == ' ' || c == '\t')
206619617Sjulian			if ((cp = nextline(fconfig)))
20671553Srgrimes				goto again;
20681553Srgrimes		*cpp = (char *)0;
20691553Srgrimes		return ((char *)0);
20701553Srgrimes	}
207111933Sadam	if (*cp == '"' || *cp == '\'')
207211933Sadam		quote = *cp++;
20731553Srgrimes	start = cp;
207411933Sadam	if (quote)
207511933Sadam		while (*cp && *cp != quote)
207611933Sadam			cp++;
207711933Sadam	else
207811933Sadam		while (*cp && *cp != ' ' && *cp != '\t')
207911933Sadam			cp++;
20801553Srgrimes	if (*cp != '\0')
20811553Srgrimes		*cp++ = '\0';
20821553Srgrimes	*cpp = cp;
20831553Srgrimes	return (start);
20841553Srgrimes}
20851553Srgrimes
20861553Srgrimeschar *
208798558Sjmallettnextline(FILE *fd)
20881553Srgrimes{
20891553Srgrimes	char *cp;
20901553Srgrimes
20911553Srgrimes	if (fgets(line, sizeof (line), fd) == NULL)
20921553Srgrimes		return ((char *)0);
20931553Srgrimes	cp = strchr(line, '\n');
20941553Srgrimes	if (cp)
20951553Srgrimes		*cp = '\0';
20961553Srgrimes	return (line);
20971553Srgrimes}
20981553Srgrimes
20991553Srgrimeschar *
210098558Sjmallettnewstr(const char *cp)
21011553Srgrimes{
210278694Sdwmalone	char *cr;
210378694Sdwmalone
210478694Sdwmalone	if ((cr = strdup(cp != NULL ? cp : "")))
210578694Sdwmalone		return (cr);
21061553Srgrimes	syslog(LOG_ERR, "strdup: %m");
210719617Sjulian	exit(EX_OSERR);
21081553Srgrimes}
21091553Srgrimes
21101553Srgrimesvoid
211198558Sjmallettinetd_setproctitle(const char *a, int s)
21121553Srgrimes{
211371399Sdwmalone	socklen_t size;
211456590Sshin	struct sockaddr_storage ss;
211556590Sshin	char buf[80], pbuf[INET6_ADDRSTRLEN];
21161553Srgrimes
211756590Sshin	size = sizeof(ss);
211856590Sshin	if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) {
211956590Sshin		getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf),
212056590Sshin			    NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID);
212156590Sshin		(void) sprintf(buf, "%s [%s]", a, pbuf);
212256590Sshin	} else
212313142Speter		(void) sprintf(buf, "%s", a);
212413142Speter	setproctitle("%s", buf);
212513142Speter}
212613142Speter
212778694Sdwmaloneint
212898558Sjmallettcheck_loop(const struct sockaddr *sa, const struct servtab *sep)
21295182Swollman{
21305182Swollman	struct servtab *se2;
213156590Sshin	char pname[INET6_ADDRSTRLEN];
21325182Swollman
21335182Swollman	for (se2 = servtab; se2; se2 = se2->se_next) {
21345182Swollman		if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
21355182Swollman			continue;
21365182Swollman
213756590Sshin		switch (se2->se_family) {
213856590Sshin		case AF_INET:
213978694Sdwmalone			if (((const struct sockaddr_in *)sa)->sin_port ==
214056590Sshin			    se2->se_ctrladdr4.sin_port)
214156590Sshin				goto isloop;
214256590Sshin			continue;
214356590Sshin#ifdef INET6
214456590Sshin		case AF_INET6:
214578694Sdwmalone			if (((const struct sockaddr_in *)sa)->sin_port ==
214656590Sshin			    se2->se_ctrladdr4.sin_port)
214756590Sshin				goto isloop;
214856590Sshin			continue;
214956590Sshin#endif
215056590Sshin		default:
215156590Sshin			continue;
21525182Swollman		}
215356590Sshin	isloop:
215456590Sshin		getnameinfo(sa, sa->sa_len, pname, sizeof(pname), NULL, 0,
215556590Sshin			    NI_NUMERICHOST|NI_WITHSCOPEID);
215656590Sshin		syslog(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s",
215756590Sshin		       sep->se_service, sep->se_proto,
215856590Sshin		       se2->se_service, se2->se_proto,
215956590Sshin		       pname);
216056590Sshin		return 1;
21615182Swollman	}
21625182Swollman	return 0;
21635182Swollman}
21645182Swollman
21651553Srgrimes/*
21661553Srgrimes * print_service:
21671553Srgrimes *	Dump relevant information to stderr
21681553Srgrimes */
21691553Srgrimesvoid
217098558Sjmallettprint_service(const char *action, const struct servtab *sep)
21711553Srgrimes{
217219617Sjulian	fprintf(stderr,
217356590Sshin	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s"
217430792Sache#ifdef LOGIN_CAP
217556590Sshin	    "class=%s"
217630792Sache#endif
217756590Sshin	    " builtin=%p server=%s"
217856590Sshin#ifdef IPSEC
217956590Sshin	    " policy=\"%s\""
218056590Sshin#endif
218156590Sshin	    "\n",
218219617Sjulian	    action, sep->se_service, sep->se_proto,
218330807Sache	    sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group,
218430792Sache#ifdef LOGIN_CAP
218530792Sache	    sep->se_class,
218630792Sache#endif
218756590Sshin	    (void *) sep->se_bi, sep->se_server
218856590Sshin#ifdef IPSEC
218956590Sshin	    , (sep->se_policy ? sep->se_policy : "")
219056590Sshin#endif
219156590Sshin	    );
21921553Srgrimes}
21931553Srgrimes
219430847Sdima#define CPMHSIZE	256
219530847Sdima#define CPMHMASK	(CPMHSIZE-1)
219630847Sdima#define CHTGRAN		10
219730847Sdima#define CHTSIZE		6
219830847Sdima
219930847Sdimatypedef struct CTime {
220030847Sdima	unsigned long 	ct_Ticks;
220130847Sdima	int		ct_Count;
220230847Sdima} CTime;
220330847Sdima
220430847Sdimatypedef struct CHash {
220556590Sshin	union {
220656590Sshin		struct in_addr	c4_Addr;
220756590Sshin		struct in6_addr	c6_Addr;
220856590Sshin	} cu_Addr;
220956590Sshin#define	ch_Addr4	cu_Addr.c4_Addr
221056590Sshin#define	ch_Addr6	cu_Addr.c6_Addr
221156590Sshin	int		ch_Family;
221230847Sdima	time_t		ch_LTime;
221330847Sdima	char		*ch_Service;
221430847Sdima	CTime		ch_Times[CHTSIZE];
221530847Sdima} CHash;
221630847Sdima
221730847SdimaCHash	CHashAry[CPMHSIZE];
221830847Sdima
221930847Sdimaint
222098558Sjmallettcpmip(const struct servtab *sep, int ctrl)
222130847Sdima{
222256590Sshin	struct sockaddr_storage rss;
222371399Sdwmalone	socklen_t rssLen = sizeof(rss);
222430847Sdima	int r = 0;
222530847Sdima
222630847Sdima	/*
222730847Sdima	 * If getpeername() fails, just let it through (if logging is
222830847Sdima	 * enabled the condition is caught elsewhere)
222930847Sdima	 */
223030847Sdima
223130847Sdima	if (sep->se_maxcpm > 0 &&
223256590Sshin	    getpeername(ctrl, (struct sockaddr *)&rss, &rssLen) == 0 ) {
223330847Sdima		time_t t = time(NULL);
223430847Sdima		int hv = 0xABC3D20F;
223530847Sdima		int i;
223630847Sdima		int cnt = 0;
223730847Sdima		CHash *chBest = NULL;
223830847Sdima		unsigned int ticks = t / CHTGRAN;
223978694Sdwmalone		struct sockaddr_in *sin4;
224056590Sshin#ifdef INET6
224156590Sshin		struct sockaddr_in6 *sin6;
224256590Sshin#endif
224330847Sdima
224478694Sdwmalone		sin4 = (struct sockaddr_in *)&rss;
224556590Sshin#ifdef INET6
224656590Sshin		sin6 = (struct sockaddr_in6 *)&rss;
224756590Sshin#endif
224830847Sdima		{
224930847Sdima			char *p;
225078694Sdwmalone			int addrlen;
225130847Sdima
225256590Sshin			switch (rss.ss_family) {
225356590Sshin			case AF_INET:
225478694Sdwmalone				p = (char *)&sin4->sin_addr;
225556590Sshin				addrlen = sizeof(struct in_addr);
225656590Sshin				break;
225756590Sshin#ifdef INET6
225856590Sshin			case AF_INET6:
225956590Sshin				p = (char *)&sin6->sin6_addr;
226056590Sshin				addrlen = sizeof(struct in6_addr);
226156590Sshin				break;
226256590Sshin#endif
226356590Sshin			default:
226456590Sshin				/* should not happen */
226556590Sshin				return -1;
226656590Sshin			}
226756590Sshin
226856590Sshin			for (i = 0; i < addrlen; ++i, ++p) {
226930847Sdima				hv = (hv << 5) ^ (hv >> 23) ^ *p;
227030847Sdima			}
227130847Sdima			hv = (hv ^ (hv >> 16));
227230847Sdima		}
227330847Sdima		for (i = 0; i < 5; ++i) {
227430847Sdima			CHash *ch = &CHashAry[(hv + i) & CPMHMASK];
227530847Sdima
227656590Sshin			if (rss.ss_family == AF_INET &&
227756590Sshin			    ch->ch_Family == AF_INET &&
227878694Sdwmalone			    sin4->sin_addr.s_addr == ch->ch_Addr4.s_addr &&
227930847Sdima			    ch->ch_Service && strcmp(sep->se_service,
228030847Sdima			    ch->ch_Service) == 0) {
228130847Sdima				chBest = ch;
228230847Sdima				break;
228330847Sdima			}
228456590Sshin#ifdef INET6
228556590Sshin			if (rss.ss_family == AF_INET6 &&
228656590Sshin			    ch->ch_Family == AF_INET6 &&
228756590Sshin			    IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
228856590Sshin					       &ch->ch_Addr6) != 0 &&
228956590Sshin			    ch->ch_Service && strcmp(sep->se_service,
229056590Sshin			    ch->ch_Service) == 0) {
229156590Sshin				chBest = ch;
229256590Sshin				break;
229356590Sshin			}
229456590Sshin#endif
229530847Sdima			if (chBest == NULL || ch->ch_LTime == 0 ||
229630847Sdima			    ch->ch_LTime < chBest->ch_LTime) {
229730847Sdima				chBest = ch;
229830847Sdima			}
229930847Sdima		}
230056590Sshin		if ((rss.ss_family == AF_INET &&
230156590Sshin		     (chBest->ch_Family != AF_INET ||
230278694Sdwmalone		      sin4->sin_addr.s_addr != chBest->ch_Addr4.s_addr)) ||
230330847Sdima		    chBest->ch_Service == NULL ||
230430847Sdima		    strcmp(sep->se_service, chBest->ch_Service) != 0) {
230578694Sdwmalone			chBest->ch_Family = sin4->sin_family;
230678694Sdwmalone			chBest->ch_Addr4 = sin4->sin_addr;
230730847Sdima			if (chBest->ch_Service)
230830847Sdima				free(chBest->ch_Service);
230930847Sdima			chBest->ch_Service = strdup(sep->se_service);
231030847Sdima			bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
231130847Sdima		}
231256590Sshin#ifdef INET6
231356590Sshin		if ((rss.ss_family == AF_INET6 &&
231456590Sshin		     (chBest->ch_Family != AF_INET6 ||
231556590Sshin		      IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
231656590Sshin					 &chBest->ch_Addr6) == 0)) ||
231756590Sshin		    chBest->ch_Service == NULL ||
231856590Sshin		    strcmp(sep->se_service, chBest->ch_Service) != 0) {
231956590Sshin			chBest->ch_Family = sin6->sin6_family;
232056590Sshin			chBest->ch_Addr6 = sin6->sin6_addr;
232156590Sshin			if (chBest->ch_Service)
232256590Sshin				free(chBest->ch_Service);
232356590Sshin			chBest->ch_Service = strdup(sep->se_service);
232456590Sshin			bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
232556590Sshin		}
232656590Sshin#endif
232730847Sdima		chBest->ch_LTime = t;
232830847Sdima		{
232930847Sdima			CTime *ct = &chBest->ch_Times[ticks % CHTSIZE];
233030847Sdima			if (ct->ct_Ticks != ticks) {
233130847Sdima				ct->ct_Ticks = ticks;
233230847Sdima				ct->ct_Count = 0;
233330847Sdima			}
233430847Sdima			++ct->ct_Count;
233530847Sdima		}
233630847Sdima		for (i = 0; i < CHTSIZE; ++i) {
233730847Sdima			CTime *ct = &chBest->ch_Times[i];
233830847Sdima			if (ct->ct_Ticks <= ticks &&
233930847Sdima			    ct->ct_Ticks >= ticks - CHTSIZE) {
234030847Sdima				cnt += ct->ct_Count;
234130847Sdima			}
234230847Sdima		}
2343117644Sdwmalone		if ((cnt * 60) / (CHTSIZE * CHTGRAN) > sep->se_maxcpm) {
234456590Sshin			char pname[INET6_ADDRSTRLEN];
234556590Sshin
234656590Sshin			getnameinfo((struct sockaddr *)&rss,
234756590Sshin				    ((struct sockaddr *)&rss)->sa_len,
234856590Sshin				    pname, sizeof(pname), NULL, 0,
234956590Sshin				    NI_NUMERICHOST|NI_WITHSCOPEID);
235030847Sdima			r = -1;
235130847Sdima			syslog(LOG_ERR,
235233794Spst			    "%s from %s exceeded counts/min (limit %d/min)",
235356590Sshin			    sep->se_service, pname,
235433794Spst			    sep->se_maxcpm);
235530847Sdima		}
235630847Sdima	}
235730847Sdima	return(r);
235830847Sdima}
2359101474Sume
2360101474Sumestatic struct conninfo *
2361101474Sumesearch_conn(struct servtab *sep, int ctrl)
2362101474Sume{
2363101474Sume	struct sockaddr_storage ss;
2364101474Sume	socklen_t sslen = sizeof(ss);
2365101474Sume	struct conninfo *conn;
2366101474Sume	int hv;
2367101474Sume	char pname[NI_MAXHOST],  pname2[NI_MAXHOST];
2368101474Sume
2369101474Sume	if (sep->se_maxperip <= 0)
2370101474Sume		return NULL;
2371101474Sume
2372101474Sume	/*
2373101474Sume	 * If getpeername() fails, just let it through (if logging is
2374101474Sume	 * enabled the condition is caught elsewhere)
2375101474Sume	 */
2376101474Sume	if (getpeername(ctrl, (struct sockaddr *)&ss, &sslen) != 0)
2377101474Sume		return NULL;
2378101474Sume
2379101474Sume	switch (ss.ss_family) {
2380101474Sume	case AF_INET:
2381101474Sume		hv = hashval((char *)&((struct sockaddr_in *)&ss)->sin_addr,
2382101474Sume		    sizeof(struct in_addr));
2383101474Sume		break;
2384101474Sume#ifdef INET6
2385101474Sume	case AF_INET6:
2386101474Sume		hv = hashval((char *)&((struct sockaddr_in6 *)&ss)->sin6_addr,
2387101474Sume		    sizeof(struct in6_addr));
2388101474Sume		break;
2389101474Sume#endif
2390101474Sume	default:
2391101474Sume		/*
2392101474Sume		 * Since we only support AF_INET and AF_INET6, just
2393101474Sume		 * let other than AF_INET and AF_INET6 through.
2394101474Sume		 */
2395101474Sume		return NULL;
2396101474Sume	}
2397101474Sume
2398101474Sume	if (getnameinfo((struct sockaddr *)&ss, sslen, pname, sizeof(pname),
2399101474Sume	    NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0)
2400101474Sume		return NULL;
2401101474Sume
2402101474Sume	LIST_FOREACH(conn, &sep->se_conn[hv], co_link) {
2403101474Sume		if (getnameinfo((struct sockaddr *)&conn->co_addr,
2404101474Sume		    conn->co_addr.ss_len, pname2, sizeof(pname2), NULL, 0,
2405101474Sume		    NI_NUMERICHOST | NI_WITHSCOPEID) == 0 &&
2406101474Sume		    strcmp(pname, pname2) == 0)
2407101474Sume			break;
2408101474Sume	}
2409101474Sume
2410101474Sume	if (conn == NULL) {
2411101474Sume		if ((conn = malloc(sizeof(struct conninfo))) == NULL) {
2412101474Sume			syslog(LOG_ERR, "malloc: %m");
2413101474Sume			exit(EX_OSERR);
2414101474Sume		}
2415101474Sume		conn->co_proc = malloc(sep->se_maxperip * sizeof(*conn->co_proc));
2416101474Sume		if (conn->co_proc == NULL) {
2417101474Sume			syslog(LOG_ERR, "malloc: %m");
2418101474Sume			exit(EX_OSERR);
2419101474Sume		}
2420101474Sume		memcpy(&conn->co_addr, (struct sockaddr *)&ss, sslen);
2421101474Sume		conn->co_numchild = 0;
2422101474Sume		LIST_INSERT_HEAD(&sep->se_conn[hv], conn, co_link);
2423101474Sume	}
2424101474Sume
2425101474Sume	/*
2426101474Sume	 * Since a child process is not invoked yet, we cannot
2427101474Sume	 * determine a pid of a child.  So, co_proc and co_numchild
2428101474Sume	 * should be filled leter.
2429101474Sume	 */
2430101474Sume
2431101474Sume	return conn;
2432101474Sume}
2433101474Sume
2434101474Sumestatic int
2435101474Sumeroom_conn(struct servtab *sep, struct conninfo *conn)
2436101474Sume{
2437101474Sume	char pname[NI_MAXHOST];
2438101474Sume
2439101474Sume	if (conn->co_numchild >= sep->se_maxperip) {
2440101474Sume		getnameinfo((struct sockaddr *)&conn->co_addr,
2441101474Sume		    conn->co_addr.ss_len, pname, sizeof(pname), NULL, 0,
2442101474Sume		    NI_NUMERICHOST | NI_WITHSCOPEID);
2443101474Sume		syslog(LOG_ERR, "%s from %s exceeded counts (limit %d)",
2444101474Sume		    sep->se_service, pname, sep->se_maxperip);
2445101474Sume		return 0;
2446101474Sume	}
2447101474Sume	return 1;
2448101474Sume}
2449101474Sume
2450101474Sumestatic void
2451101474Sumeaddchild_conn(struct conninfo *conn, pid_t pid)
2452101474Sume{
2453101474Sume	struct procinfo *proc;
2454101474Sume
2455101474Sume	if (conn == NULL)
2456101474Sume		return;
2457101474Sume
2458101474Sume	if ((proc = search_proc(pid, 1)) != NULL) {
2459101474Sume		if (proc->pr_conn != NULL) {
2460101474Sume			syslog(LOG_ERR,
2461101474Sume			    "addchild_conn: child already on process list");
2462101474Sume			exit(EX_OSERR);
2463101474Sume		}
2464101474Sume		proc->pr_conn = conn;
2465101474Sume	}
2466101474Sume
2467101474Sume	conn->co_proc[conn->co_numchild++] = proc;
2468101474Sume}
2469101474Sume
2470101474Sumestatic void
2471101474Sumereapchild_conn(pid_t pid)
2472101474Sume{
2473101474Sume	struct procinfo *proc;
2474101474Sume	struct conninfo *conn;
2475101474Sume	int i;
2476101474Sume
2477101474Sume	if ((proc = search_proc(pid, 0)) == NULL)
2478101474Sume		return;
2479101474Sume	if ((conn = proc->pr_conn) == NULL)
2480101474Sume		return;
2481101474Sume	for (i = 0; i < conn->co_numchild; ++i)
2482101474Sume		if (conn->co_proc[i] == proc) {
2483101474Sume			conn->co_proc[i] = conn->co_proc[--conn->co_numchild];
2484101474Sume			break;
2485101474Sume		}
2486101474Sume	free_proc(proc);
2487101474Sume	free_conn(conn);
2488101474Sume}
2489101474Sume
2490101474Sumestatic void
2491102859Sdwmaloneresize_conn(struct servtab *sep, int maxpip)
2492101474Sume{
2493101474Sume	struct conninfo *conn;
2494101474Sume	int i, j;
2495101474Sume
2496101474Sume	if (sep->se_maxperip <= 0)
2497101474Sume		return;
2498102859Sdwmalone	if (maxpip <= 0) {
2499101474Sume		free_connlist(sep);
2500101474Sume		return;
2501101474Sume	}
2502101474Sume	for (i = 0; i < PERIPSIZE; ++i) {
2503101474Sume		LIST_FOREACH(conn, &sep->se_conn[i], co_link) {
2504102859Sdwmalone			for (j = maxpip; j < conn->co_numchild; ++j)
2505101474Sume				free_proc(conn->co_proc[j]);
2506101474Sume			conn->co_proc = realloc(conn->co_proc,
2507102859Sdwmalone			    maxpip * sizeof(*conn->co_proc));
2508101474Sume			if (conn->co_proc == NULL) {
2509101474Sume				syslog(LOG_ERR, "realloc: %m");
2510101474Sume				exit(EX_OSERR);
2511101474Sume			}
2512102859Sdwmalone			if (conn->co_numchild > maxpip)
2513102859Sdwmalone				conn->co_numchild = maxpip;
2514101474Sume		}
2515101474Sume	}
2516101474Sume}
2517101474Sume
2518101474Sumestatic void
2519101474Sumefree_connlist(struct servtab *sep)
2520101474Sume{
2521101474Sume	struct conninfo *conn;
2522101474Sume	int i, j;
2523101474Sume
2524101474Sume	for (i = 0; i < PERIPSIZE; ++i) {
2525101474Sume		while ((conn = LIST_FIRST(&sep->se_conn[i])) != NULL) {
2526101474Sume			for (j = 0; j < conn->co_numchild; ++j)
2527101474Sume				free_proc(conn->co_proc[j]);
2528101474Sume			conn->co_numchild = 0;
2529101474Sume			free_conn(conn);
2530101474Sume		}
2531101474Sume	}
2532101474Sume}
2533101474Sume
2534101474Sumestatic void
2535101474Sumefree_conn(struct conninfo *conn)
2536101474Sume{
2537101474Sume	if (conn == NULL)
2538101474Sume		return;
2539101474Sume	if (conn->co_numchild <= 0) {
2540101474Sume		LIST_REMOVE(conn, co_link);
2541101474Sume		free(conn->co_proc);
2542101474Sume		free(conn);
2543101474Sume	}
2544101474Sume}
2545101474Sume
2546101474Sumestatic struct procinfo *
2547101474Sumesearch_proc(pid_t pid, int add)
2548101474Sume{
2549101474Sume	struct procinfo *proc;
2550101474Sume	int hv;
2551101474Sume
2552101474Sume	hv = hashval((char *)&pid, sizeof(pid));
2553101474Sume	LIST_FOREACH(proc, &proctable[hv], pr_link) {
2554101474Sume		if (proc->pr_pid == pid)
2555101474Sume			break;
2556101474Sume	}
2557101474Sume	if (proc == NULL && add) {
2558101474Sume		if ((proc = malloc(sizeof(struct procinfo))) == NULL) {
2559101474Sume			syslog(LOG_ERR, "malloc: %m");
2560101474Sume			exit(EX_OSERR);
2561101474Sume		}
2562101474Sume		proc->pr_pid = pid;
2563101474Sume		proc->pr_conn = NULL;
2564101474Sume		LIST_INSERT_HEAD(&proctable[hv], proc, pr_link);
2565101474Sume	}
2566101474Sume	return proc;
2567101474Sume}
2568101474Sume
2569101474Sumestatic void
2570101474Sumefree_proc(struct procinfo *proc)
2571101474Sume{
2572101474Sume	if (proc == NULL)
2573101474Sume		return;
2574101474Sume	LIST_REMOVE(proc, pr_link);
2575101474Sume	free(proc);
2576101474Sume}
2577101474Sume
2578101474Sumestatic int
2579101474Sumehashval(char *p, int len)
2580101474Sume{
2581101474Sume	int i, hv = 0xABC3D20F;
2582101474Sume
2583101474Sume	for (i = 0; i < len; ++i, ++p)
2584101474Sume		hv = (hv << 5) ^ (hv >> 23) ^ *p;
2585101474Sume	hv = (hv ^ (hv >> 16)) & (PERIPSIZE - 1);
2586101474Sume	return hv;
2587101474Sume}
2588