inetd.c revision 121559
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 121559 2003-10-26 06:11:31Z ume $");
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;
2552659Scsgrint	log = 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':
3622659Scsgr			log = 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];
4691553Srgrimes	if (debug == 0) {
47011447Swollman		FILE *fp;
47112024Speter		if (daemon(0, 0) < 0) {
47212024Speter			syslog(LOG_WARNING, "daemon(0,0) failed: %m");
47312024Speter		}
47497293Sjwd		/* From now on we don't want syslog messages going to stderr. */
47597293Sjwd		closelog();
47697293Sjwd		openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
47712024Speter		/*
47812024Speter		 * In case somebody has started inetd manually, we need to
47912024Speter		 * clear the logname, so that old servers run as root do not
48012024Speter		 * get the user's logname..
48112024Speter		 */
48212024Speter		if (setlogin("") < 0) {
48312024Speter			syslog(LOG_WARNING, "cannot clear logname: %m");
48412024Speter			/* no big deal if it fails.. */
48512024Speter		}
48611447Swollman		pid = getpid();
48717482Sjulian		fp = fopen(pid_file, "w");
48811447Swollman		if (fp) {
48911447Swollman			fprintf(fp, "%ld\n", (long)pid);
49011447Swollman			fclose(fp);
49111447Swollman		} else {
49217482Sjulian			syslog(LOG_WARNING, "%s: %m", pid_file);
49311447Swollman		}
4941553Srgrimes	}
495100127Salfred
496101474Sume	for (i = 0; i < PERIPSIZE; ++i)
497101474Sume		LIST_INIT(&proctable[i]);
498101474Sume
499102938Sdwmalone	if (v4bind_ok) {
500100127Salfred		udpconf = getnetconfigent("udp");
501100127Salfred		tcpconf = getnetconfigent("tcp");
502100127Salfred		if (udpconf == NULL || tcpconf == NULL) {
503102860Sdwmalone			syslog(LOG_ERR, "unknown rpc/udp or rpc/tcp");
504100127Salfred			exit(EX_USAGE);
505100127Salfred		}
506100127Salfred	}
507100127Salfred#ifdef INET6
508102938Sdwmalone	if (v6bind_ok) {
509100127Salfred		udp6conf = getnetconfigent("udp6");
510100127Salfred		tcp6conf = getnetconfigent("tcp6");
511100127Salfred		if (udp6conf == NULL || tcp6conf == NULL) {
512102860Sdwmalone			syslog(LOG_ERR, "unknown rpc/udp6 or rpc/tcp6");
513100127Salfred			exit(EX_USAGE);
514100127Salfred		}
515100127Salfred	}
516100127Salfred#endif
517100127Salfred
51835948Sbde	sa.sa_flags = 0;
51935948Sbde	sigemptyset(&sa.sa_mask);
52035948Sbde	sigaddset(&sa.sa_mask, SIGALRM);
52135948Sbde	sigaddset(&sa.sa_mask, SIGCHLD);
52235948Sbde	sigaddset(&sa.sa_mask, SIGHUP);
52342122Sdes	sa.sa_handler = flag_retry;
52448962Ssheldonh	sigaction(SIGALRM, &sa, &saalrm);
52542122Sdes	config();
52642122Sdes	sa.sa_handler = flag_config;
52748962Ssheldonh	sigaction(SIGHUP, &sa, &sahup);
52842122Sdes	sa.sa_handler = flag_reapchild;
52948962Ssheldonh	sigaction(SIGCHLD, &sa, &sachld);
53035848Sguido	sa.sa_handler = SIG_IGN;
53135948Sbde	sigaction(SIGPIPE, &sa, &sapipe);
5321553Srgrimes
5331553Srgrimes	{
5341553Srgrimes		/* space for daemons to overwrite environment for ps */
5351553Srgrimes#define	DUMMYSIZE	100
5361553Srgrimes		char dummy[DUMMYSIZE];
5371553Srgrimes
53819298Salex		(void)memset(dummy, 'x', DUMMYSIZE - 1);
5391553Srgrimes		dummy[DUMMYSIZE - 1] = '\0';
5401553Srgrimes		(void)setenv("inetd_dummy", dummy, 1);
5411553Srgrimes	}
5421553Srgrimes
54342250Sdes	if (pipe(signalpipe) != 0) {
54452219Scharnier		syslog(LOG_ERR, "pipe: %m");
54542250Sdes		exit(EX_OSERR);
54642122Sdes	}
547111324Sdwmalone	if (fcntl(signalpipe[0], F_SETFD, FD_CLOEXEC) < 0 ||
548111324Sdwmalone	    fcntl(signalpipe[1], F_SETFD, FD_CLOEXEC) < 0) {
549111324Sdwmalone		syslog(LOG_ERR, "signalpipe: fcntl (F_SETFD, FD_CLOEXEC): %m");
550111324Sdwmalone		exit(EX_OSERR);
551111324Sdwmalone	}
55242122Sdes	FD_SET(signalpipe[0], &allsock);
55348991Ssheldonh#ifdef SANITY_CHECK
55447015Sdes	nsock++;
55548991Ssheldonh#endif
55648989Ssheldonh	if (signalpipe[0] > maxsock)
55748989Ssheldonh	    maxsock = signalpipe[0];
55848989Ssheldonh	if (signalpipe[1] > maxsock)
55948989Ssheldonh	    maxsock = signalpipe[1];
56041685Sdillon
5611553Srgrimes	for (;;) {
5621553Srgrimes	    int n, ctrl;
5631553Srgrimes	    fd_set readable;
5641553Srgrimes
56548991Ssheldonh#ifdef SANITY_CHECK
5661553Srgrimes	    if (nsock == 0) {
56747015Sdes		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
56847015Sdes		exit(EX_SOFTWARE);
5691553Srgrimes	    }
57048991Ssheldonh#endif
5711553Srgrimes	    readable = allsock;
57242122Sdes	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
57342122Sdes		(fd_set *)0, (struct timeval *)0)) <= 0) {
57442122Sdes		    if (n < 0 && errno != EINTR) {
5751553Srgrimes			syslog(LOG_WARNING, "select: %m");
57628907Simp			sleep(1);
57728907Simp		    }
5781553Srgrimes		    continue;
5791553Srgrimes	    }
58042122Sdes	    /* handle any queued signal flags */
58142250Sdes	    if (FD_ISSET(signalpipe[0], &readable)) {
58271399Sdwmalone		int nsig;
58371399Sdwmalone		if (ioctl(signalpipe[0], FIONREAD, &nsig) != 0) {
58442122Sdes		    syslog(LOG_ERR, "ioctl: %m");
58542122Sdes		    exit(EX_OSERR);
58642122Sdes		}
58771399Sdwmalone		while (--nsig >= 0) {
58842250Sdes		    char c;
58942250Sdes		    if (read(signalpipe[0], &c, 1) != 1) {
59042250Sdes			syslog(LOG_ERR, "read: %m");
59142250Sdes			exit(EX_OSERR);
59242250Sdes		    }
59342250Sdes		    if (debug)
59456482Scharnier			warnx("handling signal flag %c", c);
59542250Sdes		    switch(c) {
59642250Sdes		    case 'A': /* sigalrm */
59742250Sdes			retry();
59842250Sdes			break;
59942250Sdes		    case 'C': /* sigchld */
60042250Sdes			reapchild();
60142250Sdes			break;
60242250Sdes		    case 'H': /* sighup */
60342250Sdes			config();
60442250Sdes			break;
60542250Sdes		    }
60642250Sdes		}
60742122Sdes	    }
6081553Srgrimes	    for (sep = servtab; n && sep; sep = sep->se_next)
6091553Srgrimes	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
6101553Srgrimes		    n--;
6111553Srgrimes		    if (debug)
61229602Scharnier			    warnx("someone wants %s", sep->se_service);
613101474Sume		    dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep);
614101474Sume		    conn = NULL;
61519618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
61653256Speter			    i = 1;
61753256Speter			    if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
61853256Speter				    syslog(LOG_ERR, "ioctl (FIONBIO, 1): %m");
6191553Srgrimes			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
62071399Sdwmalone				(socklen_t *)0);
6211553Srgrimes			    if (debug)
62229602Scharnier				    warnx("accept, ctrl %d", ctrl);
6231553Srgrimes			    if (ctrl < 0) {
6241553Srgrimes				    if (errno != EINTR)
6251553Srgrimes					    syslog(LOG_WARNING,
6261553Srgrimes						"accept (for %s): %m",
62737844Sphk						sep->se_service);
62837816Sphk                                      if (sep->se_accept &&
62937816Sphk                                          sep->se_socktype == SOCK_STREAM)
63037816Sphk                                              close(ctrl);
6311553Srgrimes				    continue;
6321553Srgrimes			    }
63353256Speter			    i = 0;
63453256Speter			    if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
63553256Speter				    syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m");
63653256Speter			    if (ioctl(ctrl, FIONBIO, &i) < 0)
63753256Speter				    syslog(LOG_ERR, "ioctl2(FIONBIO, 0): %m");
63830847Sdima			    if (cpmip(sep, ctrl) < 0) {
63930847Sdima				close(ctrl);
64030847Sdima				continue;
64130847Sdima			    }
642101474Sume			    if (dofork &&
643101474Sume				(conn = search_conn(sep, ctrl)) != NULL &&
644101474Sume				!room_conn(sep, conn)) {
645101474Sume				close(ctrl);
646101474Sume				continue;
647101474Sume			    }
6481553Srgrimes		    } else
6491553Srgrimes			    ctrl = sep->se_fd;
65048382Ssheldonh		    if (log && !ISWRAP(sep)) {
65171399Sdwmalone			    char pname[INET6_ADDRSTRLEN] = "unknown";
65271399Sdwmalone			    socklen_t sl;
65371399Sdwmalone			    sl = sizeof peermax;
65448382Ssheldonh			    if (getpeername(ctrl, (struct sockaddr *)
65571399Sdwmalone					    &peermax, &sl)) {
65671399Sdwmalone				    sl = sizeof peermax;
65748382Ssheldonh				    if (recvfrom(ctrl, buf, sizeof(buf),
65848382Ssheldonh					MSG_PEEK,
65956590Sshin					(struct sockaddr *)&peermax,
66071399Sdwmalone					&sl) >= 0) {
66156590Sshin				      getnameinfo((struct sockaddr *)&peermax,
66257383Sshin						  peer.sa_len,
66356590Sshin						  pname, sizeof(pname),
66456590Sshin						  NULL, 0,
66556590Sshin						  NI_NUMERICHOST|
66656590Sshin						  NI_WITHSCOPEID);
66756590Sshin				    }
66856590Sshin			    } else {
66956590Sshin			            getnameinfo((struct sockaddr *)&peermax,
67057383Sshin						peer.sa_len,
67156590Sshin						pname, sizeof(pname),
67256590Sshin						NULL, 0,
67356590Sshin						NI_NUMERICHOST|
67456590Sshin						NI_WITHSCOPEID);
67548382Ssheldonh			    }
67671399Sdwmalone			    syslog(LOG_INFO,"%s from %s", sep->se_service, pname);
67748382Ssheldonh		    }
67842122Sdes		    (void) sigblock(SIGBLOCK);
6791553Srgrimes		    pid = 0;
68047972Ssheldonh		    /*
68148958Ssheldonh		     * Fork for all external services, builtins which need to
68248958Ssheldonh		     * fork and anything we're wrapping (as wrapping might
68348958Ssheldonh		     * block or use hosts_options(5) twist).
68447972Ssheldonh		     */
6851553Srgrimes		    if (dofork) {
6861553Srgrimes			    if (sep->se_count++ == 0)
68737856Sache				(void)gettimeofday(&sep->se_time, (struct timezone *)NULL);
68864197Sdwmalone			    else if (toomany > 0 && sep->se_count >= toomany) {
6891553Srgrimes				struct timeval now;
6901553Srgrimes
69137856Sache				(void)gettimeofday(&now, (struct timezone *)NULL);
6921553Srgrimes				if (now.tv_sec - sep->se_time.tv_sec >
6931553Srgrimes				    CNT_INTVL) {
6941553Srgrimes					sep->se_time = now;
6951553Srgrimes					sep->se_count = 1;
6961553Srgrimes				} else {
6971553Srgrimes					syslog(LOG_ERR,
6981553Srgrimes			"%s/%s server failing (looping), service terminated",
6991553Srgrimes					    sep->se_service, sep->se_proto);
70067415Sdwmalone					if (sep->se_accept &&
70167415Sdwmalone					    sep->se_socktype == SOCK_STREAM)
70267415Sdwmalone						close(ctrl);
7031553Srgrimes					close_sep(sep);
704101474Sume					free_conn(conn);
70542122Sdes					sigsetmask(0L);
7061553Srgrimes					if (!timingout) {
7071553Srgrimes						timingout = 1;
7081553Srgrimes						alarm(RETRYTIME);
7091553Srgrimes					}
7101553Srgrimes					continue;
7111553Srgrimes				}
7121553Srgrimes			    }
7131553Srgrimes			    pid = fork();
7141553Srgrimes		    }
7151553Srgrimes		    if (pid < 0) {
7161553Srgrimes			    syslog(LOG_ERR, "fork: %m");
71719618Sjulian			    if (sep->se_accept &&
7181553Srgrimes				sep->se_socktype == SOCK_STREAM)
7191553Srgrimes				    close(ctrl);
720101474Sume			    free_conn(conn);
72142122Sdes			    sigsetmask(0L);
7221553Srgrimes			    sleep(1);
7231553Srgrimes			    continue;
7241553Srgrimes		    }
725101474Sume		    if (pid) {
726101474Sume			addchild_conn(conn, pid);
72719618Sjulian			addchild(sep, pid);
728101474Sume		    }
72942122Sdes		    sigsetmask(0L);
7301553Srgrimes		    if (pid == 0) {
7311553Srgrimes			    if (dofork) {
73248962Ssheldonh				sigaction(SIGALRM, &saalrm, (struct sigaction *)0);
73348962Ssheldonh				sigaction(SIGCHLD, &sachld, (struct sigaction *)0);
73448962Ssheldonh				sigaction(SIGHUP, &sahup, (struct sigaction *)0);
73548962Ssheldonh				/* SIGPIPE reset before exec */
7361553Srgrimes			    }
73735829Sguido			    /*
73835829Sguido			     * Call tcpmux to find the real service to exec.
73935829Sguido			     */
74035829Sguido			    if (sep->se_bi &&
74178694Sdwmalone				sep->se_bi->bi_fn == (bi_fn_t *) tcpmux) {
74235829Sguido				    sep = tcpmux(ctrl);
74335829Sguido				    if (sep == NULL) {
74435829Sguido					    close(ctrl);
74535829Sguido					    _exit(0);
74635829Sguido				    }
74735829Sguido			    }
74848382Ssheldonh			    if (ISWRAP(sep)) {
74948698Ssheldonh				inetd_setproctitle("wrapping", ctrl);
75047972Ssheldonh				service = sep->se_server_name ?
75147972Ssheldonh				    sep->se_server_name : sep->se_service;
75247972Ssheldonh				request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, NULL);
75345089Smarkm				fromhost(&req);
75447972Ssheldonh				deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
75547972Ssheldonh				allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
75645089Smarkm				denied = !hosts_access(&req);
75745089Smarkm				if (denied) {
75845089Smarkm				    syslog(deny_severity,
75996224Sume				        "refused connection from %.500s, service %s (%s%s)",
76096224Sume				        eval_client(&req), service, sep->se_proto,
761110802Sume					(whichaf(&req) == AF_INET6) ? "6" : "");
76248382Ssheldonh				    if (sep->se_socktype != SOCK_STREAM)
76348382Ssheldonh					recv(ctrl, buf, sizeof (buf), 0);
76464059Sdwmalone				    if (dofork) {
76564059Sdwmalone					sleep(1);
76648382Ssheldonh					_exit(0);
76764059Sdwmalone				    }
76845089Smarkm				}
76945089Smarkm				if (log) {
77045089Smarkm				    syslog(allow_severity,
77196224Sume				        "connection from %.500s, service %s (%s%s)",
77296224Sume					eval_client(&req), service, sep->se_proto,
773110802Sume					(whichaf(&req) == AF_INET6) ? "6" : "");
77445089Smarkm				}
77545089Smarkm			    }
77619617Sjulian			    if (sep->se_bi) {
7771553Srgrimes				(*sep->se_bi->bi_fn)(ctrl, sep);
77819617Sjulian			    } else {
7791553Srgrimes				if (debug)
78029602Scharnier					warnx("%d execl %s",
78129602Scharnier						getpid(), sep->se_server);
782111324Sdwmalone				/* Clear close-on-exec. */
783111324Sdwmalone				if (fcntl(ctrl, F_SETFD, 0) < 0) {
784111324Sdwmalone					syslog(LOG_ERR,
785111324Sdwmalone					    "%s/%s: fcntl (F_SETFD, 0): %m",
786111324Sdwmalone						sep->se_service, sep->se_proto);
787111324Sdwmalone					_exit(EX_OSERR);
788111324Sdwmalone				}
789111324Sdwmalone				if (ctrl != 0) {
790111324Sdwmalone					dup2(ctrl, 0);
791111324Sdwmalone					close(ctrl);
792111324Sdwmalone				}
7931553Srgrimes				dup2(0, 1);
7941553Srgrimes				dup2(0, 2);
7951553Srgrimes				if ((pwd = getpwnam(sep->se_user)) == NULL) {
7961553Srgrimes					syslog(LOG_ERR,
79756482Scharnier					    "%s/%s: %s: no such user",
7981553Srgrimes						sep->se_service, sep->se_proto,
7991553Srgrimes						sep->se_user);
8001553Srgrimes					if (sep->se_socktype != SOCK_STREAM)
8011553Srgrimes						recv(0, buf, sizeof (buf), 0);
80219617Sjulian					_exit(EX_NOUSER);
8031553Srgrimes				}
80430807Sache				grp = NULL;
80530807Sache				if (   sep->se_group != NULL
80630807Sache				    && (grp = getgrnam(sep->se_group)) == NULL
80730807Sache				   ) {
80830807Sache					syslog(LOG_ERR,
80956482Scharnier					    "%s/%s: %s: no such group",
81030807Sache						sep->se_service, sep->se_proto,
81130807Sache						sep->se_group);
81230807Sache					if (sep->se_socktype != SOCK_STREAM)
81330807Sache						recv(0, buf, sizeof (buf), 0);
81430807Sache					_exit(EX_NOUSER);
81530807Sache				}
81630807Sache				if (grp != NULL)
81730807Sache					pwd->pw_gid = grp->gr_gid;
81821640Speter#ifdef LOGIN_CAP
81930792Sache				if ((lc = login_getclass(sep->se_class)) == NULL) {
82030792Sache					/* error syslogged by getclass */
82130792Sache					syslog(LOG_ERR,
82230792Sache					    "%s/%s: %s: login class error",
82337850Sache						sep->se_service, sep->se_proto,
82437850Sache						sep->se_class);
82530792Sache					if (sep->se_socktype != SOCK_STREAM)
82630792Sache						recv(0, buf, sizeof (buf), 0);
82730792Sache					_exit(EX_NOUSER);
82830792Sache				}
82921640Speter#endif
83012024Speter				if (setsid() < 0) {
83112024Speter					syslog(LOG_ERR,
83212024Speter						"%s: can't setsid(): %m",
83312024Speter						 sep->se_service);
83419617Sjulian					/* _exit(EX_OSERR); not fatal yet */
83512024Speter				}
83621640Speter#ifdef LOGIN_CAP
83721640Speter				if (setusercontext(lc, pwd, pwd->pw_uid,
838109349Srwatson				    LOGIN_SETALL & ~LOGIN_SETMAC)
839108951Srwatson				    != 0) {
84021640Speter					syslog(LOG_ERR,
84121640Speter					 "%s: can't setusercontext(..%s..): %m",
84221640Speter					 sep->se_service, sep->se_user);
84321640Speter					_exit(EX_OSERR);
84421640Speter				}
845111323Sdwmalone				login_close(lc);
84621640Speter#else
8471553Srgrimes				if (pwd->pw_uid) {
84812024Speter					if (setlogin(sep->se_user) < 0) {
84912024Speter						syslog(LOG_ERR,
85012024Speter						 "%s: can't setlogin(%s): %m",
85112024Speter						 sep->se_service, sep->se_user);
85219617Sjulian						/* _exit(EX_OSERR); not yet */
85312024Speter					}
8541553Srgrimes					if (setgid(pwd->pw_gid) < 0) {
8551553Srgrimes						syslog(LOG_ERR,
8568857Srgrimes						  "%s: can't set gid %d: %m",
8571553Srgrimes						  sep->se_service, pwd->pw_gid);
85819617Sjulian						_exit(EX_OSERR);
8591553Srgrimes					}
8601553Srgrimes					(void) initgroups(pwd->pw_name,
8611553Srgrimes							pwd->pw_gid);
8621553Srgrimes					if (setuid(pwd->pw_uid) < 0) {
8631553Srgrimes						syslog(LOG_ERR,
8648857Srgrimes						  "%s: can't set uid %d: %m",
8651553Srgrimes						  sep->se_service, pwd->pw_uid);
86619617Sjulian						_exit(EX_OSERR);
8671553Srgrimes					}
8681553Srgrimes				}
86921640Speter#endif
87035948Sbde				sigaction(SIGPIPE, &sapipe,
87135948Sbde				    (struct sigaction *)0);
8721553Srgrimes				execv(sep->se_server, sep->se_argv);
87345089Smarkm				syslog(LOG_ERR,
87445089Smarkm				    "cannot execute %s: %m", sep->se_server);
8751553Srgrimes				if (sep->se_socktype != SOCK_STREAM)
8761553Srgrimes					recv(0, buf, sizeof (buf), 0);
8771553Srgrimes			    }
87847972Ssheldonh			    if (dofork)
87947972Ssheldonh				_exit(0);
8801553Srgrimes		    }
88119618Sjulian		    if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
8821553Srgrimes			    close(ctrl);
8831553Srgrimes		}
8841553Srgrimes	}
8851553Srgrimes}
8861553Srgrimes
88719618Sjulian/*
88842122Sdes * Add a signal flag to the signal flag queue for later handling
88942122Sdes */
89042122Sdes
89178694Sdwmalonevoid
89298558Sjmallettflag_signal(int c)
89342122Sdes{
89469546Sdwmalone	char ch = c;
89569546Sdwmalone
89669546Sdwmalone	if (write(signalpipe[1], &ch, 1) != 1) {
89742250Sdes		syslog(LOG_ERR, "write: %m");
89848985Ssheldonh		_exit(EX_OSERR);
89942250Sdes	}
90042122Sdes}
90142122Sdes
90242122Sdes/*
90319618Sjulian * Record a new child pid for this service. If we've reached the
90419618Sjulian * limit on children, then stop accepting incoming requests.
90519618Sjulian */
90619618Sjulian
9071553Srgrimesvoid
90819618Sjulianaddchild(struct servtab *sep, pid_t pid)
90919618Sjulian{
91064197Sdwmalone	if (sep->se_maxchild <= 0)
91164197Sdwmalone		return;
91219618Sjulian#ifdef SANITY_CHECK
91319618Sjulian	if (sep->se_numchild >= sep->se_maxchild) {
91419618Sjulian		syslog(LOG_ERR, "%s: %d >= %d",
91519618Sjulian		    __FUNCTION__, sep->se_numchild, sep->se_maxchild);
91619618Sjulian		exit(EX_SOFTWARE);
91719618Sjulian	}
91819618Sjulian#endif
91919618Sjulian	sep->se_pids[sep->se_numchild++] = pid;
92019618Sjulian	if (sep->se_numchild == sep->se_maxchild)
92119618Sjulian		disable(sep);
92219618Sjulian}
92319618Sjulian
92419618Sjulian/*
92519618Sjulian * Some child process has exited. See if it's on somebody's list.
92619618Sjulian */
92719618Sjulian
92819618Sjulianvoid
92998561Sjmallettflag_reapchild(int signo __unused)
9301553Srgrimes{
93142250Sdes	flag_signal('C');
93242122Sdes}
93342122Sdes
93442122Sdesvoid
93598558Sjmallettreapchild(void)
93642122Sdes{
93719618Sjulian	int k, status;
9381553Srgrimes	pid_t pid;
9391553Srgrimes	struct servtab *sep;
9401553Srgrimes
9411553Srgrimes	for (;;) {
9421553Srgrimes		pid = wait3(&status, WNOHANG, (struct rusage *)0);
9431553Srgrimes		if (pid <= 0)
9441553Srgrimes			break;
9451553Srgrimes		if (debug)
946102939Sdwmalone			warnx("%d reaped, %s %u", pid,
947102939Sdwmalone			    WIFEXITED(status) ? "status" : "signal",
948102939Sdwmalone			    WIFEXITED(status) ? WEXITSTATUS(status)
949102939Sdwmalone				: WTERMSIG(status));
95019618Sjulian		for (sep = servtab; sep; sep = sep->se_next) {
95119618Sjulian			for (k = 0; k < sep->se_numchild; k++)
95219618Sjulian				if (sep->se_pids[k] == pid)
95319618Sjulian					break;
95419618Sjulian			if (k == sep->se_numchild)
95519618Sjulian				continue;
95619618Sjulian			if (sep->se_numchild == sep->se_maxchild)
95719618Sjulian				enable(sep);
95819618Sjulian			sep->se_pids[k] = sep->se_pids[--sep->se_numchild];
959102939Sdwmalone			if (WIFSIGNALED(status) || WEXITSTATUS(status))
96019618Sjulian				syslog(LOG_WARNING,
961102939Sdwmalone				    "%s[%d]: exited, %s %u",
962102939Sdwmalone				    sep->se_server, pid,
963102939Sdwmalone				    WIFEXITED(status) ? "status" : "signal",
964102939Sdwmalone				    WIFEXITED(status) ? WEXITSTATUS(status)
965102939Sdwmalone					: WTERMSIG(status));
96619618Sjulian			break;
96719618Sjulian		}
968101474Sume		reapchild_conn(pid);
9691553Srgrimes	}
9701553Srgrimes}
9711553Srgrimes
9721553Srgrimesvoid
97398561Sjmallettflag_config(int signo __unused)
9741553Srgrimes{
97542250Sdes	flag_signal('H');
97642122Sdes}
97742122Sdes
97878694Sdwmalonevoid
97998558Sjmallettconfig(void)
98042122Sdes{
98119618Sjulian	struct servtab *sep, *new, **sepp;
98242122Sdes	long omask;
983100127Salfred	int new_nomapped;
984111323Sdwmalone#ifdef LOGIN_CAP
985111323Sdwmalone	login_cap_t *lc = NULL;
986111323Sdwmalone#endif
9871553Srgrimes
9881553Srgrimes	if (!setconfig()) {
9891553Srgrimes		syslog(LOG_ERR, "%s: %m", CONFIG);
9901553Srgrimes		return;
9911553Srgrimes	}
9921553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
9931553Srgrimes		sep->se_checked = 0;
99419618Sjulian	while ((new = getconfigent())) {
99530807Sache		if (getpwnam(new->se_user) == NULL) {
9961553Srgrimes			syslog(LOG_ERR,
99756482Scharnier				"%s/%s: no such user '%s', service ignored",
99819618Sjulian				new->se_service, new->se_proto, new->se_user);
9991553Srgrimes			continue;
10001553Srgrimes		}
100130807Sache		if (new->se_group && getgrnam(new->se_group) == NULL) {
100230807Sache			syslog(LOG_ERR,
100356482Scharnier				"%s/%s: no such group '%s', service ignored",
100430807Sache				new->se_service, new->se_proto, new->se_group);
100530807Sache			continue;
100630807Sache		}
100730792Sache#ifdef LOGIN_CAP
1008111323Sdwmalone		if ((lc = login_getclass(new->se_class)) == NULL) {
100930792Sache			/* error syslogged by getclass */
101030792Sache			syslog(LOG_ERR,
101137850Sache				"%s/%s: %s: login class error, service ignored",
101237850Sache				new->se_service, new->se_proto, new->se_class);
101330792Sache			continue;
101430792Sache		}
1015111323Sdwmalone		login_close(lc);
101630792Sache#endif
1017100127Salfred		new_nomapped = new->se_nomapped;
10181553Srgrimes		for (sep = servtab; sep; sep = sep->se_next)
101919618Sjulian			if (strcmp(sep->se_service, new->se_service) == 0 &&
102056590Sshin			    strcmp(sep->se_proto, new->se_proto) == 0 &&
1021100127Salfred			    sep->se_rpc == new->se_rpc &&
102278356Sdwmalone			    sep->se_socktype == new->se_socktype &&
102356590Sshin			    sep->se_family == new->se_family)
10241553Srgrimes				break;
10251553Srgrimes		if (sep != 0) {
10261553Srgrimes			int i;
10271553Srgrimes
102898611Sjmallett#define SWAP(t,a, b) { t c = a; a = b; b = c; }
102942122Sdes			omask = sigblock(SIGBLOCK);
103056590Sshin			if (sep->se_nomapped != new->se_nomapped) {
1031100127Salfred				/* for rpc keep old nommaped till unregister */
1032100127Salfred				if (!sep->se_rpc)
1033100127Salfred					sep->se_nomapped = new->se_nomapped;
103456590Sshin				sep->se_reset = 1;
103556590Sshin			}
103619618Sjulian			/* copy over outstanding child pids */
103764197Sdwmalone			if (sep->se_maxchild > 0 && new->se_maxchild > 0) {
103819618Sjulian				new->se_numchild = sep->se_numchild;
103919618Sjulian				if (new->se_numchild > new->se_maxchild)
104019618Sjulian					new->se_numchild = new->se_maxchild;
104119618Sjulian				memcpy(new->se_pids, sep->se_pids,
104219618Sjulian				    new->se_numchild * sizeof(*new->se_pids));
104319618Sjulian			}
104498611Sjmallett			SWAP(pid_t *, sep->se_pids, new->se_pids);
104519618Sjulian			sep->se_maxchild = new->se_maxchild;
104619618Sjulian			sep->se_numchild = new->se_numchild;
104730847Sdima			sep->se_maxcpm = new->se_maxcpm;
1048101474Sume			resize_conn(sep, new->se_maxperip);
1049101474Sume			sep->se_maxperip = new->se_maxperip;
105066544Sdwmalone			sep->se_bi = new->se_bi;
105119618Sjulian			/* might need to turn on or off service now */
105219618Sjulian			if (sep->se_fd >= 0) {
105364197Sdwmalone			      if (sep->se_maxchild > 0
105419618Sjulian				  && sep->se_numchild == sep->se_maxchild) {
105519618Sjulian				      if (FD_ISSET(sep->se_fd, &allsock))
105619618Sjulian					  disable(sep);
105719618Sjulian			      } else {
105819618Sjulian				      if (!FD_ISSET(sep->se_fd, &allsock))
105919618Sjulian					  enable(sep);
106019618Sjulian			      }
106119618Sjulian			}
106219618Sjulian			sep->se_accept = new->se_accept;
106398611Sjmallett			SWAP(char *, sep->se_user, new->se_user);
106498611Sjmallett			SWAP(char *, sep->se_group, new->se_group);
106530792Sache#ifdef LOGIN_CAP
106698611Sjmallett			SWAP(char *, sep->se_class, new->se_class);
106730792Sache#endif
106898611Sjmallett			SWAP(char *, sep->se_server, new->se_server);
106998611Sjmallett			SWAP(char *, sep->se_server_name, new->se_server_name);
10701553Srgrimes			for (i = 0; i < MAXARGV; i++)
107198611Sjmallett				SWAP(char *, sep->se_argv[i], new->se_argv[i]);
107256590Sshin#ifdef IPSEC
107398611Sjmallett			SWAP(char *, sep->se_policy, new->se_policy);
107456590Sshin			ipsecsetup(sep);
107556590Sshin#endif
107642122Sdes			sigsetmask(omask);
107719618Sjulian			freeconfig(new);
10781553Srgrimes			if (debug)
10791553Srgrimes				print_service("REDO", sep);
10801553Srgrimes		} else {
108119618Sjulian			sep = enter(new);
10821553Srgrimes			if (debug)
10831553Srgrimes				print_service("ADD ", sep);
10841553Srgrimes		}
10851553Srgrimes		sep->se_checked = 1;
10861553Srgrimes		if (ISMUX(sep)) {
10871553Srgrimes			sep->se_fd = -1;
10881553Srgrimes			continue;
10891553Srgrimes		}
109056590Sshin		switch (sep->se_family) {
109156590Sshin		case AF_INET:
1092102938Sdwmalone			if (!v4bind_ok) {
109356590Sshin				sep->se_fd = -1;
109456590Sshin				continue;
109556590Sshin			}
109656590Sshin			break;
109756590Sshin#ifdef INET6
109856590Sshin		case AF_INET6:
1099102938Sdwmalone			if (!v6bind_ok) {
110056590Sshin				sep->se_fd = -1;
110156590Sshin				continue;
110256590Sshin			}
110356590Sshin			break;
110456590Sshin#endif
110556590Sshin		}
11062657Scsgr		if (!sep->se_rpc) {
110778356Sdwmalone			if (sep->se_family != AF_UNIX) {
110878356Sdwmalone				sp = getservbyname(sep->se_service, sep->se_proto);
110978356Sdwmalone				if (sp == 0) {
111078356Sdwmalone					syslog(LOG_ERR, "%s/%s: unknown service",
111178356Sdwmalone					sep->se_service, sep->se_proto);
111278356Sdwmalone					sep->se_checked = 0;
111378356Sdwmalone					continue;
111478356Sdwmalone				}
11152657Scsgr			}
111656590Sshin			switch (sep->se_family) {
111756590Sshin			case AF_INET:
111856590Sshin				if (sp->s_port != sep->se_ctrladdr4.sin_port) {
111956590Sshin					sep->se_ctrladdr4.sin_port =
112056590Sshin						sp->s_port;
112156590Sshin					sep->se_reset = 1;
112256590Sshin				}
112356590Sshin				break;
112456590Sshin#ifdef INET6
112556590Sshin			case AF_INET6:
112656590Sshin				if (sp->s_port !=
112756590Sshin				    sep->se_ctrladdr6.sin6_port) {
112856590Sshin					sep->se_ctrladdr6.sin6_port =
112956590Sshin						sp->s_port;
113056590Sshin					sep->se_reset = 1;
113156590Sshin				}
113256590Sshin				break;
113356590Sshin#endif
11342657Scsgr			}
113556590Sshin			if (sep->se_reset != 0 && sep->se_fd >= 0)
113656590Sshin				close_sep(sep);
11372657Scsgr		} else {
11382657Scsgr			rpc = getrpcbyname(sep->se_service);
11392657Scsgr			if (rpc == 0) {
114052219Scharnier				syslog(LOG_ERR, "%s/%s unknown RPC service",
11412657Scsgr					sep->se_service, sep->se_proto);
11422657Scsgr				if (sep->se_fd != -1)
11432657Scsgr					(void) close(sep->se_fd);
11442657Scsgr				sep->se_fd = -1;
11452657Scsgr					continue;
11462657Scsgr			}
1147100127Salfred			if (sep->se_reset != 0 ||
1148100127Salfred			    rpc->r_number != sep->se_rpc_prog) {
11492657Scsgr				if (sep->se_rpc_prog)
11502657Scsgr					unregisterrpc(sep);
11512657Scsgr				sep->se_rpc_prog = rpc->r_number;
11522657Scsgr				if (sep->se_fd != -1)
11532657Scsgr					(void) close(sep->se_fd);
11542657Scsgr				sep->se_fd = -1;
11552657Scsgr			}
1156100127Salfred			sep->se_nomapped = new_nomapped;
11571553Srgrimes		}
1158100127Salfred		sep->se_reset = 0;
11591553Srgrimes		if (sep->se_fd == -1)
11601553Srgrimes			setup(sep);
11611553Srgrimes	}
11621553Srgrimes	endconfig();
11631553Srgrimes	/*
11641553Srgrimes	 * Purge anything not looked at above.
11651553Srgrimes	 */
116642122Sdes	omask = sigblock(SIGBLOCK);
11671553Srgrimes	sepp = &servtab;
116819617Sjulian	while ((sep = *sepp)) {
11691553Srgrimes		if (sep->se_checked) {
11701553Srgrimes			sepp = &sep->se_next;
11711553Srgrimes			continue;
11721553Srgrimes		}
11731553Srgrimes		*sepp = sep->se_next;
11741553Srgrimes		if (sep->se_fd >= 0)
11751553Srgrimes			close_sep(sep);
11761553Srgrimes		if (debug)
11771553Srgrimes			print_service("FREE", sep);
11782657Scsgr		if (sep->se_rpc && sep->se_rpc_prog > 0)
11792657Scsgr			unregisterrpc(sep);
11801553Srgrimes		freeconfig(sep);
118171399Sdwmalone		free(sep);
11821553Srgrimes	}
118342122Sdes	(void) sigsetmask(omask);
11841553Srgrimes}
11851553Srgrimes
11861553Srgrimesvoid
118798558Sjmallettunregisterrpc(struct servtab *sep)
11882657Scsgr{
118978694Sdwmalone        u_int i;
11902657Scsgr        struct servtab *sepp;
119142122Sdes	long omask;
1192100127Salfred	struct netconfig *netid4, *netid6;
11932657Scsgr
119442122Sdes	omask = sigblock(SIGBLOCK);
1195100127Salfred	netid4 = sep->se_socktype == SOCK_DGRAM ? udpconf : tcpconf;
1196100127Salfred	netid6 = sep->se_socktype == SOCK_DGRAM ? udp6conf : tcp6conf;
1197100127Salfred	if (sep->se_family == AF_INET)
1198100127Salfred		netid6 = NULL;
1199100127Salfred	else if (sep->se_nomapped)
1200100127Salfred		netid4 = NULL;
1201100127Salfred	/*
1202100127Salfred	 * Conflict if same prog and protocol - In that case one should look
1203100127Salfred	 * to versions, but it is not interesting: having separate servers for
1204100127Salfred	 * different versions does not work well.
1205100127Salfred	 * Therefore one do not unregister if there is a conflict.
1206100127Salfred	 * There is also transport conflict if destroying INET when INET46
1207100127Salfred	 * exists, or destroying INET46 when INET exists
1208100127Salfred	 */
12092657Scsgr        for (sepp = servtab; sepp; sepp = sepp->se_next) {
12102657Scsgr                if (sepp == sep)
12112657Scsgr                        continue;
1212100127Salfred		if (sepp->se_checked == 0 ||
12132657Scsgr                    !sepp->se_rpc ||
1214100127Salfred		    strcmp(sep->se_proto, sepp->se_proto) != 0 ||
12152657Scsgr                    sep->se_rpc_prog != sepp->se_rpc_prog)
12162657Scsgr			continue;
1217100127Salfred		if (sepp->se_family == AF_INET)
1218100127Salfred			netid4 = NULL;
1219100127Salfred		if (sepp->se_family == AF_INET6) {
1220100127Salfred			netid6 = NULL;
1221100127Salfred			if (!sep->se_nomapped)
1222100127Salfred				netid4 = NULL;
1223100127Salfred		}
1224100127Salfred		if (netid4 == NULL && netid6 == NULL)
1225100127Salfred			return;
12262657Scsgr        }
12272657Scsgr        if (debug)
12282657Scsgr                print_service("UNREG", sep);
1229100127Salfred        for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
1230100127Salfred		if (netid4)
1231100127Salfred			rpcb_unset(sep->se_rpc_prog, i, netid4);
1232100127Salfred		if (netid6)
1233100127Salfred			rpcb_unset(sep->se_rpc_prog, i, netid6);
1234100127Salfred	}
12352657Scsgr        if (sep->se_fd != -1)
12362657Scsgr                (void) close(sep->se_fd);
12372657Scsgr        sep->se_fd = -1;
123842122Sdes	(void) sigsetmask(omask);
12392657Scsgr}
12402657Scsgr
12412657Scsgrvoid
124298561Sjmallettflag_retry(int signo __unused)
12431553Srgrimes{
124442250Sdes	flag_signal('A');
124542122Sdes}
124642122Sdes
124742122Sdesvoid
124898558Sjmallettretry(void)
124942122Sdes{
12501553Srgrimes	struct servtab *sep;
12511553Srgrimes
12521553Srgrimes	timingout = 0;
12531553Srgrimes	for (sep = servtab; sep; sep = sep->se_next)
125419617Sjulian		if (sep->se_fd == -1 && !ISMUX(sep))
12551553Srgrimes			setup(sep);
12561553Srgrimes}
12571553Srgrimes
12581553Srgrimesvoid
125998558Sjmallettsetup(struct servtab *sep)
12601553Srgrimes{
12611553Srgrimes	int on = 1;
12621553Srgrimes
126356590Sshin	if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
12641553Srgrimes		if (debug)
126529602Scharnier			warn("socket failed on %s/%s",
126629602Scharnier				sep->se_service, sep->se_proto);
12671553Srgrimes		syslog(LOG_ERR, "%s/%s: socket: %m",
12681553Srgrimes		    sep->se_service, sep->se_proto);
12691553Srgrimes		return;
12701553Srgrimes	}
1271111324Sdwmalone	/* Set all listening sockets to close-on-exec. */
1272111324Sdwmalone	if (fcntl(sep->se_fd, F_SETFD, FD_CLOEXEC) < 0) {
1273111324Sdwmalone		syslog(LOG_ERR, "%s/%s: fcntl (F_SETFD, FD_CLOEXEC): %m",
1274111324Sdwmalone		    sep->se_service, sep->se_proto);
1275111324Sdwmalone		close(sep->se_fd);
1276111324Sdwmalone		return;
1277111324Sdwmalone	}
12781553Srgrimes#define	turnon(fd, opt) \
12791553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
12801553Srgrimes	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
12811553Srgrimes	    turnon(sep->se_fd, SO_DEBUG) < 0)
12821553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
12831553Srgrimes	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
12841553Srgrimes		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
128525253Swollman#ifdef SO_PRIVSTATE
128613956Swollman	if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
128713956Swollman		syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
128825253Swollman#endif
128956590Sshin	/* tftpd opens a new connection then needs more infos */
129056590Sshin	if ((sep->se_family == AF_INET6) &&
129156590Sshin	    (strcmp(sep->se_proto, "udp") == 0) &&
129256590Sshin	    (sep->se_accept == 0) &&
1293121559Sume	    (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
129456590Sshin			(char *)&on, sizeof (on)) < 0))
129556590Sshin		syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m");
129658935Sume	if (sep->se_family == AF_INET6) {
129758935Sume		int flag = sep->se_nomapped ? 1 : 0;
1298100505Sume		if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_V6ONLY,
129958935Sume			       (char *)&flag, sizeof (flag)) < 0)
1300100505Sume			syslog(LOG_ERR, "setsockopt (IPV6_V6ONLY): %m");
130158935Sume	}
13021553Srgrimes#undef turnon
130336042Sguido	if (sep->se_type == TTCP_TYPE)
130436042Sguido		if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH,
130536042Sguido		    (char *)&on, sizeof (on)) < 0)
130636042Sguido			syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m");
130756590Sshin#ifdef IPV6_FAITH
130856590Sshin	if (sep->se_type == FAITH_TYPE) {
130956590Sshin		if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH, &on,
131056590Sshin				sizeof(on)) < 0) {
131156590Sshin			syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m");
131256590Sshin		}
131356590Sshin	}
131456590Sshin#endif
131556590Sshin#ifdef IPSEC
131656590Sshin	ipsecsetup(sep);
131756590Sshin#endif
131878356Sdwmalone	if (sep->se_family == AF_UNIX) {
131978356Sdwmalone		(void) unlink(sep->se_ctrladdr_un.sun_path);
132078356Sdwmalone		umask(0777); /* Make socket with conservative permissions */
132178356Sdwmalone	}
13221553Srgrimes	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
132356590Sshin	    sep->se_ctrladdr_size) < 0) {
13241553Srgrimes		if (debug)
132529602Scharnier			warn("bind failed on %s/%s",
132629602Scharnier				sep->se_service, sep->se_proto);
13271553Srgrimes		syslog(LOG_ERR, "%s/%s: bind: %m",
13281553Srgrimes		    sep->se_service, sep->se_proto);
13291553Srgrimes		(void) close(sep->se_fd);
13301553Srgrimes		sep->se_fd = -1;
13311553Srgrimes		if (!timingout) {
13321553Srgrimes			timingout = 1;
13331553Srgrimes			alarm(RETRYTIME);
13341553Srgrimes		}
133578356Sdwmalone		if (sep->se_family == AF_UNIX)
133678356Sdwmalone			umask(mask);
13371553Srgrimes		return;
13381553Srgrimes	}
133978356Sdwmalone	if (sep->se_family == AF_UNIX) {
134078356Sdwmalone		/* Ick - fch{own,mod} don't work on Unix domain sockets */
134178356Sdwmalone		if (chown(sep->se_service, sep->se_sockuid, sep->se_sockgid) < 0)
134278356Sdwmalone			syslog(LOG_ERR, "chown socket: %m");
134378356Sdwmalone		if (chmod(sep->se_service, sep->se_sockmode) < 0)
134478356Sdwmalone			syslog(LOG_ERR, "chmod socket: %m");
134578356Sdwmalone		umask(mask);
134678356Sdwmalone	}
13472657Scsgr        if (sep->se_rpc) {
134878694Sdwmalone		u_int i;
134971399Sdwmalone		socklen_t len = sep->se_ctrladdr_size;
1350100127Salfred		struct netconfig *netid, *netid2 = NULL;
1351100127Salfred		struct sockaddr_in sock;
1352100127Salfred		struct netbuf nbuf, nbuf2;
13532657Scsgr
13548857Srgrimes                if (getsockname(sep->se_fd,
13552657Scsgr				(struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
13562657Scsgr                        syslog(LOG_ERR, "%s/%s: getsockname: %m",
13572657Scsgr                               sep->se_service, sep->se_proto);
13582657Scsgr                        (void) close(sep->se_fd);
13592657Scsgr                        sep->se_fd = -1;
13608857Srgrimes                        return;
13612657Scsgr                }
1362100127Salfred		nbuf.buf = &sep->se_ctrladdr;
1363100127Salfred		nbuf.len = sep->se_ctrladdr.sa_len;
1364100127Salfred		if (sep->se_family == AF_INET)
1365100127Salfred			netid = sep->se_socktype==SOCK_DGRAM? udpconf:tcpconf;
1366100127Salfred		else  {
1367100127Salfred			netid = sep->se_socktype==SOCK_DGRAM? udp6conf:tcp6conf;
1368100127Salfred			if (!sep->se_nomapped) { /* INET and INET6 */
1369100127Salfred				netid2 = netid==udp6conf? udpconf:tcpconf;
1370100127Salfred				memset(&sock, 0, sizeof sock);	/* ADDR_ANY */
1371100127Salfred				nbuf2.buf = &sock;
1372100127Salfred				nbuf2.len = sock.sin_len = sizeof sock;
1373100127Salfred				sock.sin_family = AF_INET;
1374100127Salfred				sock.sin_port = sep->se_ctrladdr6.sin6_port;
1375100127Salfred			}
1376100127Salfred		}
13772657Scsgr                if (debug)
13782657Scsgr                        print_service("REG ", sep);
13792657Scsgr                for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
1380100127Salfred			rpcb_unset(sep->se_rpc_prog, i, netid);
1381100127Salfred			rpcb_set(sep->se_rpc_prog, i, netid, &nbuf);
1382100127Salfred			if (netid2) {
1383100127Salfred				rpcb_unset(sep->se_rpc_prog, i, netid2);
1384100127Salfred				rpcb_set(sep->se_rpc_prog, i, netid2, &nbuf2);
1385100127Salfred			}
13862657Scsgr                }
13872657Scsgr        }
13881553Srgrimes	if (sep->se_socktype == SOCK_STREAM)
138917197Sdg		listen(sep->se_fd, 64);
139019618Sjulian	enable(sep);
13911553Srgrimes	if (debug) {
139229602Scharnier		warnx("registered %s on %d",
13931553Srgrimes			sep->se_server, sep->se_fd);
13941553Srgrimes	}
13951553Srgrimes}
13961553Srgrimes
139756590Sshin#ifdef IPSEC
139856590Sshinvoid
139956590Sshinipsecsetup(sep)
140056590Sshin	struct servtab *sep;
140156590Sshin{
140256590Sshin	char *buf;
140356590Sshin	char *policy_in = NULL;
140456590Sshin	char *policy_out = NULL;
140556590Sshin	int level;
140656590Sshin	int opt;
140756590Sshin
140856590Sshin	switch (sep->se_family) {
140956590Sshin	case AF_INET:
141056590Sshin		level = IPPROTO_IP;
141156590Sshin		opt = IP_IPSEC_POLICY;
141256590Sshin		break;
141356590Sshin#ifdef INET6
141456590Sshin	case AF_INET6:
141556590Sshin		level = IPPROTO_IPV6;
141656590Sshin		opt = IPV6_IPSEC_POLICY;
141756590Sshin		break;
141856590Sshin#endif
141956590Sshin	default:
142056590Sshin		return;
142156590Sshin	}
142256590Sshin
142356590Sshin	if (!sep->se_policy || sep->se_policy[0] == '\0') {
142478694Sdwmalone		static char def_in[] = "in entrust", def_out[] = "out entrust";
142578694Sdwmalone		policy_in = def_in;
142678694Sdwmalone		policy_out = def_out;
142756590Sshin	} else {
142856590Sshin		if (!strncmp("in", sep->se_policy, 2))
142956590Sshin			policy_in = sep->se_policy;
143056590Sshin		else if (!strncmp("out", sep->se_policy, 3))
143156590Sshin			policy_out = sep->se_policy;
143256590Sshin		else {
143356590Sshin			syslog(LOG_ERR, "invalid security policy \"%s\"",
143456590Sshin				sep->se_policy);
143556590Sshin			return;
143656590Sshin		}
143756590Sshin	}
143856590Sshin
143956590Sshin	if (policy_in != NULL) {
144056590Sshin		buf = ipsec_set_policy(policy_in, strlen(policy_in));
144156590Sshin		if (buf != NULL) {
144256590Sshin			if (setsockopt(sep->se_fd, level, opt,
144356675Sshin					buf, ipsec_get_policylen(buf)) < 0 &&
144456759Sshin			    debug != 0)
144556759Sshin				warnx("%s/%s: ipsec initialization failed; %s",
144656759Sshin				      sep->se_service, sep->se_proto,
144756759Sshin				      policy_in);
144856590Sshin			free(buf);
144956590Sshin		} else
145056590Sshin			syslog(LOG_ERR, "invalid security policy \"%s\"",
145156590Sshin				policy_in);
145256590Sshin	}
145356590Sshin	if (policy_out != NULL) {
145456590Sshin		buf = ipsec_set_policy(policy_out, strlen(policy_out));
145556590Sshin		if (buf != NULL) {
145656590Sshin			if (setsockopt(sep->se_fd, level, opt,
145756675Sshin					buf, ipsec_get_policylen(buf)) < 0 &&
145856759Sshin			    debug != 0)
145956759Sshin				warnx("%s/%s: ipsec initialization failed; %s",
146056759Sshin				      sep->se_service, sep->se_proto,
146156759Sshin				      policy_out);
146256590Sshin			free(buf);
146356590Sshin		} else
146456590Sshin			syslog(LOG_ERR, "invalid security policy \"%s\"",
146556590Sshin				policy_out);
146656590Sshin	}
146756590Sshin}
146856590Sshin#endif
146956590Sshin
14701553Srgrimes/*
14711553Srgrimes * Finish with a service and its socket.
14721553Srgrimes */
14731553Srgrimesvoid
147498558Sjmallettclose_sep(struct servtab *sep)
14751553Srgrimes{
14761553Srgrimes	if (sep->se_fd >= 0) {
147719618Sjulian		if (FD_ISSET(sep->se_fd, &allsock))
147819618Sjulian			disable(sep);
14791553Srgrimes		(void) close(sep->se_fd);
14801553Srgrimes		sep->se_fd = -1;
14811553Srgrimes	}
14821553Srgrimes	sep->se_count = 0;
148319618Sjulian	sep->se_numchild = 0;	/* forget about any existing children */
14841553Srgrimes}
14851553Srgrimes
148648467Ssheldonhint
148798558Sjmallettmatchservent(const char *name1, const char *name2, const char *proto)
148848467Ssheldonh{
148978356Sdwmalone	char **alias, *p;
149048467Ssheldonh	struct servent *se;
149148467Ssheldonh
149278356Sdwmalone	if (strcmp(proto, "unix") == 0) {
149378356Sdwmalone		if ((p = strrchr(name1, '/')) != NULL)
149478356Sdwmalone			name1 = p + 1;
149578356Sdwmalone		if ((p = strrchr(name2, '/')) != NULL)
149678356Sdwmalone			name2 = p + 1;
149778356Sdwmalone	}
149849026Sdes	if (strcmp(name1, name2) == 0)
149949026Sdes		return(1);
150048467Ssheldonh	if ((se = getservbyname(name1, proto)) != NULL) {
150148467Ssheldonh		if (strcmp(name2, se->s_name) == 0)
150248467Ssheldonh			return(1);
150348467Ssheldonh		for (alias = se->s_aliases; *alias; alias++)
150448467Ssheldonh			if (strcmp(name2, *alias) == 0)
150548467Ssheldonh				return(1);
150648467Ssheldonh	}
150748467Ssheldonh	return(0);
150848467Ssheldonh}
150948467Ssheldonh
15101553Srgrimesstruct servtab *
151198558Sjmallettenter(struct servtab *cp)
15121553Srgrimes{
15131553Srgrimes	struct servtab *sep;
151442122Sdes	long omask;
15151553Srgrimes
15161553Srgrimes	sep = (struct servtab *)malloc(sizeof (*sep));
15171553Srgrimes	if (sep == (struct servtab *)0) {
151849102Ssheldonh		syslog(LOG_ERR, "malloc: %m");
151919617Sjulian		exit(EX_OSERR);
15201553Srgrimes	}
15211553Srgrimes	*sep = *cp;
15221553Srgrimes	sep->se_fd = -1;
152342122Sdes	omask = sigblock(SIGBLOCK);
15241553Srgrimes	sep->se_next = servtab;
15251553Srgrimes	servtab = sep;
152642122Sdes	sigsetmask(omask);
15271553Srgrimes	return (sep);
15281553Srgrimes}
15291553Srgrimes
153019618Sjulianvoid
153198558Sjmallettenable(struct servtab *sep)
153219618Sjulian{
153319618Sjulian	if (debug)
153429602Scharnier		warnx(
153519618Sjulian		    "enabling %s, fd %d", sep->se_service, sep->se_fd);
153619618Sjulian#ifdef SANITY_CHECK
153719618Sjulian	if (sep->se_fd < 0) {
153819618Sjulian		syslog(LOG_ERR,
153919618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
154019618Sjulian		exit(EX_SOFTWARE);
154119618Sjulian	}
154219618Sjulian	if (ISMUX(sep)) {
154319618Sjulian		syslog(LOG_ERR,
154419618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
154519618Sjulian		exit(EX_SOFTWARE);
154619618Sjulian	}
154719618Sjulian	if (FD_ISSET(sep->se_fd, &allsock)) {
154819618Sjulian		syslog(LOG_ERR,
154919618Sjulian		    "%s: %s: not off", __FUNCTION__, sep->se_service);
155019618Sjulian		exit(EX_SOFTWARE);
155119618Sjulian	}
155248991Ssheldonh	nsock++;
155319618Sjulian#endif
155419618Sjulian	FD_SET(sep->se_fd, &allsock);
155519618Sjulian	if (sep->se_fd > maxsock)
155619618Sjulian		maxsock = sep->se_fd;
155719618Sjulian}
155819618Sjulian
155919618Sjulianvoid
156098558Sjmallettdisable(struct servtab *sep)
156119618Sjulian{
156219618Sjulian	if (debug)
156329602Scharnier		warnx(
156419618Sjulian		    "disabling %s, fd %d", sep->se_service, sep->se_fd);
156519618Sjulian#ifdef SANITY_CHECK
156619618Sjulian	if (sep->se_fd < 0) {
156719618Sjulian		syslog(LOG_ERR,
156819618Sjulian		    "%s: %s: bad fd", __FUNCTION__, sep->se_service);
156919618Sjulian		exit(EX_SOFTWARE);
157019618Sjulian	}
157119618Sjulian	if (ISMUX(sep)) {
157219618Sjulian		syslog(LOG_ERR,
157319618Sjulian		    "%s: %s: is mux", __FUNCTION__, sep->se_service);
157419618Sjulian		exit(EX_SOFTWARE);
157519618Sjulian	}
157619618Sjulian	if (!FD_ISSET(sep->se_fd, &allsock)) {
157719618Sjulian		syslog(LOG_ERR,
157819618Sjulian		    "%s: %s: not on", __FUNCTION__, sep->se_service);
157919618Sjulian		exit(EX_SOFTWARE);
158019618Sjulian	}
158119618Sjulian	if (nsock == 0) {
158219618Sjulian		syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
158319618Sjulian		exit(EX_SOFTWARE);
158419618Sjulian	}
158548991Ssheldonh	nsock--;
158619618Sjulian#endif
158719618Sjulian	FD_CLR(sep->se_fd, &allsock);
158819618Sjulian	if (sep->se_fd == maxsock)
158919618Sjulian		maxsock--;
159019618Sjulian}
159119618Sjulian
15921553SrgrimesFILE	*fconfig = NULL;
15931553Srgrimesstruct	servtab serv;
15941553Srgrimeschar	line[LINE_MAX];
15951553Srgrimes
15961553Srgrimesint
159798558Sjmallettsetconfig(void)
15981553Srgrimes{
15991553Srgrimes
16001553Srgrimes	if (fconfig != NULL) {
16011553Srgrimes		fseek(fconfig, 0L, SEEK_SET);
16021553Srgrimes		return (1);
16031553Srgrimes	}
16041553Srgrimes	fconfig = fopen(CONFIG, "r");
16051553Srgrimes	return (fconfig != NULL);
16061553Srgrimes}
16071553Srgrimes
16081553Srgrimesvoid
160998558Sjmallettendconfig(void)
16101553Srgrimes{
16111553Srgrimes	if (fconfig) {
16121553Srgrimes		(void) fclose(fconfig);
16131553Srgrimes		fconfig = NULL;
16141553Srgrimes	}
16151553Srgrimes}
16161553Srgrimes
16171553Srgrimesstruct servtab *
161898558Sjmallettgetconfigent(void)
16191553Srgrimes{
16201553Srgrimes	struct servtab *sep = &serv;
16211553Srgrimes	int argc;
162219618Sjulian	char *cp, *arg, *s;
16232657Scsgr	char *versp;
16241553Srgrimes	static char TCPMUX_TOKEN[] = "tcpmux/";
16251553Srgrimes#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
162656590Sshin#ifdef IPSEC
1627102861Sdwmalone	char *policy;
162856590Sshin#endif
1629102861Sdwmalone	int v4bind;
163056590Sshin#ifdef INET6
1631102861Sdwmalone	int v6bind;
163256590Sshin#endif
1633101474Sume	int i;
16341553Srgrimes
1635102861Sdwmalone#ifdef IPSEC
1636102861Sdwmalone	policy = NULL;
1637102861Sdwmalone#endif
16381553Srgrimesmore:
1639102861Sdwmalone	v4bind = 0;
1640102861Sdwmalone#ifdef INET6
1641102861Sdwmalone	v6bind = 0;
1642102861Sdwmalone#endif
164356590Sshin	while ((cp = nextline(fconfig)) != NULL) {
164456590Sshin#ifdef IPSEC
164556590Sshin		/* lines starting with #@ is not a comment, but the policy */
164656590Sshin		if (cp[0] == '#' && cp[1] == '@') {
164756590Sshin			char *p;
164856590Sshin			for (p = cp + 2; p && *p && isspace(*p); p++)
164956590Sshin				;
165056590Sshin			if (*p == '\0') {
165156590Sshin				if (policy)
165256590Sshin					free(policy);
165356590Sshin				policy = NULL;
165456590Sshin			} else if (ipsec_get_policylen(p) >= 0) {
165556590Sshin				if (policy)
165656590Sshin					free(policy);
165756590Sshin				policy = newstr(p);
165856590Sshin			} else {
165956590Sshin				syslog(LOG_ERR,
166056590Sshin					"%s: invalid ipsec policy \"%s\"",
166156590Sshin					CONFIG, p);
166256590Sshin				exit(EX_CONFIG);
166356590Sshin			}
166456590Sshin		}
166556590Sshin#endif
166656590Sshin		if (*cp == '#' || *cp == '\0')
166756590Sshin			continue;
166856590Sshin		break;
166956590Sshin	}
16701553Srgrimes	if (cp == NULL)
16711553Srgrimes		return ((struct servtab *)0);
16721553Srgrimes	/*
16731553Srgrimes	 * clear the static buffer, since some fields (se_ctrladdr,
16741553Srgrimes	 * for example) don't get initialized here.
16751553Srgrimes	 */
167671399Sdwmalone	memset(sep, 0, sizeof *sep);
16771553Srgrimes	arg = skip(&cp);
16781553Srgrimes	if (cp == NULL) {
16791553Srgrimes		/* got an empty line containing just blanks/tabs. */
16801553Srgrimes		goto more;
16811553Srgrimes	}
168278356Sdwmalone	if (arg[0] == ':') { /* :user:group:perm: */
168378356Sdwmalone		char *user, *group, *perm;
168478356Sdwmalone		struct passwd *pw;
168578356Sdwmalone		struct group *gr;
168678356Sdwmalone		user = arg+1;
168778356Sdwmalone		if ((group = strchr(user, ':')) == NULL) {
168878356Sdwmalone			syslog(LOG_ERR, "no group after user '%s'", user);
168978356Sdwmalone			goto more;
169078356Sdwmalone		}
169178356Sdwmalone		*group++ = '\0';
169278356Sdwmalone		if ((perm = strchr(group, ':')) == NULL) {
169378356Sdwmalone			syslog(LOG_ERR, "no mode after group '%s'", group);
169478356Sdwmalone			goto more;
169578356Sdwmalone		}
169678356Sdwmalone		*perm++ = '\0';
169778356Sdwmalone		if ((pw = getpwnam(user)) == NULL) {
169878356Sdwmalone			syslog(LOG_ERR, "no such user '%s'", user);
169978356Sdwmalone			goto more;
170078356Sdwmalone		}
170178356Sdwmalone		sep->se_sockuid = pw->pw_uid;
170278356Sdwmalone		if ((gr = getgrnam(group)) == NULL) {
170378356Sdwmalone			syslog(LOG_ERR, "no such user '%s'", group);
170478356Sdwmalone			goto more;
170578356Sdwmalone		}
170678356Sdwmalone		sep->se_sockgid = gr->gr_gid;
170778356Sdwmalone		sep->se_sockmode = strtol(perm, &arg, 8);
170878356Sdwmalone		if (*arg != ':') {
170978356Sdwmalone			syslog(LOG_ERR, "bad mode '%s'", perm);
171078356Sdwmalone			goto more;
171178356Sdwmalone		}
171278356Sdwmalone		*arg++ = '\0';
171378356Sdwmalone	} else {
171478356Sdwmalone		sep->se_sockuid = euid;
171578356Sdwmalone		sep->se_sockgid = egid;
171678356Sdwmalone		sep->se_sockmode = 0200;
171778356Sdwmalone	}
17181553Srgrimes	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
17191553Srgrimes		char *c = arg + MUX_LEN;
17201553Srgrimes		if (*c == '+') {
17211553Srgrimes			sep->se_type = MUXPLUS_TYPE;
17221553Srgrimes			c++;
17231553Srgrimes		} else
17241553Srgrimes			sep->se_type = MUX_TYPE;
17251553Srgrimes		sep->se_service = newstr(c);
17261553Srgrimes	} else {
17271553Srgrimes		sep->se_service = newstr(arg);
17281553Srgrimes		sep->se_type = NORM_TYPE;
17291553Srgrimes	}
17301553Srgrimes	arg = sskip(&cp);
17311553Srgrimes	if (strcmp(arg, "stream") == 0)
17321553Srgrimes		sep->se_socktype = SOCK_STREAM;
17331553Srgrimes	else if (strcmp(arg, "dgram") == 0)
17341553Srgrimes		sep->se_socktype = SOCK_DGRAM;
17351553Srgrimes	else if (strcmp(arg, "rdm") == 0)
17361553Srgrimes		sep->se_socktype = SOCK_RDM;
17371553Srgrimes	else if (strcmp(arg, "seqpacket") == 0)
17381553Srgrimes		sep->se_socktype = SOCK_SEQPACKET;
17391553Srgrimes	else if (strcmp(arg, "raw") == 0)
17401553Srgrimes		sep->se_socktype = SOCK_RAW;
17411553Srgrimes	else
17421553Srgrimes		sep->se_socktype = -1;
174336042Sguido
174436042Sguido	arg = sskip(&cp);
174556590Sshin	if (strncmp(arg, "tcp", 3) == 0) {
174656590Sshin		sep->se_proto = newstr(strsep(&arg, "/"));
174756590Sshin		if (arg != NULL) {
174856590Sshin			if (strcmp(arg, "ttcp") == 0)
174956590Sshin				sep->se_type = TTCP_TYPE;
175056590Sshin			else if (strcmp(arg, "faith") == 0)
175156590Sshin				sep->se_type = FAITH_TYPE;
175256590Sshin		}
175377518Sume	} else {
175477518Sume		if (sep->se_type == NORM_TYPE &&
175577518Sume		    strncmp(arg, "faith/", 6) == 0) {
175677518Sume			arg += 6;
175777518Sume			sep->se_type = FAITH_TYPE;
175877518Sume		}
175936042Sguido		sep->se_proto = newstr(arg);
176077518Sume	}
17612657Scsgr        if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
176219237Sjoerg                memmove(sep->se_proto, sep->se_proto + 4,
176319237Sjoerg                    strlen(sep->se_proto) + 1 - 4);
17642657Scsgr                sep->se_rpc = 1;
17652657Scsgr                sep->se_rpc_prog = sep->se_rpc_lowvers =
17662657Scsgr			sep->se_rpc_lowvers = 0;
176756590Sshin		memcpy(&sep->se_ctrladdr4, bind_sa4,
176856590Sshin		       sizeof(sep->se_ctrladdr4));
17692657Scsgr                if ((versp = rindex(sep->se_service, '/'))) {
17702657Scsgr                        *versp++ = '\0';
1771102859Sdwmalone                        switch (sscanf(versp, "%u-%u",
17722657Scsgr                                       &sep->se_rpc_lowvers,
17732657Scsgr                                       &sep->se_rpc_highvers)) {
17742657Scsgr                        case 2:
17752657Scsgr                                break;
17762657Scsgr                        case 1:
17772657Scsgr                                sep->se_rpc_highvers =
17782657Scsgr                                        sep->se_rpc_lowvers;
17792657Scsgr                                break;
17802657Scsgr                        default:
17818857Srgrimes                                syslog(LOG_ERR,
178252219Scharnier					"bad RPC version specifier; %s",
17832657Scsgr					sep->se_service);
17842657Scsgr                                freeconfig(sep);
17852657Scsgr                                goto more;
17862657Scsgr                        }
17872657Scsgr                }
17882657Scsgr                else {
17892657Scsgr                        sep->se_rpc_lowvers =
17902657Scsgr                                sep->se_rpc_highvers = 1;
17912657Scsgr                }
17922657Scsgr        }
179356590Sshin	sep->se_nomapped = 0;
179478356Sdwmalone	if (strcmp(sep->se_proto, "unix") == 0) {
179578356Sdwmalone	        sep->se_family = AF_UNIX;
1796102937Sdwmalone	} else {
1797102937Sdwmalone		while (isdigit(sep->se_proto[strlen(sep->se_proto) - 1])) {
179856590Sshin#ifdef INET6
1799102937Sdwmalone			if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') {
1800102937Sdwmalone				sep->se_proto[strlen(sep->se_proto) - 1] = '\0';
1801102937Sdwmalone				v6bind = 1;
1802102937Sdwmalone				continue;
1803102937Sdwmalone			}
1804102937Sdwmalone#endif
1805102937Sdwmalone			if (sep->se_proto[strlen(sep->se_proto) - 1] == '4') {
1806102937Sdwmalone				sep->se_proto[strlen(sep->se_proto) - 1] = '\0';
1807102937Sdwmalone				v4bind = 1;
1808102937Sdwmalone				continue;
1809102937Sdwmalone			}
1810102937Sdwmalone			/* illegal version num */
1811102937Sdwmalone			syslog(LOG_ERR,	"bad IP version for %s", sep->se_proto);
1812100127Salfred			freeconfig(sep);
1813100127Salfred			goto more;
1814100127Salfred		}
1815102937Sdwmalone#ifdef INET6
1816102938Sdwmalone		if (v6bind && !v6bind_ok) {
1817102937Sdwmalone			syslog(LOG_INFO, "IPv6 bind is ignored for %s",
181856590Sshin			       sep->se_service);
1819102938Sdwmalone			if (v4bind && v4bind_ok)
1820102937Sdwmalone				v6bind = 0;
1821102937Sdwmalone			else {
1822102937Sdwmalone				freeconfig(sep);
1823102937Sdwmalone				goto more;
1824102937Sdwmalone			}
182556590Sshin		}
1826102938Sdwmalone		if (v6bind) {
1827102937Sdwmalone			sep->se_family = AF_INET6;
1828102938Sdwmalone			if (!v4bind || !v4bind_ok)
1829102937Sdwmalone				sep->se_nomapped = 1;
1830102937Sdwmalone		} else
1831102937Sdwmalone#endif
1832102937Sdwmalone		{ /* default to v4 bind if not v6 bind */
1833102938Sdwmalone			if (!v4bind_ok) {
1834102937Sdwmalone				syslog(LOG_NOTICE, "IPv4 bind is ignored for %s",
1835102937Sdwmalone				       sep->se_service);
1836102937Sdwmalone				freeconfig(sep);
1837102937Sdwmalone				goto more;
1838102937Sdwmalone			}
1839102937Sdwmalone			sep->se_family = AF_INET;
1840102937Sdwmalone		}
184156590Sshin	}
184256590Sshin	/* init ctladdr */
184356590Sshin	switch(sep->se_family) {
184456590Sshin	case AF_INET:
184556590Sshin		memcpy(&sep->se_ctrladdr4, bind_sa4,
184656590Sshin		       sizeof(sep->se_ctrladdr4));
184756590Sshin		sep->se_ctrladdr_size =	sizeof(sep->se_ctrladdr4);
184856590Sshin		break;
184956590Sshin#ifdef INET6
185056590Sshin	case AF_INET6:
185156590Sshin		memcpy(&sep->se_ctrladdr6, bind_sa6,
185256590Sshin		       sizeof(sep->se_ctrladdr6));
185356590Sshin		sep->se_ctrladdr_size =	sizeof(sep->se_ctrladdr6);
185456590Sshin		break;
185556590Sshin#endif
185678356Sdwmalone	case AF_UNIX:
185778356Sdwmalone		if (strlen(sep->se_service) >= sizeof(sep->se_ctrladdr_un.sun_path)) {
185878356Sdwmalone			syslog(LOG_ERR,
185978356Sdwmalone			    "domain socket pathname too long for service %s",
186078356Sdwmalone			    sep->se_service);
186178356Sdwmalone			goto more;
186278356Sdwmalone		}
186378356Sdwmalone		memset(&sep->se_ctrladdr, 0, sizeof(sep->se_ctrladdr));
186478356Sdwmalone		sep->se_ctrladdr_un.sun_family = sep->se_family;
186578356Sdwmalone		sep->se_ctrladdr_un.sun_len = strlen(sep->se_service);
186678356Sdwmalone		strcpy(sep->se_ctrladdr_un.sun_path, sep->se_service);
186778356Sdwmalone		sep->se_ctrladdr_size = SUN_LEN(&sep->se_ctrladdr_un);
186856590Sshin	}
18691553Srgrimes	arg = sskip(&cp);
187019618Sjulian	if (!strncmp(arg, "wait", 4))
187119618Sjulian		sep->se_accept = 0;
187219618Sjulian	else if (!strncmp(arg, "nowait", 6))
187319618Sjulian		sep->se_accept = 1;
187419618Sjulian	else {
187519618Sjulian		syslog(LOG_ERR,
187619618Sjulian			"%s: bad wait/nowait for service %s",
187719618Sjulian			CONFIG, sep->se_service);
187819618Sjulian		goto more;
187919618Sjulian	}
188048069Ssheldonh	sep->se_maxchild = -1;
188148069Ssheldonh	sep->se_maxcpm = -1;
1882101474Sume	sep->se_maxperip = -1;
188319618Sjulian	if ((s = strchr(arg, '/')) != NULL) {
188419618Sjulian		char *eptr;
188519618Sjulian		u_long val;
188619618Sjulian
188719618Sjulian		val = strtoul(s + 1, &eptr, 10);
188830847Sdima		if (eptr == s + 1 || val > MAX_MAXCHLD) {
188919618Sjulian			syslog(LOG_ERR,
189019618Sjulian				"%s: bad max-child for service %s",
189119618Sjulian				CONFIG, sep->se_service);
189219618Sjulian			goto more;
189319618Sjulian		}
189448069Ssheldonh		if (debug)
189548069Ssheldonh			if (!sep->se_accept && val != 1)
189648069Ssheldonh				warnx("maxchild=%lu for wait service %s"
189748069Ssheldonh				    " not recommended", val, sep->se_service);
189819618Sjulian		sep->se_maxchild = val;
189930847Sdima		if (*eptr == '/')
190030847Sdima			sep->se_maxcpm = strtol(eptr + 1, &eptr, 10);
1901101474Sume		if (*eptr == '/')
1902101474Sume			sep->se_maxperip = strtol(eptr + 1, &eptr, 10);
190330847Sdima		/*
190430847Sdima		 * explicitly do not check for \0 for future expansion /
190530847Sdima		 * backwards compatibility
190630847Sdima		 */
190719618Sjulian	}
19081553Srgrimes	if (ISMUX(sep)) {
19091553Srgrimes		/*
191019618Sjulian		 * Silently enforce "nowait" mode for TCPMUX services
191119618Sjulian		 * since they don't have an assigned port to listen on.
19121553Srgrimes		 */
191319618Sjulian		sep->se_accept = 1;
19141553Srgrimes		if (strcmp(sep->se_proto, "tcp")) {
19158857Srgrimes			syslog(LOG_ERR,
19161553Srgrimes				"%s: bad protocol for tcpmux service %s",
19171553Srgrimes				CONFIG, sep->se_service);
19181553Srgrimes			goto more;
19191553Srgrimes		}
19201553Srgrimes		if (sep->se_socktype != SOCK_STREAM) {
19218857Srgrimes			syslog(LOG_ERR,
19221553Srgrimes				"%s: bad socket type for tcpmux service %s",
19231553Srgrimes				CONFIG, sep->se_service);
19241553Srgrimes			goto more;
19251553Srgrimes		}
19261553Srgrimes	}
19271553Srgrimes	sep->se_user = newstr(sskip(&cp));
192830792Sache#ifdef LOGIN_CAP
192930792Sache	if ((s = strrchr(sep->se_user, '/')) != NULL) {
193030792Sache		*s = '\0';
193130792Sache		sep->se_class = newstr(s + 1);
193230792Sache	} else
193330792Sache		sep->se_class = newstr(RESOURCE_RC);
193430792Sache#endif
193530807Sache	if ((s = strrchr(sep->se_user, ':')) != NULL) {
193630807Sache		*s = '\0';
193730807Sache		sep->se_group = newstr(s + 1);
193830807Sache	} else
193930807Sache		sep->se_group = NULL;
19401553Srgrimes	sep->se_server = newstr(sskip(&cp));
194145588Smarkm	if ((sep->se_server_name = rindex(sep->se_server, '/')))
194245588Smarkm		sep->se_server_name++;
19431553Srgrimes	if (strcmp(sep->se_server, "internal") == 0) {
19441553Srgrimes		struct biltin *bi;
19451553Srgrimes
19461553Srgrimes		for (bi = biltins; bi->bi_service; bi++)
194749026Sdes			if (bi->bi_socktype == sep->se_socktype &&
194848467Ssheldonh			    matchservent(bi->bi_service, sep->se_service,
194948467Ssheldonh			    sep->se_proto))
19501553Srgrimes				break;
19511553Srgrimes		if (bi->bi_service == 0) {
19521553Srgrimes			syslog(LOG_ERR, "internal service %s unknown",
19531553Srgrimes				sep->se_service);
19541553Srgrimes			goto more;
19551553Srgrimes		}
195619618Sjulian		sep->se_accept = 1;	/* force accept mode for built-ins */
19571553Srgrimes		sep->se_bi = bi;
19581553Srgrimes	} else
19591553Srgrimes		sep->se_bi = NULL;
1960101474Sume	if (sep->se_maxperip < 0)
1961101474Sume		sep->se_maxperip = maxperip;
196248069Ssheldonh	if (sep->se_maxcpm < 0)
196348069Ssheldonh		sep->se_maxcpm = maxcpm;
196445588Smarkm	if (sep->se_maxchild < 0) {	/* apply default max-children */
196548069Ssheldonh		if (sep->se_bi && sep->se_bi->bi_maxchild >= 0)
196619618Sjulian			sep->se_maxchild = sep->se_bi->bi_maxchild;
196748069Ssheldonh		else if (sep->se_accept)
196848069Ssheldonh			sep->se_maxchild = maxchild > 0 ? maxchild : 0;
196919618Sjulian		else
197048069Ssheldonh			sep->se_maxchild = 1;
197145588Smarkm	}
197264197Sdwmalone	if (sep->se_maxchild > 0) {
197319618Sjulian		sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
197419618Sjulian		if (sep->se_pids == NULL) {
197549102Ssheldonh			syslog(LOG_ERR, "malloc: %m");
197619618Sjulian			exit(EX_OSERR);
197719618Sjulian		}
197819618Sjulian	}
19791553Srgrimes	argc = 0;
19801553Srgrimes	for (arg = skip(&cp); cp; arg = skip(&cp))
198119618Sjulian		if (argc < MAXARGV) {
19821553Srgrimes			sep->se_argv[argc++] = newstr(arg);
198319618Sjulian		} else {
198419618Sjulian			syslog(LOG_ERR,
198519618Sjulian				"%s: too many arguments for service %s",
198619618Sjulian				CONFIG, sep->se_service);
198719618Sjulian			goto more;
198819618Sjulian		}
19891553Srgrimes	while (argc <= MAXARGV)
19901553Srgrimes		sep->se_argv[argc++] = NULL;
1991101474Sume	for (i = 0; i < PERIPSIZE; ++i)
1992101474Sume		LIST_INIT(&sep->se_conn[i]);
199356590Sshin#ifdef IPSEC
199456590Sshin	sep->se_policy = policy ? newstr(policy) : NULL;
199556590Sshin#endif
19961553Srgrimes	return (sep);
19971553Srgrimes}
19981553Srgrimes
19991553Srgrimesvoid
200098558Sjmallettfreeconfig(struct servtab *cp)
20011553Srgrimes{
20021553Srgrimes	int i;
20031553Srgrimes
20041553Srgrimes	if (cp->se_service)
20051553Srgrimes		free(cp->se_service);
20061553Srgrimes	if (cp->se_proto)
20071553Srgrimes		free(cp->se_proto);
20081553Srgrimes	if (cp->se_user)
20091553Srgrimes		free(cp->se_user);
201030807Sache	if (cp->se_group)
201130807Sache		free(cp->se_group);
201230792Sache#ifdef LOGIN_CAP
201330792Sache	if (cp->se_class)
201430792Sache		free(cp->se_class);
201530792Sache#endif
20161553Srgrimes	if (cp->se_server)
20171553Srgrimes		free(cp->se_server);
201819618Sjulian	if (cp->se_pids)
201919618Sjulian		free(cp->se_pids);
20201553Srgrimes	for (i = 0; i < MAXARGV; i++)
20211553Srgrimes		if (cp->se_argv[i])
20221553Srgrimes			free(cp->se_argv[i]);
2023101474Sume	free_connlist(cp);
202456590Sshin#ifdef IPSEC
202556590Sshin	if (cp->se_policy)
202656590Sshin		free(cp->se_policy);
202756590Sshin#endif
20281553Srgrimes}
20291553Srgrimes
20301553Srgrimes
20311553Srgrimes/*
20321553Srgrimes * Safe skip - if skip returns null, log a syntax error in the
20331553Srgrimes * configuration file and exit.
20341553Srgrimes */
20351553Srgrimeschar *
203698558Sjmallettsskip(char **cpp)
20371553Srgrimes{
20381553Srgrimes	char *cp;
20391553Srgrimes
20401553Srgrimes	cp = skip(cpp);
20411553Srgrimes	if (cp == NULL) {
20421553Srgrimes		syslog(LOG_ERR, "%s: syntax error", CONFIG);
204319617Sjulian		exit(EX_DATAERR);
20441553Srgrimes	}
20451553Srgrimes	return (cp);
20461553Srgrimes}
20471553Srgrimes
20481553Srgrimeschar *
204998558Sjmallettskip(char **cpp)
20501553Srgrimes{
20511553Srgrimes	char *cp = *cpp;
20521553Srgrimes	char *start;
205311933Sadam	char quote = '\0';
20541553Srgrimes
20551553Srgrimesagain:
20561553Srgrimes	while (*cp == ' ' || *cp == '\t')
20571553Srgrimes		cp++;
20581553Srgrimes	if (*cp == '\0') {
20591553Srgrimes		int c;
20601553Srgrimes
20611553Srgrimes		c = getc(fconfig);
20621553Srgrimes		(void) ungetc(c, fconfig);
20631553Srgrimes		if (c == ' ' || c == '\t')
206419617Sjulian			if ((cp = nextline(fconfig)))
20651553Srgrimes				goto again;
20661553Srgrimes		*cpp = (char *)0;
20671553Srgrimes		return ((char *)0);
20681553Srgrimes	}
206911933Sadam	if (*cp == '"' || *cp == '\'')
207011933Sadam		quote = *cp++;
20711553Srgrimes	start = cp;
207211933Sadam	if (quote)
207311933Sadam		while (*cp && *cp != quote)
207411933Sadam			cp++;
207511933Sadam	else
207611933Sadam		while (*cp && *cp != ' ' && *cp != '\t')
207711933Sadam			cp++;
20781553Srgrimes	if (*cp != '\0')
20791553Srgrimes		*cp++ = '\0';
20801553Srgrimes	*cpp = cp;
20811553Srgrimes	return (start);
20821553Srgrimes}
20831553Srgrimes
20841553Srgrimeschar *
208598558Sjmallettnextline(FILE *fd)
20861553Srgrimes{
20871553Srgrimes	char *cp;
20881553Srgrimes
20891553Srgrimes	if (fgets(line, sizeof (line), fd) == NULL)
20901553Srgrimes		return ((char *)0);
20911553Srgrimes	cp = strchr(line, '\n');
20921553Srgrimes	if (cp)
20931553Srgrimes		*cp = '\0';
20941553Srgrimes	return (line);
20951553Srgrimes}
20961553Srgrimes
20971553Srgrimeschar *
209898558Sjmallettnewstr(const char *cp)
20991553Srgrimes{
210078694Sdwmalone	char *cr;
210178694Sdwmalone
210278694Sdwmalone	if ((cr = strdup(cp != NULL ? cp : "")))
210378694Sdwmalone		return (cr);
21041553Srgrimes	syslog(LOG_ERR, "strdup: %m");
210519617Sjulian	exit(EX_OSERR);
21061553Srgrimes}
21071553Srgrimes
21081553Srgrimesvoid
210998558Sjmallettinetd_setproctitle(const char *a, int s)
21101553Srgrimes{
211171399Sdwmalone	socklen_t size;
211256590Sshin	struct sockaddr_storage ss;
211356590Sshin	char buf[80], pbuf[INET6_ADDRSTRLEN];
21141553Srgrimes
211556590Sshin	size = sizeof(ss);
211656590Sshin	if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) {
211756590Sshin		getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf),
211856590Sshin			    NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID);
211956590Sshin		(void) sprintf(buf, "%s [%s]", a, pbuf);
212056590Sshin	} else
212113142Speter		(void) sprintf(buf, "%s", a);
212213142Speter	setproctitle("%s", buf);
212313142Speter}
212413142Speter
212578694Sdwmaloneint
212698558Sjmallettcheck_loop(const struct sockaddr *sa, const struct servtab *sep)
21275182Swollman{
21285182Swollman	struct servtab *se2;
212956590Sshin	char pname[INET6_ADDRSTRLEN];
21305182Swollman
21315182Swollman	for (se2 = servtab; se2; se2 = se2->se_next) {
21325182Swollman		if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
21335182Swollman			continue;
21345182Swollman
213556590Sshin		switch (se2->se_family) {
213656590Sshin		case AF_INET:
213778694Sdwmalone			if (((const struct sockaddr_in *)sa)->sin_port ==
213856590Sshin			    se2->se_ctrladdr4.sin_port)
213956590Sshin				goto isloop;
214056590Sshin			continue;
214156590Sshin#ifdef INET6
214256590Sshin		case AF_INET6:
214378694Sdwmalone			if (((const struct sockaddr_in *)sa)->sin_port ==
214456590Sshin			    se2->se_ctrladdr4.sin_port)
214556590Sshin				goto isloop;
214656590Sshin			continue;
214756590Sshin#endif
214856590Sshin		default:
214956590Sshin			continue;
21505182Swollman		}
215156590Sshin	isloop:
215256590Sshin		getnameinfo(sa, sa->sa_len, pname, sizeof(pname), NULL, 0,
215356590Sshin			    NI_NUMERICHOST|NI_WITHSCOPEID);
215456590Sshin		syslog(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s",
215556590Sshin		       sep->se_service, sep->se_proto,
215656590Sshin		       se2->se_service, se2->se_proto,
215756590Sshin		       pname);
215856590Sshin		return 1;
21595182Swollman	}
21605182Swollman	return 0;
21615182Swollman}
21625182Swollman
21631553Srgrimes/*
21641553Srgrimes * print_service:
21651553Srgrimes *	Dump relevant information to stderr
21661553Srgrimes */
21671553Srgrimesvoid
216898558Sjmallettprint_service(const char *action, const struct servtab *sep)
21691553Srgrimes{
217019617Sjulian	fprintf(stderr,
217156590Sshin	    "%s: %s proto=%s accept=%d max=%d user=%s group=%s"
217230792Sache#ifdef LOGIN_CAP
217356590Sshin	    "class=%s"
217430792Sache#endif
217556590Sshin	    " builtin=%p server=%s"
217656590Sshin#ifdef IPSEC
217756590Sshin	    " policy=\"%s\""
217856590Sshin#endif
217956590Sshin	    "\n",
218019617Sjulian	    action, sep->se_service, sep->se_proto,
218130807Sache	    sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group,
218230792Sache#ifdef LOGIN_CAP
218330792Sache	    sep->se_class,
218430792Sache#endif
218556590Sshin	    (void *) sep->se_bi, sep->se_server
218656590Sshin#ifdef IPSEC
218756590Sshin	    , (sep->se_policy ? sep->se_policy : "")
218856590Sshin#endif
218956590Sshin	    );
21901553Srgrimes}
21911553Srgrimes
219230847Sdima#define CPMHSIZE	256
219330847Sdima#define CPMHMASK	(CPMHSIZE-1)
219430847Sdima#define CHTGRAN		10
219530847Sdima#define CHTSIZE		6
219630847Sdima
219730847Sdimatypedef struct CTime {
219830847Sdima	unsigned long 	ct_Ticks;
219930847Sdima	int		ct_Count;
220030847Sdima} CTime;
220130847Sdima
220230847Sdimatypedef struct CHash {
220356590Sshin	union {
220456590Sshin		struct in_addr	c4_Addr;
220556590Sshin		struct in6_addr	c6_Addr;
220656590Sshin	} cu_Addr;
220756590Sshin#define	ch_Addr4	cu_Addr.c4_Addr
220856590Sshin#define	ch_Addr6	cu_Addr.c6_Addr
220956590Sshin	int		ch_Family;
221030847Sdima	time_t		ch_LTime;
221130847Sdima	char		*ch_Service;
221230847Sdima	CTime		ch_Times[CHTSIZE];
221330847Sdima} CHash;
221430847Sdima
221530847SdimaCHash	CHashAry[CPMHSIZE];
221630847Sdima
221730847Sdimaint
221898558Sjmallettcpmip(const struct servtab *sep, int ctrl)
221930847Sdima{
222056590Sshin	struct sockaddr_storage rss;
222171399Sdwmalone	socklen_t rssLen = sizeof(rss);
222230847Sdima	int r = 0;
222330847Sdima
222430847Sdima	/*
222530847Sdima	 * If getpeername() fails, just let it through (if logging is
222630847Sdima	 * enabled the condition is caught elsewhere)
222730847Sdima	 */
222830847Sdima
222930847Sdima	if (sep->se_maxcpm > 0 &&
223056590Sshin	    getpeername(ctrl, (struct sockaddr *)&rss, &rssLen) == 0 ) {
223130847Sdima		time_t t = time(NULL);
223230847Sdima		int hv = 0xABC3D20F;
223330847Sdima		int i;
223430847Sdima		int cnt = 0;
223530847Sdima		CHash *chBest = NULL;
223630847Sdima		unsigned int ticks = t / CHTGRAN;
223778694Sdwmalone		struct sockaddr_in *sin4;
223856590Sshin#ifdef INET6
223956590Sshin		struct sockaddr_in6 *sin6;
224056590Sshin#endif
224130847Sdima
224278694Sdwmalone		sin4 = (struct sockaddr_in *)&rss;
224356590Sshin#ifdef INET6
224456590Sshin		sin6 = (struct sockaddr_in6 *)&rss;
224556590Sshin#endif
224630847Sdima		{
224730847Sdima			char *p;
224878694Sdwmalone			int addrlen;
224930847Sdima
225056590Sshin			switch (rss.ss_family) {
225156590Sshin			case AF_INET:
225278694Sdwmalone				p = (char *)&sin4->sin_addr;
225356590Sshin				addrlen = sizeof(struct in_addr);
225456590Sshin				break;
225556590Sshin#ifdef INET6
225656590Sshin			case AF_INET6:
225756590Sshin				p = (char *)&sin6->sin6_addr;
225856590Sshin				addrlen = sizeof(struct in6_addr);
225956590Sshin				break;
226056590Sshin#endif
226156590Sshin			default:
226256590Sshin				/* should not happen */
226356590Sshin				return -1;
226456590Sshin			}
226556590Sshin
226656590Sshin			for (i = 0; i < addrlen; ++i, ++p) {
226730847Sdima				hv = (hv << 5) ^ (hv >> 23) ^ *p;
226830847Sdima			}
226930847Sdima			hv = (hv ^ (hv >> 16));
227030847Sdima		}
227130847Sdima		for (i = 0; i < 5; ++i) {
227230847Sdima			CHash *ch = &CHashAry[(hv + i) & CPMHMASK];
227330847Sdima
227456590Sshin			if (rss.ss_family == AF_INET &&
227556590Sshin			    ch->ch_Family == AF_INET &&
227678694Sdwmalone			    sin4->sin_addr.s_addr == ch->ch_Addr4.s_addr &&
227730847Sdima			    ch->ch_Service && strcmp(sep->se_service,
227830847Sdima			    ch->ch_Service) == 0) {
227930847Sdima				chBest = ch;
228030847Sdima				break;
228130847Sdima			}
228256590Sshin#ifdef INET6
228356590Sshin			if (rss.ss_family == AF_INET6 &&
228456590Sshin			    ch->ch_Family == AF_INET6 &&
228556590Sshin			    IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
228656590Sshin					       &ch->ch_Addr6) != 0 &&
228756590Sshin			    ch->ch_Service && strcmp(sep->se_service,
228856590Sshin			    ch->ch_Service) == 0) {
228956590Sshin				chBest = ch;
229056590Sshin				break;
229156590Sshin			}
229256590Sshin#endif
229330847Sdima			if (chBest == NULL || ch->ch_LTime == 0 ||
229430847Sdima			    ch->ch_LTime < chBest->ch_LTime) {
229530847Sdima				chBest = ch;
229630847Sdima			}
229730847Sdima		}
229856590Sshin		if ((rss.ss_family == AF_INET &&
229956590Sshin		     (chBest->ch_Family != AF_INET ||
230078694Sdwmalone		      sin4->sin_addr.s_addr != chBest->ch_Addr4.s_addr)) ||
230130847Sdima		    chBest->ch_Service == NULL ||
230230847Sdima		    strcmp(sep->se_service, chBest->ch_Service) != 0) {
230378694Sdwmalone			chBest->ch_Family = sin4->sin_family;
230478694Sdwmalone			chBest->ch_Addr4 = sin4->sin_addr;
230530847Sdima			if (chBest->ch_Service)
230630847Sdima				free(chBest->ch_Service);
230730847Sdima			chBest->ch_Service = strdup(sep->se_service);
230830847Sdima			bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
230930847Sdima		}
231056590Sshin#ifdef INET6
231156590Sshin		if ((rss.ss_family == AF_INET6 &&
231256590Sshin		     (chBest->ch_Family != AF_INET6 ||
231356590Sshin		      IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
231456590Sshin					 &chBest->ch_Addr6) == 0)) ||
231556590Sshin		    chBest->ch_Service == NULL ||
231656590Sshin		    strcmp(sep->se_service, chBest->ch_Service) != 0) {
231756590Sshin			chBest->ch_Family = sin6->sin6_family;
231856590Sshin			chBest->ch_Addr6 = sin6->sin6_addr;
231956590Sshin			if (chBest->ch_Service)
232056590Sshin				free(chBest->ch_Service);
232156590Sshin			chBest->ch_Service = strdup(sep->se_service);
232256590Sshin			bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
232356590Sshin		}
232456590Sshin#endif
232530847Sdima		chBest->ch_LTime = t;
232630847Sdima		{
232730847Sdima			CTime *ct = &chBest->ch_Times[ticks % CHTSIZE];
232830847Sdima			if (ct->ct_Ticks != ticks) {
232930847Sdima				ct->ct_Ticks = ticks;
233030847Sdima				ct->ct_Count = 0;
233130847Sdima			}
233230847Sdima			++ct->ct_Count;
233330847Sdima		}
233430847Sdima		for (i = 0; i < CHTSIZE; ++i) {
233530847Sdima			CTime *ct = &chBest->ch_Times[i];
233630847Sdima			if (ct->ct_Ticks <= ticks &&
233730847Sdima			    ct->ct_Ticks >= ticks - CHTSIZE) {
233830847Sdima				cnt += ct->ct_Count;
233930847Sdima			}
234030847Sdima		}
2341117644Sdwmalone		if ((cnt * 60) / (CHTSIZE * CHTGRAN) > sep->se_maxcpm) {
234256590Sshin			char pname[INET6_ADDRSTRLEN];
234356590Sshin
234456590Sshin			getnameinfo((struct sockaddr *)&rss,
234556590Sshin				    ((struct sockaddr *)&rss)->sa_len,
234656590Sshin				    pname, sizeof(pname), NULL, 0,
234756590Sshin				    NI_NUMERICHOST|NI_WITHSCOPEID);
234830847Sdima			r = -1;
234930847Sdima			syslog(LOG_ERR,
235033794Spst			    "%s from %s exceeded counts/min (limit %d/min)",
235156590Sshin			    sep->se_service, pname,
235233794Spst			    sep->se_maxcpm);
235330847Sdima		}
235430847Sdima	}
235530847Sdima	return(r);
235630847Sdima}
2357101474Sume
2358101474Sumestatic struct conninfo *
2359101474Sumesearch_conn(struct servtab *sep, int ctrl)
2360101474Sume{
2361101474Sume	struct sockaddr_storage ss;
2362101474Sume	socklen_t sslen = sizeof(ss);
2363101474Sume	struct conninfo *conn;
2364101474Sume	int hv;
2365101474Sume	char pname[NI_MAXHOST],  pname2[NI_MAXHOST];
2366101474Sume
2367101474Sume	if (sep->se_maxperip <= 0)
2368101474Sume		return NULL;
2369101474Sume
2370101474Sume	/*
2371101474Sume	 * If getpeername() fails, just let it through (if logging is
2372101474Sume	 * enabled the condition is caught elsewhere)
2373101474Sume	 */
2374101474Sume	if (getpeername(ctrl, (struct sockaddr *)&ss, &sslen) != 0)
2375101474Sume		return NULL;
2376101474Sume
2377101474Sume	switch (ss.ss_family) {
2378101474Sume	case AF_INET:
2379101474Sume		hv = hashval((char *)&((struct sockaddr_in *)&ss)->sin_addr,
2380101474Sume		    sizeof(struct in_addr));
2381101474Sume		break;
2382101474Sume#ifdef INET6
2383101474Sume	case AF_INET6:
2384101474Sume		hv = hashval((char *)&((struct sockaddr_in6 *)&ss)->sin6_addr,
2385101474Sume		    sizeof(struct in6_addr));
2386101474Sume		break;
2387101474Sume#endif
2388101474Sume	default:
2389101474Sume		/*
2390101474Sume		 * Since we only support AF_INET and AF_INET6, just
2391101474Sume		 * let other than AF_INET and AF_INET6 through.
2392101474Sume		 */
2393101474Sume		return NULL;
2394101474Sume	}
2395101474Sume
2396101474Sume	if (getnameinfo((struct sockaddr *)&ss, sslen, pname, sizeof(pname),
2397101474Sume	    NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0)
2398101474Sume		return NULL;
2399101474Sume
2400101474Sume	LIST_FOREACH(conn, &sep->se_conn[hv], co_link) {
2401101474Sume		if (getnameinfo((struct sockaddr *)&conn->co_addr,
2402101474Sume		    conn->co_addr.ss_len, pname2, sizeof(pname2), NULL, 0,
2403101474Sume		    NI_NUMERICHOST | NI_WITHSCOPEID) == 0 &&
2404101474Sume		    strcmp(pname, pname2) == 0)
2405101474Sume			break;
2406101474Sume	}
2407101474Sume
2408101474Sume	if (conn == NULL) {
2409101474Sume		if ((conn = malloc(sizeof(struct conninfo))) == NULL) {
2410101474Sume			syslog(LOG_ERR, "malloc: %m");
2411101474Sume			exit(EX_OSERR);
2412101474Sume		}
2413101474Sume		conn->co_proc = malloc(sep->se_maxperip * sizeof(*conn->co_proc));
2414101474Sume		if (conn->co_proc == NULL) {
2415101474Sume			syslog(LOG_ERR, "malloc: %m");
2416101474Sume			exit(EX_OSERR);
2417101474Sume		}
2418101474Sume		memcpy(&conn->co_addr, (struct sockaddr *)&ss, sslen);
2419101474Sume		conn->co_numchild = 0;
2420101474Sume		LIST_INSERT_HEAD(&sep->se_conn[hv], conn, co_link);
2421101474Sume	}
2422101474Sume
2423101474Sume	/*
2424101474Sume	 * Since a child process is not invoked yet, we cannot
2425101474Sume	 * determine a pid of a child.  So, co_proc and co_numchild
2426101474Sume	 * should be filled leter.
2427101474Sume	 */
2428101474Sume
2429101474Sume	return conn;
2430101474Sume}
2431101474Sume
2432101474Sumestatic int
2433101474Sumeroom_conn(struct servtab *sep, struct conninfo *conn)
2434101474Sume{
2435101474Sume	char pname[NI_MAXHOST];
2436101474Sume
2437101474Sume	if (conn->co_numchild >= sep->se_maxperip) {
2438101474Sume		getnameinfo((struct sockaddr *)&conn->co_addr,
2439101474Sume		    conn->co_addr.ss_len, pname, sizeof(pname), NULL, 0,
2440101474Sume		    NI_NUMERICHOST | NI_WITHSCOPEID);
2441101474Sume		syslog(LOG_ERR, "%s from %s exceeded counts (limit %d)",
2442101474Sume		    sep->se_service, pname, sep->se_maxperip);
2443101474Sume		return 0;
2444101474Sume	}
2445101474Sume	return 1;
2446101474Sume}
2447101474Sume
2448101474Sumestatic void
2449101474Sumeaddchild_conn(struct conninfo *conn, pid_t pid)
2450101474Sume{
2451101474Sume	struct procinfo *proc;
2452101474Sume
2453101474Sume	if (conn == NULL)
2454101474Sume		return;
2455101474Sume
2456101474Sume	if ((proc = search_proc(pid, 1)) != NULL) {
2457101474Sume		if (proc->pr_conn != NULL) {
2458101474Sume			syslog(LOG_ERR,
2459101474Sume			    "addchild_conn: child already on process list");
2460101474Sume			exit(EX_OSERR);
2461101474Sume		}
2462101474Sume		proc->pr_conn = conn;
2463101474Sume	}
2464101474Sume
2465101474Sume	conn->co_proc[conn->co_numchild++] = proc;
2466101474Sume}
2467101474Sume
2468101474Sumestatic void
2469101474Sumereapchild_conn(pid_t pid)
2470101474Sume{
2471101474Sume	struct procinfo *proc;
2472101474Sume	struct conninfo *conn;
2473101474Sume	int i;
2474101474Sume
2475101474Sume	if ((proc = search_proc(pid, 0)) == NULL)
2476101474Sume		return;
2477101474Sume	if ((conn = proc->pr_conn) == NULL)
2478101474Sume		return;
2479101474Sume	for (i = 0; i < conn->co_numchild; ++i)
2480101474Sume		if (conn->co_proc[i] == proc) {
2481101474Sume			conn->co_proc[i] = conn->co_proc[--conn->co_numchild];
2482101474Sume			break;
2483101474Sume		}
2484101474Sume	free_proc(proc);
2485101474Sume	free_conn(conn);
2486101474Sume}
2487101474Sume
2488101474Sumestatic void
2489102859Sdwmaloneresize_conn(struct servtab *sep, int maxpip)
2490101474Sume{
2491101474Sume	struct conninfo *conn;
2492101474Sume	int i, j;
2493101474Sume
2494101474Sume	if (sep->se_maxperip <= 0)
2495101474Sume		return;
2496102859Sdwmalone	if (maxpip <= 0) {
2497101474Sume		free_connlist(sep);
2498101474Sume		return;
2499101474Sume	}
2500101474Sume	for (i = 0; i < PERIPSIZE; ++i) {
2501101474Sume		LIST_FOREACH(conn, &sep->se_conn[i], co_link) {
2502102859Sdwmalone			for (j = maxpip; j < conn->co_numchild; ++j)
2503101474Sume				free_proc(conn->co_proc[j]);
2504101474Sume			conn->co_proc = realloc(conn->co_proc,
2505102859Sdwmalone			    maxpip * sizeof(*conn->co_proc));
2506101474Sume			if (conn->co_proc == NULL) {
2507101474Sume				syslog(LOG_ERR, "realloc: %m");
2508101474Sume				exit(EX_OSERR);
2509101474Sume			}
2510102859Sdwmalone			if (conn->co_numchild > maxpip)
2511102859Sdwmalone				conn->co_numchild = maxpip;
2512101474Sume		}
2513101474Sume	}
2514101474Sume}
2515101474Sume
2516101474Sumestatic void
2517101474Sumefree_connlist(struct servtab *sep)
2518101474Sume{
2519101474Sume	struct conninfo *conn;
2520101474Sume	int i, j;
2521101474Sume
2522101474Sume	for (i = 0; i < PERIPSIZE; ++i) {
2523101474Sume		while ((conn = LIST_FIRST(&sep->se_conn[i])) != NULL) {
2524101474Sume			for (j = 0; j < conn->co_numchild; ++j)
2525101474Sume				free_proc(conn->co_proc[j]);
2526101474Sume			conn->co_numchild = 0;
2527101474Sume			free_conn(conn);
2528101474Sume		}
2529101474Sume	}
2530101474Sume}
2531101474Sume
2532101474Sumestatic void
2533101474Sumefree_conn(struct conninfo *conn)
2534101474Sume{
2535101474Sume	if (conn == NULL)
2536101474Sume		return;
2537101474Sume	if (conn->co_numchild <= 0) {
2538101474Sume		LIST_REMOVE(conn, co_link);
2539101474Sume		free(conn->co_proc);
2540101474Sume		free(conn);
2541101474Sume	}
2542101474Sume}
2543101474Sume
2544101474Sumestatic struct procinfo *
2545101474Sumesearch_proc(pid_t pid, int add)
2546101474Sume{
2547101474Sume	struct procinfo *proc;
2548101474Sume	int hv;
2549101474Sume
2550101474Sume	hv = hashval((char *)&pid, sizeof(pid));
2551101474Sume	LIST_FOREACH(proc, &proctable[hv], pr_link) {
2552101474Sume		if (proc->pr_pid == pid)
2553101474Sume			break;
2554101474Sume	}
2555101474Sume	if (proc == NULL && add) {
2556101474Sume		if ((proc = malloc(sizeof(struct procinfo))) == NULL) {
2557101474Sume			syslog(LOG_ERR, "malloc: %m");
2558101474Sume			exit(EX_OSERR);
2559101474Sume		}
2560101474Sume		proc->pr_pid = pid;
2561101474Sume		proc->pr_conn = NULL;
2562101474Sume		LIST_INSERT_HEAD(&proctable[hv], proc, pr_link);
2563101474Sume	}
2564101474Sume	return proc;
2565101474Sume}
2566101474Sume
2567101474Sumestatic void
2568101474Sumefree_proc(struct procinfo *proc)
2569101474Sume{
2570101474Sume	if (proc == NULL)
2571101474Sume		return;
2572101474Sume	LIST_REMOVE(proc, pr_link);
2573101474Sume	free(proc);
2574101474Sume}
2575101474Sume
2576101474Sumestatic int
2577101474Sumehashval(char *p, int len)
2578101474Sume{
2579101474Sume	int i, hv = 0xABC3D20F;
2580101474Sume
2581101474Sume	for (i = 0; i < len; ++i, ++p)
2582101474Sume		hv = (hv << 5) ^ (hv >> 23) ^ *p;
2583101474Sume	hv = (hv ^ (hv >> 16)) & (PERIPSIZE - 1);
2584101474Sume	return hv;
2585101474Sume}
2586