inetd.c revision 58935
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 4429602Scharnierstatic const char rcsid[] = 4550479Speter "$FreeBSD: head/usr.sbin/inetd/inetd.c 58935 2000-04-02 16:11:14Z ume $"; 461553Srgrimes#endif /* not lint */ 471553Srgrimes 481553Srgrimes/* 491553Srgrimes * Inetd - Internet super-server 501553Srgrimes * 511553Srgrimes * This program invokes all internet services as needed. Connection-oriented 521553Srgrimes * services are invoked each time a connection is made, by creating a process. 531553Srgrimes * This process is passed the connection as file descriptor 0 and is expected 541553Srgrimes * to do a getpeername to find out the source host and port. 551553Srgrimes * 561553Srgrimes * Datagram oriented services are invoked when a datagram 571553Srgrimes * arrives; a process is created and passed a pending message 581553Srgrimes * on file descriptor 0. Datagram servers may either connect 591553Srgrimes * to their peer, freeing up the original socket for inetd 601553Srgrimes * to receive further messages on, or ``take over the socket'', 611553Srgrimes * processing all arriving datagrams and, eventually, timing 621553Srgrimes * out. The first type of server is said to be ``multi-threaded''; 638857Srgrimes * the second type of server ``single-threaded''. 641553Srgrimes * 651553Srgrimes * Inetd uses a configuration file which is read at startup 661553Srgrimes * and, possibly, at some later time in response to a hangup signal. 671553Srgrimes * The configuration file is ``free format'' with fields given in the 681553Srgrimes * order shown below. Continuation lines for an entry must being with 691553Srgrimes * a space or tab. All fields must be present in each entry. 701553Srgrimes * 711553Srgrimes * service name must be in /etc/services or must 721553Srgrimes * name a tcpmux service 731553Srgrimes * socket type stream/dgram/raw/rdm/seqpacket 741553Srgrimes * protocol must be in /etc/protocols 751553Srgrimes * wait/nowait single-threaded/multi-threaded 761553Srgrimes * user user to run daemon as 771553Srgrimes * server program full path name 781553Srgrimes * server program arguments maximum of MAXARGS (20) 791553Srgrimes * 801553Srgrimes * TCP services without official port numbers are handled with the 811553Srgrimes * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for 821553Srgrimes * requests. When a connection is made from a foreign host, the service 831553Srgrimes * requested is passed to tcpmux, which looks it up in the servtab list 841553Srgrimes * and returns the proper entry for the service. Tcpmux returns a 851553Srgrimes * negative reply if the service doesn't exist, otherwise the invoked 861553Srgrimes * server is expected to return the positive reply if the service type in 871553Srgrimes * inetd.conf file has the prefix "tcpmux/". If the service type has the 881553Srgrimes * prefix "tcpmux/+", tcpmux will return the positive reply for the 891553Srgrimes * process; this is for compatibility with older server code, and also 901553Srgrimes * allows you to invoke programs that use stdin/stdout without putting any 911553Srgrimes * special server code in them. Services that use tcpmux are "nowait" 921553Srgrimes * because they do not have a well-known port and hence cannot listen 931553Srgrimes * for new requests. 941553Srgrimes * 952657Scsgr * For RPC services 962657Scsgr * service name/version must be in /etc/rpc 972657Scsgr * socket type stream/dgram/raw/rdm/seqpacket 982657Scsgr * protocol must be in /etc/protocols 992657Scsgr * wait/nowait single-threaded/multi-threaded 1002657Scsgr * user user to run daemon as 1012657Scsgr * server program full path name 1022657Scsgr * server program arguments maximum of MAXARGS 1032657Scsgr * 1041553Srgrimes * Comment lines are indicated by a `#' in column 1. 10556590Sshin * 10656590Sshin * #ifdef IPSEC 10756590Sshin * Comment lines that start with "#@" denote IPsec policy string, as described 10856590Sshin * in ipsec_set_policy(3). This will affect all the following items in 10956590Sshin * inetd.conf(8). To reset the policy, just use "#@" line. By default, 11056590Sshin * there's no IPsec policy. 11156590Sshin * #endif 1121553Srgrimes */ 1131553Srgrimes#include <sys/param.h> 1141553Srgrimes#include <sys/ioctl.h> 1151553Srgrimes#include <sys/wait.h> 1161553Srgrimes#include <sys/time.h> 1171553Srgrimes#include <sys/resource.h> 1181553Srgrimes 1191553Srgrimes#include <netinet/in.h> 12036042Sguido#include <netinet/tcp.h> 1211553Srgrimes#include <arpa/inet.h> 1222657Scsgr#include <rpc/rpc.h> 12319617Sjulian#include <rpc/pmap_clnt.h> 1241553Srgrimes 1251553Srgrimes#include <errno.h> 12629602Scharnier#include <err.h> 1271553Srgrimes#include <fcntl.h> 12830807Sache#include <grp.h> 1291553Srgrimes#include <netdb.h> 1301553Srgrimes#include <pwd.h> 1311553Srgrimes#include <signal.h> 1321553Srgrimes#include <stdio.h> 1331553Srgrimes#include <stdlib.h> 1341553Srgrimes#include <string.h> 1351553Srgrimes#include <syslog.h> 13648279Ssheldonh#include <tcpd.h> 1371553Srgrimes#include <unistd.h> 13813142Speter#include <libutil.h> 13919617Sjulian#include <sysexits.h> 14056590Sshin#include <ctype.h> 1411553Srgrimes 14248981Ssheldonh#include "inetd.h" 14348981Ssheldonh#include "pathnames.h" 14448981Ssheldonh 14556590Sshin#ifdef IPSEC 14656590Sshin#include <netinet6/ipsec.h> 14756590Sshin#ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */ 14856590Sshin#undef IPSEC 14956590Sshin#endif 15056590Sshin#endif 15156590Sshin 15256590Sshin/* wrapper for KAME-special getnameinfo() */ 15356590Sshin#ifndef NI_WITHSCOPEID 15456590Sshin#define NI_WITHSCOPEID 0 15556590Sshin#endif 15656590Sshin 15745089Smarkm#ifndef LIBWRAP_ALLOW_FACILITY 15845089Smarkm# define LIBWRAP_ALLOW_FACILITY LOG_AUTH 15945089Smarkm#endif 16045089Smarkm#ifndef LIBWRAP_ALLOW_SEVERITY 16145089Smarkm# define LIBWRAP_ALLOW_SEVERITY LOG_INFO 16245089Smarkm#endif 16345089Smarkm#ifndef LIBWRAP_DENY_FACILITY 16445089Smarkm# define LIBWRAP_DENY_FACILITY LOG_AUTH 16545089Smarkm#endif 16645089Smarkm#ifndef LIBWRAP_DENY_SEVERITY 16745089Smarkm# define LIBWRAP_DENY_SEVERITY LOG_WARNING 16845089Smarkm#endif 16945089Smarkm 17048382Ssheldonh#define ISWRAP(sep) \ 17148697Ssheldonh ( ((wrap_ex && !(sep)->se_bi) || (wrap_bi && (sep)->se_bi)) \ 17248382Ssheldonh && ( ((sep)->se_accept && (sep)->se_socktype == SOCK_STREAM) \ 17348382Ssheldonh || (sep)->se_socktype == SOCK_DGRAM)) 17448382Ssheldonh 17521640Speter#ifdef LOGIN_CAP 17621640Speter#include <login_cap.h> 17730792Sache 17830792Sache/* see init.c */ 17930792Sache#define RESOURCE_RC "daemon" 18030792Sache 18121640Speter#endif 18221640Speter 18333794Spst#ifndef MAXCHILD 18433794Spst#define MAXCHILD -1 /* maximum number of this service 18533794Spst < 0 = no limit */ 18633794Spst#endif 18733794Spst 18833794Spst#ifndef MAXCPM 18933794Spst#define MAXCPM -1 /* rate limit invocations from a 19033794Spst single remote address, 19133794Spst < 0 = no limit */ 19233794Spst#endif 19333794Spst 1942659Scsgr#define TOOMANY 256 /* don't start more than TOOMANY */ 1951553Srgrimes#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ 1961553Srgrimes#define RETRYTIME (60*10) /* retry after bind or server fail */ 19719618Sjulian#define MAX_MAXCHLD 32767 /* max allowable max children */ 1981553Srgrimes 1991553Srgrimes#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) 2001553Srgrimes 20148279Ssheldonhint allow_severity; 20248279Ssheldonhint deny_severity; 20348697Ssheldonhint wrap_ex = 0; 20448279Ssheldonhint wrap_bi = 0; 2051553Srgrimesint debug = 0; 2062659Scsgrint log = 0; 20748988Ssheldonhint maxsock; /* highest-numbered descriptor */ 2081553Srgrimesfd_set allsock; 2091553Srgrimesint options; 2101553Srgrimesint timingout; 2111553Srgrimesint toomany = TOOMANY; 21248069Ssheldonhint maxchild = MAXCHILD; 21348069Ssheldonhint maxcpm = MAXCPM; 2141553Srgrimesstruct servent *sp; 2152657Scsgrstruct rpcent *rpc; 21656590Sshinchar *hostname = NULL; 21756590Sshinstruct sockaddr_in *bind_sa4; 21856590Sshinint no_v4bind = 1; 21956590Sshin#ifdef INET6 22056590Sshinstruct sockaddr_in6 *bind_sa6; 22156590Sshinint no_v6bind = 1; 22256590Sshin#endif 22342122Sdesint signalpipe[2]; 22448991Ssheldonh#ifdef SANITY_CHECK 22548991Ssheldonhint nsock; 22648991Ssheldonh#endif 2271553Srgrimes 22848981Ssheldonhstruct servtab *servtab; 2291553Srgrimes 23048981Ssheldonhextern struct biltin biltins[]; 2311553Srgrimes 2321553Srgrimes#define NUMINT (sizeof(intab) / sizeof(struct inent)) 2331553Srgrimeschar *CONFIG = _PATH_INETDCONF; 23417482Sjulianchar *pid_file = _PATH_INETDPID; 23513142Speter 23613142Speter#ifdef OLD_SETPROCTITLE 2371553Srgrimeschar **Argv; 2381553Srgrimeschar *LastArg; 23913142Speter#endif 2401553Srgrimes 2411553Srgrimesint 24233794Spstgetvalue(arg, value, whine) 24333794Spst char *arg, *whine; 24433794Spst int *value; 24533794Spst{ 24633794Spst int tmp; 24733794Spst char *p; 24833794Spst 24933794Spst tmp = strtol(arg, &p, 0); 25033794Spst if (tmp < 1 || *p) { 25133794Spst syslog(LOG_ERR, whine, arg); 25233794Spst return 1; /* failure */ 25333794Spst } 25433794Spst *value = tmp; 25533794Spst return 0; /* success */ 25633794Spst} 25733794Spst 25833794Spstint 2591553Srgrimesmain(argc, argv, envp) 2601553Srgrimes int argc; 2611553Srgrimes char *argv[], *envp[]; 2621553Srgrimes{ 2631553Srgrimes struct servtab *sep; 2641553Srgrimes struct passwd *pwd; 26530807Sache struct group *grp; 26648962Ssheldonh struct sigaction sa, saalrm, sachld, sahup, sapipe; 2671553Srgrimes int tmpint, ch, dofork; 2681553Srgrimes pid_t pid; 2691553Srgrimes char buf[50]; 27021640Speter#ifdef LOGIN_CAP 27121640Speter login_cap_t *lc = NULL; 27221640Speter#endif 27345089Smarkm struct request_info req; 27445089Smarkm int denied; 27545089Smarkm char *service = NULL; 27648382Ssheldonh char *pnm; 27756590Sshin union { 27856590Sshin struct sockaddr peer_un; 27956590Sshin struct sockaddr_in peer_un4; 28056590Sshin struct sockaddr_in6 peer_un6; 28156590Sshin struct sockaddr_storage peer_max; 28256590Sshin } p_un; 28356590Sshin#define peer p_un.peer_un 28456590Sshin#define peer4 p_un.peer_un4 28556590Sshin#define peer6 p_un.peer_un6 28656590Sshin#define peermax p_un.peer_max 28747972Ssheldonh int i; 28856590Sshin struct addrinfo hints, *res; 28956590Sshin char *servname; 29056590Sshin int error; 2911553Srgrimes 29213142Speter 29313142Speter#ifdef OLD_SETPROCTITLE 2941553Srgrimes Argv = argv; 2951553Srgrimes if (envp == 0 || *envp == 0) 2961553Srgrimes envp = argv; 2971553Srgrimes while (*envp) 2981553Srgrimes envp++; 2991553Srgrimes LastArg = envp[-1] + strlen(envp[-1]); 30013142Speter#endif 3011553Srgrimes 3021553Srgrimes openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); 3031553Srgrimes 30448697Ssheldonh while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:")) != -1) 3051553Srgrimes switch(ch) { 3061553Srgrimes case 'd': 3071553Srgrimes debug = 1; 3081553Srgrimes options |= SO_DEBUG; 3091553Srgrimes break; 3102659Scsgr case 'l': 3112659Scsgr log = 1; 3122659Scsgr break; 31333794Spst case 'R': 31433794Spst getvalue(optarg, &toomany, 31533794Spst "-R %s: bad value for service invocation rate"); 3161553Srgrimes break; 31733794Spst case 'c': 31833794Spst getvalue(optarg, &maxchild, 31933794Spst "-c %s: bad value for maximum children"); 32033794Spst break; 32133794Spst case 'C': 32233794Spst getvalue(optarg, &maxcpm, 32333794Spst "-C %s: bad value for maximum children/minute"); 32433794Spst break; 32517482Sjulian case 'a': 32656590Sshin hostname = optarg; 32717482Sjulian break; 32817482Sjulian case 'p': 32917482Sjulian pid_file = optarg; 33017482Sjulian break; 33148279Ssheldonh case 'w': 33248697Ssheldonh wrap_ex++; 33348279Ssheldonh break; 33448697Ssheldonh case 'W': 33548697Ssheldonh wrap_bi++; 33648697Ssheldonh break; 3371553Srgrimes case '?': 3381553Srgrimes default: 3391553Srgrimes syslog(LOG_ERR, 34048697Ssheldonh "usage: inetd [-dlwW] [-a address] [-R rate]" 34133794Spst " [-c maximum] [-C rate]" 34217482Sjulian " [-p pidfile] [conf-file]"); 34319617Sjulian exit(EX_USAGE); 3441553Srgrimes } 34556590Sshin /* 34656590Sshin * Initialize Bind Addrs. 34756590Sshin * When hostname is NULL, wild card bind addrs are obtained from 34856590Sshin * getaddrinfo(). But getaddrinfo() requires at least one of 34956590Sshin * hostname or servname is non NULL. 35056590Sshin * So when hostname is NULL, set dummy value to servname. 35156590Sshin */ 35256590Sshin servname = (hostname == NULL) ? "discard" /* dummy */ : NULL; 35356590Sshin 35456590Sshin bzero(&hints, sizeof(struct addrinfo)); 35556590Sshin hints.ai_flags = AI_PASSIVE; 35656590Sshin hints.ai_family = AF_UNSPEC; 35756590Sshin error = getaddrinfo(hostname, servname, &hints, &res); 35856590Sshin if (error != 0) { 35956590Sshin syslog(LOG_ERR, "-a %s: %s", hostname, gai_strerror(error)); 36056590Sshin if (error == EAI_SYSTEM) 36156590Sshin syslog(LOG_ERR, "%s", strerror(errno)); 36256590Sshin exit(EX_USAGE); 36356590Sshin } 36456590Sshin do { 36556590Sshin if (res->ai_addr == NULL) { 36656590Sshin syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname); 36756590Sshin exit(EX_USAGE); 36856590Sshin } 36956590Sshin switch (res->ai_addr->sa_family) { 37056590Sshin case AF_INET: 37156590Sshin if (no_v4bind == 0) 37256590Sshin continue; 37356590Sshin bind_sa4 = (struct sockaddr_in *)res->ai_addr; 37456590Sshin /* init port num in case servname is dummy */ 37556590Sshin bind_sa4->sin_port = 0; 37656590Sshin no_v4bind = 0; 37756590Sshin continue; 37856590Sshin#ifdef INET6 37956590Sshin case AF_INET6: 38056590Sshin if (no_v6bind == 0) 38156590Sshin continue; 38256590Sshin bind_sa6 = (struct sockaddr_in6 *)res->ai_addr; 38356590Sshin /* init port num in case servname is dummy */ 38456590Sshin bind_sa6->sin6_port = 0; 38556590Sshin no_v6bind = 0; 38656590Sshin continue; 38756590Sshin#endif 38856590Sshin } 38956590Sshin if (no_v4bind == 0 39056590Sshin#ifdef INET6 39156590Sshin && no_v6bind == 0 39256590Sshin#endif 39356590Sshin ) 39456590Sshin break; 39556590Sshin } while ((res = res->ai_next) != NULL); 39656590Sshin if (no_v4bind != 0 39756590Sshin#ifdef INET6 39856590Sshin && no_v6bind != 0 39956590Sshin#endif 40056590Sshin ) { 40156590Sshin syslog(LOG_ERR, "-a %s: unknown address family", hostname); 40256590Sshin exit(EX_USAGE); 40356590Sshin } 40456590Sshin 4051553Srgrimes argc -= optind; 4061553Srgrimes argv += optind; 4071553Srgrimes 4081553Srgrimes if (argc > 0) 4091553Srgrimes CONFIG = argv[0]; 4101553Srgrimes if (debug == 0) { 41111447Swollman FILE *fp; 41212024Speter if (daemon(0, 0) < 0) { 41312024Speter syslog(LOG_WARNING, "daemon(0,0) failed: %m"); 41412024Speter } 41512024Speter /* 41612024Speter * In case somebody has started inetd manually, we need to 41712024Speter * clear the logname, so that old servers run as root do not 41812024Speter * get the user's logname.. 41912024Speter */ 42012024Speter if (setlogin("") < 0) { 42112024Speter syslog(LOG_WARNING, "cannot clear logname: %m"); 42212024Speter /* no big deal if it fails.. */ 42312024Speter } 42411447Swollman pid = getpid(); 42517482Sjulian fp = fopen(pid_file, "w"); 42611447Swollman if (fp) { 42711447Swollman fprintf(fp, "%ld\n", (long)pid); 42811447Swollman fclose(fp); 42911447Swollman } else { 43017482Sjulian syslog(LOG_WARNING, "%s: %m", pid_file); 43111447Swollman } 4321553Srgrimes } 43335948Sbde sa.sa_flags = 0; 43435948Sbde sigemptyset(&sa.sa_mask); 43535948Sbde sigaddset(&sa.sa_mask, SIGALRM); 43635948Sbde sigaddset(&sa.sa_mask, SIGCHLD); 43735948Sbde sigaddset(&sa.sa_mask, SIGHUP); 43842122Sdes sa.sa_handler = flag_retry; 43948962Ssheldonh sigaction(SIGALRM, &sa, &saalrm); 44042122Sdes config(); 44142122Sdes sa.sa_handler = flag_config; 44248962Ssheldonh sigaction(SIGHUP, &sa, &sahup); 44342122Sdes sa.sa_handler = flag_reapchild; 44448962Ssheldonh sigaction(SIGCHLD, &sa, &sachld); 44535848Sguido sa.sa_handler = SIG_IGN; 44635948Sbde sigaction(SIGPIPE, &sa, &sapipe); 4471553Srgrimes 4481553Srgrimes { 4491553Srgrimes /* space for daemons to overwrite environment for ps */ 4501553Srgrimes#define DUMMYSIZE 100 4511553Srgrimes char dummy[DUMMYSIZE]; 4521553Srgrimes 45319298Salex (void)memset(dummy, 'x', DUMMYSIZE - 1); 4541553Srgrimes dummy[DUMMYSIZE - 1] = '\0'; 4551553Srgrimes (void)setenv("inetd_dummy", dummy, 1); 4561553Srgrimes } 4571553Srgrimes 45842250Sdes if (pipe(signalpipe) != 0) { 45952219Scharnier syslog(LOG_ERR, "pipe: %m"); 46042250Sdes exit(EX_OSERR); 46142122Sdes } 46242122Sdes FD_SET(signalpipe[0], &allsock); 46348991Ssheldonh#ifdef SANITY_CHECK 46447015Sdes nsock++; 46548991Ssheldonh#endif 46648989Ssheldonh if (signalpipe[0] > maxsock) 46748989Ssheldonh maxsock = signalpipe[0]; 46848989Ssheldonh if (signalpipe[1] > maxsock) 46948989Ssheldonh maxsock = signalpipe[1]; 47041685Sdillon 4711553Srgrimes for (;;) { 4721553Srgrimes int n, ctrl; 4731553Srgrimes fd_set readable; 4741553Srgrimes 47548991Ssheldonh#ifdef SANITY_CHECK 4761553Srgrimes if (nsock == 0) { 47747015Sdes syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 47847015Sdes exit(EX_SOFTWARE); 4791553Srgrimes } 48048991Ssheldonh#endif 4811553Srgrimes readable = allsock; 48242122Sdes if ((n = select(maxsock + 1, &readable, (fd_set *)0, 48342122Sdes (fd_set *)0, (struct timeval *)0)) <= 0) { 48442122Sdes if (n < 0 && errno != EINTR) { 4851553Srgrimes syslog(LOG_WARNING, "select: %m"); 48628907Simp sleep(1); 48728907Simp } 4881553Srgrimes continue; 4891553Srgrimes } 49042122Sdes /* handle any queued signal flags */ 49142250Sdes if (FD_ISSET(signalpipe[0], &readable)) { 49242122Sdes int n; 49342250Sdes if (ioctl(signalpipe[0], FIONREAD, &n) != 0) { 49442122Sdes syslog(LOG_ERR, "ioctl: %m"); 49542122Sdes exit(EX_OSERR); 49642122Sdes } 49742250Sdes while (--n >= 0) { 49842250Sdes char c; 49942250Sdes if (read(signalpipe[0], &c, 1) != 1) { 50042250Sdes syslog(LOG_ERR, "read: %m"); 50142250Sdes exit(EX_OSERR); 50242250Sdes } 50342250Sdes if (debug) 50456482Scharnier warnx("handling signal flag %c", c); 50542250Sdes switch(c) { 50642250Sdes case 'A': /* sigalrm */ 50742250Sdes retry(); 50842250Sdes break; 50942250Sdes case 'C': /* sigchld */ 51042250Sdes reapchild(); 51142250Sdes break; 51242250Sdes case 'H': /* sighup */ 51342250Sdes config(); 51442250Sdes break; 51542250Sdes } 51642250Sdes } 51742122Sdes } 5181553Srgrimes for (sep = servtab; n && sep; sep = sep->se_next) 5191553Srgrimes if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { 5201553Srgrimes n--; 5211553Srgrimes if (debug) 52229602Scharnier warnx("someone wants %s", sep->se_service); 52319618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) { 52453256Speter i = 1; 52553256Speter if (ioctl(sep->se_fd, FIONBIO, &i) < 0) 52653256Speter syslog(LOG_ERR, "ioctl (FIONBIO, 1): %m"); 5271553Srgrimes ctrl = accept(sep->se_fd, (struct sockaddr *)0, 5281553Srgrimes (int *)0); 5291553Srgrimes if (debug) 53029602Scharnier warnx("accept, ctrl %d", ctrl); 5311553Srgrimes if (ctrl < 0) { 5321553Srgrimes if (errno != EINTR) 5331553Srgrimes syslog(LOG_WARNING, 5341553Srgrimes "accept (for %s): %m", 53537844Sphk sep->se_service); 53637816Sphk if (sep->se_accept && 53737816Sphk sep->se_socktype == SOCK_STREAM) 53837816Sphk close(ctrl); 5391553Srgrimes continue; 5401553Srgrimes } 54153256Speter i = 0; 54253256Speter if (ioctl(sep->se_fd, FIONBIO, &i) < 0) 54353256Speter syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m"); 54453256Speter if (ioctl(ctrl, FIONBIO, &i) < 0) 54553256Speter syslog(LOG_ERR, "ioctl2(FIONBIO, 0): %m"); 54630847Sdima if (cpmip(sep, ctrl) < 0) { 54730847Sdima close(ctrl); 54830847Sdima continue; 54930847Sdima } 5501553Srgrimes } else 5511553Srgrimes ctrl = sep->se_fd; 55248382Ssheldonh if (log && !ISWRAP(sep)) { 55356590Sshin char pname[INET6_ADDRSTRLEN]; 55448382Ssheldonh pnm = "unknown"; 55556590Sshin i = sizeof peermax; 55648382Ssheldonh if (getpeername(ctrl, (struct sockaddr *) 55756590Sshin &peermax, &i)) { 55856590Sshin i = sizeof peermax; 55948382Ssheldonh if (recvfrom(ctrl, buf, sizeof(buf), 56048382Ssheldonh MSG_PEEK, 56156590Sshin (struct sockaddr *)&peermax, 56256590Sshin &i) >= 0) { 56356590Sshin getnameinfo((struct sockaddr *)&peermax, 56457383Sshin peer.sa_len, 56556590Sshin pname, sizeof(pname), 56656590Sshin NULL, 0, 56756590Sshin NI_NUMERICHOST| 56856590Sshin NI_WITHSCOPEID); 56956590Sshin pnm = pname; 57056590Sshin } 57156590Sshin } else { 57256590Sshin getnameinfo((struct sockaddr *)&peermax, 57357383Sshin peer.sa_len, 57456590Sshin pname, sizeof(pname), 57556590Sshin NULL, 0, 57656590Sshin NI_NUMERICHOST| 57756590Sshin NI_WITHSCOPEID); 57856590Sshin pnm = pname; 57948382Ssheldonh } 58048382Ssheldonh syslog(LOG_INFO,"%s from %s", sep->se_service, pnm); 58148382Ssheldonh } 58242122Sdes (void) sigblock(SIGBLOCK); 5831553Srgrimes pid = 0; 58447972Ssheldonh /* 58548958Ssheldonh * Fork for all external services, builtins which need to 58648958Ssheldonh * fork and anything we're wrapping (as wrapping might 58748958Ssheldonh * block or use hosts_options(5) twist). 58847972Ssheldonh */ 58948382Ssheldonh dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep); 5901553Srgrimes if (dofork) { 5911553Srgrimes if (sep->se_count++ == 0) 59237856Sache (void)gettimeofday(&sep->se_time, (struct timezone *)NULL); 5931553Srgrimes else if (sep->se_count >= toomany) { 5941553Srgrimes struct timeval now; 5951553Srgrimes 59637856Sache (void)gettimeofday(&now, (struct timezone *)NULL); 5971553Srgrimes if (now.tv_sec - sep->se_time.tv_sec > 5981553Srgrimes CNT_INTVL) { 5991553Srgrimes sep->se_time = now; 6001553Srgrimes sep->se_count = 1; 6011553Srgrimes } else { 6021553Srgrimes syslog(LOG_ERR, 6031553Srgrimes "%s/%s server failing (looping), service terminated", 6041553Srgrimes sep->se_service, sep->se_proto); 6051553Srgrimes close_sep(sep); 60642122Sdes sigsetmask(0L); 6071553Srgrimes if (!timingout) { 6081553Srgrimes timingout = 1; 6091553Srgrimes alarm(RETRYTIME); 6101553Srgrimes } 6111553Srgrimes continue; 6121553Srgrimes } 6131553Srgrimes } 6141553Srgrimes pid = fork(); 6151553Srgrimes } 6161553Srgrimes if (pid < 0) { 6171553Srgrimes syslog(LOG_ERR, "fork: %m"); 61819618Sjulian if (sep->se_accept && 6191553Srgrimes sep->se_socktype == SOCK_STREAM) 6201553Srgrimes close(ctrl); 62142122Sdes sigsetmask(0L); 6221553Srgrimes sleep(1); 6231553Srgrimes continue; 6241553Srgrimes } 62519618Sjulian if (pid) 62619618Sjulian addchild(sep, pid); 62742122Sdes sigsetmask(0L); 6281553Srgrimes if (pid == 0) { 6291553Srgrimes if (dofork) { 6301553Srgrimes if (debug) 63129602Scharnier warnx("+ closing from %d", maxsock); 6321553Srgrimes for (tmpint = maxsock; tmpint > 2; tmpint--) 6331553Srgrimes if (tmpint != ctrl) 63419617Sjulian (void) close(tmpint); 63548962Ssheldonh sigaction(SIGALRM, &saalrm, (struct sigaction *)0); 63648962Ssheldonh sigaction(SIGCHLD, &sachld, (struct sigaction *)0); 63748962Ssheldonh sigaction(SIGHUP, &sahup, (struct sigaction *)0); 63848962Ssheldonh /* SIGPIPE reset before exec */ 6391553Srgrimes } 64035829Sguido /* 64135829Sguido * Call tcpmux to find the real service to exec. 64235829Sguido */ 64335829Sguido if (sep->se_bi && 64435829Sguido sep->se_bi->bi_fn == (void (*)()) tcpmux) { 64535829Sguido sep = tcpmux(ctrl); 64635829Sguido if (sep == NULL) { 64735829Sguido close(ctrl); 64835829Sguido _exit(0); 64935829Sguido } 65035829Sguido } 65148382Ssheldonh if (ISWRAP(sep)) { 65248698Ssheldonh inetd_setproctitle("wrapping", ctrl); 65347972Ssheldonh service = sep->se_server_name ? 65447972Ssheldonh sep->se_server_name : sep->se_service; 65547972Ssheldonh request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, NULL); 65645089Smarkm fromhost(&req); 65747972Ssheldonh deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY; 65847972Ssheldonh allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY; 65945089Smarkm denied = !hosts_access(&req); 66045089Smarkm if (denied) { 66145089Smarkm syslog(deny_severity, 66245089Smarkm "refused connection from %.500s, service %s (%s)", 66345089Smarkm eval_client(&req), service, sep->se_proto); 66448382Ssheldonh if (sep->se_socktype != SOCK_STREAM) 66548382Ssheldonh recv(ctrl, buf, sizeof (buf), 0); 66648382Ssheldonh if (dofork) 66748382Ssheldonh _exit(0); 66845089Smarkm } 66945089Smarkm if (log) { 67045089Smarkm syslog(allow_severity, 67145089Smarkm "connection from %.500s, service %s (%s)", 67245089Smarkm eval_client(&req), service, sep->se_proto); 67345089Smarkm } 67445089Smarkm } 67519617Sjulian if (sep->se_bi) { 6761553Srgrimes (*sep->se_bi->bi_fn)(ctrl, sep); 67719617Sjulian } else { 6781553Srgrimes if (debug) 67929602Scharnier warnx("%d execl %s", 68029602Scharnier getpid(), sep->se_server); 6811553Srgrimes dup2(ctrl, 0); 6821553Srgrimes close(ctrl); 6831553Srgrimes dup2(0, 1); 6841553Srgrimes dup2(0, 2); 6851553Srgrimes if ((pwd = getpwnam(sep->se_user)) == NULL) { 6861553Srgrimes syslog(LOG_ERR, 68756482Scharnier "%s/%s: %s: no such user", 6881553Srgrimes sep->se_service, sep->se_proto, 6891553Srgrimes sep->se_user); 6901553Srgrimes if (sep->se_socktype != SOCK_STREAM) 6911553Srgrimes recv(0, buf, sizeof (buf), 0); 69219617Sjulian _exit(EX_NOUSER); 6931553Srgrimes } 69430807Sache grp = NULL; 69530807Sache if ( sep->se_group != NULL 69630807Sache && (grp = getgrnam(sep->se_group)) == NULL 69730807Sache ) { 69830807Sache syslog(LOG_ERR, 69956482Scharnier "%s/%s: %s: no such group", 70030807Sache sep->se_service, sep->se_proto, 70130807Sache sep->se_group); 70230807Sache if (sep->se_socktype != SOCK_STREAM) 70330807Sache recv(0, buf, sizeof (buf), 0); 70430807Sache _exit(EX_NOUSER); 70530807Sache } 70630807Sache if (grp != NULL) 70730807Sache pwd->pw_gid = grp->gr_gid; 70821640Speter#ifdef LOGIN_CAP 70930792Sache if ((lc = login_getclass(sep->se_class)) == NULL) { 71030792Sache /* error syslogged by getclass */ 71130792Sache syslog(LOG_ERR, 71230792Sache "%s/%s: %s: login class error", 71337850Sache sep->se_service, sep->se_proto, 71437850Sache sep->se_class); 71530792Sache if (sep->se_socktype != SOCK_STREAM) 71630792Sache recv(0, buf, sizeof (buf), 0); 71730792Sache _exit(EX_NOUSER); 71830792Sache } 71921640Speter#endif 72012024Speter if (setsid() < 0) { 72112024Speter syslog(LOG_ERR, 72212024Speter "%s: can't setsid(): %m", 72312024Speter sep->se_service); 72419617Sjulian /* _exit(EX_OSERR); not fatal yet */ 72512024Speter } 72621640Speter#ifdef LOGIN_CAP 72721640Speter if (setusercontext(lc, pwd, pwd->pw_uid, 72821640Speter LOGIN_SETALL) != 0) { 72921640Speter syslog(LOG_ERR, 73021640Speter "%s: can't setusercontext(..%s..): %m", 73121640Speter sep->se_service, sep->se_user); 73221640Speter _exit(EX_OSERR); 73321640Speter } 73421640Speter#else 7351553Srgrimes if (pwd->pw_uid) { 73612024Speter if (setlogin(sep->se_user) < 0) { 73712024Speter syslog(LOG_ERR, 73812024Speter "%s: can't setlogin(%s): %m", 73912024Speter sep->se_service, sep->se_user); 74019617Sjulian /* _exit(EX_OSERR); not yet */ 74112024Speter } 7421553Srgrimes if (setgid(pwd->pw_gid) < 0) { 7431553Srgrimes syslog(LOG_ERR, 7448857Srgrimes "%s: can't set gid %d: %m", 7451553Srgrimes sep->se_service, pwd->pw_gid); 74619617Sjulian _exit(EX_OSERR); 7471553Srgrimes } 7481553Srgrimes (void) initgroups(pwd->pw_name, 7491553Srgrimes pwd->pw_gid); 7501553Srgrimes if (setuid(pwd->pw_uid) < 0) { 7511553Srgrimes syslog(LOG_ERR, 7528857Srgrimes "%s: can't set uid %d: %m", 7531553Srgrimes sep->se_service, pwd->pw_uid); 75419617Sjulian _exit(EX_OSERR); 7551553Srgrimes } 7561553Srgrimes } 75721640Speter#endif 75835948Sbde sigaction(SIGPIPE, &sapipe, 75935948Sbde (struct sigaction *)0); 7601553Srgrimes execv(sep->se_server, sep->se_argv); 76145089Smarkm syslog(LOG_ERR, 76245089Smarkm "cannot execute %s: %m", sep->se_server); 7631553Srgrimes if (sep->se_socktype != SOCK_STREAM) 7641553Srgrimes recv(0, buf, sizeof (buf), 0); 7651553Srgrimes } 76647972Ssheldonh if (dofork) 76747972Ssheldonh _exit(0); 7681553Srgrimes } 76919618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) 7701553Srgrimes close(ctrl); 7711553Srgrimes } 7721553Srgrimes } 7731553Srgrimes} 7741553Srgrimes 77519618Sjulian/* 77642122Sdes * Add a signal flag to the signal flag queue for later handling 77742122Sdes */ 77842122Sdes 77942122Sdesvoid flag_signal(c) 78042122Sdes char c; 78142122Sdes{ 78242250Sdes if (write(signalpipe[1], &c, 1) != 1) { 78342250Sdes syslog(LOG_ERR, "write: %m"); 78448985Ssheldonh _exit(EX_OSERR); 78542250Sdes } 78642122Sdes} 78742122Sdes 78842122Sdes/* 78919618Sjulian * Record a new child pid for this service. If we've reached the 79019618Sjulian * limit on children, then stop accepting incoming requests. 79119618Sjulian */ 79219618Sjulian 7931553Srgrimesvoid 79419618Sjulianaddchild(struct servtab *sep, pid_t pid) 79519618Sjulian{ 79619618Sjulian#ifdef SANITY_CHECK 79719618Sjulian if (sep->se_numchild >= sep->se_maxchild) { 79819618Sjulian syslog(LOG_ERR, "%s: %d >= %d", 79919618Sjulian __FUNCTION__, sep->se_numchild, sep->se_maxchild); 80019618Sjulian exit(EX_SOFTWARE); 80119618Sjulian } 80219618Sjulian#endif 80319618Sjulian if (sep->se_maxchild == 0) 80419618Sjulian return; 80519618Sjulian sep->se_pids[sep->se_numchild++] = pid; 80619618Sjulian if (sep->se_numchild == sep->se_maxchild) 80719618Sjulian disable(sep); 80819618Sjulian} 80919618Sjulian 81019618Sjulian/* 81119618Sjulian * Some child process has exited. See if it's on somebody's list. 81219618Sjulian */ 81319618Sjulian 81419618Sjulianvoid 81542122Sdesflag_reapchild(signo) 8161553Srgrimes int signo; 8171553Srgrimes{ 81842250Sdes flag_signal('C'); 81942122Sdes} 82042122Sdes 82142122Sdesvoid 82242122Sdesreapchild() 82342122Sdes{ 82419618Sjulian int k, status; 8251553Srgrimes pid_t pid; 8261553Srgrimes struct servtab *sep; 8271553Srgrimes 8281553Srgrimes for (;;) { 8291553Srgrimes pid = wait3(&status, WNOHANG, (struct rusage *)0); 8301553Srgrimes if (pid <= 0) 8311553Srgrimes break; 8321553Srgrimes if (debug) 83329602Scharnier warnx("%d reaped, status %#x", pid, status); 83419618Sjulian for (sep = servtab; sep; sep = sep->se_next) { 83519618Sjulian for (k = 0; k < sep->se_numchild; k++) 83619618Sjulian if (sep->se_pids[k] == pid) 83719618Sjulian break; 83819618Sjulian if (k == sep->se_numchild) 83919618Sjulian continue; 84019618Sjulian if (sep->se_numchild == sep->se_maxchild) 84119618Sjulian enable(sep); 84219618Sjulian sep->se_pids[k] = sep->se_pids[--sep->se_numchild]; 84319618Sjulian if (status) 84419618Sjulian syslog(LOG_WARNING, 84519618Sjulian "%s[%d]: exit status 0x%x", 84619618Sjulian sep->se_server, pid, status); 84719618Sjulian break; 84819618Sjulian } 8491553Srgrimes } 8501553Srgrimes} 8511553Srgrimes 8521553Srgrimesvoid 85342122Sdesflag_config(signo) 8541553Srgrimes int signo; 8551553Srgrimes{ 85642250Sdes flag_signal('H'); 85742122Sdes} 85842122Sdes 85942122Sdesvoid config() 86042122Sdes{ 86119618Sjulian struct servtab *sep, *new, **sepp; 86242122Sdes long omask; 8631553Srgrimes 8641553Srgrimes if (!setconfig()) { 8651553Srgrimes syslog(LOG_ERR, "%s: %m", CONFIG); 8661553Srgrimes return; 8671553Srgrimes } 8681553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 8691553Srgrimes sep->se_checked = 0; 87019618Sjulian while ((new = getconfigent())) { 87130807Sache if (getpwnam(new->se_user) == NULL) { 8721553Srgrimes syslog(LOG_ERR, 87356482Scharnier "%s/%s: no such user '%s', service ignored", 87419618Sjulian new->se_service, new->se_proto, new->se_user); 8751553Srgrimes continue; 8761553Srgrimes } 87730807Sache if (new->se_group && getgrnam(new->se_group) == NULL) { 87830807Sache syslog(LOG_ERR, 87956482Scharnier "%s/%s: no such group '%s', service ignored", 88030807Sache new->se_service, new->se_proto, new->se_group); 88130807Sache continue; 88230807Sache } 88330792Sache#ifdef LOGIN_CAP 88430792Sache if (login_getclass(new->se_class) == NULL) { 88530792Sache /* error syslogged by getclass */ 88630792Sache syslog(LOG_ERR, 88737850Sache "%s/%s: %s: login class error, service ignored", 88837850Sache new->se_service, new->se_proto, new->se_class); 88930792Sache continue; 89030792Sache } 89130792Sache#endif 8921553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 89319618Sjulian if (strcmp(sep->se_service, new->se_service) == 0 && 89456590Sshin strcmp(sep->se_proto, new->se_proto) == 0 && 89556590Sshin sep->se_family == new->se_family) 8961553Srgrimes break; 8971553Srgrimes if (sep != 0) { 8981553Srgrimes int i; 8991553Srgrimes 90019618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; } 90142122Sdes omask = sigblock(SIGBLOCK); 90256590Sshin if (sep->se_nomapped != new->se_nomapped) { 90356590Sshin sep->se_nomapped = new->se_nomapped; 90456590Sshin sep->se_reset = 1; 90556590Sshin } 90619618Sjulian /* copy over outstanding child pids */ 90719618Sjulian if (sep->se_maxchild && new->se_maxchild) { 90819618Sjulian new->se_numchild = sep->se_numchild; 90919618Sjulian if (new->se_numchild > new->se_maxchild) 91019618Sjulian new->se_numchild = new->se_maxchild; 91119618Sjulian memcpy(new->se_pids, sep->se_pids, 91219618Sjulian new->se_numchild * sizeof(*new->se_pids)); 91319618Sjulian } 91419618Sjulian SWAP(sep->se_pids, new->se_pids); 91519618Sjulian sep->se_maxchild = new->se_maxchild; 91619618Sjulian sep->se_numchild = new->se_numchild; 91730847Sdima sep->se_maxcpm = new->se_maxcpm; 91819618Sjulian /* might need to turn on or off service now */ 91919618Sjulian if (sep->se_fd >= 0) { 92019618Sjulian if (sep->se_maxchild 92119618Sjulian && sep->se_numchild == sep->se_maxchild) { 92219618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 92319618Sjulian disable(sep); 92419618Sjulian } else { 92519618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) 92619618Sjulian enable(sep); 92719618Sjulian } 92819618Sjulian } 92919618Sjulian sep->se_accept = new->se_accept; 93030807Sache SWAP(sep->se_user, new->se_user); 93130807Sache SWAP(sep->se_group, new->se_group); 93230792Sache#ifdef LOGIN_CAP 93330807Sache SWAP(sep->se_class, new->se_class); 93430792Sache#endif 93530807Sache SWAP(sep->se_server, new->se_server); 93647972Ssheldonh SWAP(sep->se_server_name, new->se_server_name); 9371553Srgrimes for (i = 0; i < MAXARGV; i++) 93819618Sjulian SWAP(sep->se_argv[i], new->se_argv[i]); 93956590Sshin#ifdef IPSEC 94056590Sshin SWAP(sep->se_policy, new->se_policy); 94156590Sshin ipsecsetup(sep); 94256590Sshin#endif 94342122Sdes sigsetmask(omask); 94419618Sjulian freeconfig(new); 9451553Srgrimes if (debug) 9461553Srgrimes print_service("REDO", sep); 9471553Srgrimes } else { 94819618Sjulian sep = enter(new); 9491553Srgrimes if (debug) 9501553Srgrimes print_service("ADD ", sep); 9511553Srgrimes } 9521553Srgrimes sep->se_checked = 1; 9531553Srgrimes if (ISMUX(sep)) { 9541553Srgrimes sep->se_fd = -1; 9551553Srgrimes continue; 9561553Srgrimes } 95756590Sshin switch (sep->se_family) { 95856590Sshin case AF_INET: 95956590Sshin if (no_v4bind != 0) { 96056590Sshin sep->se_fd = -1; 96156590Sshin continue; 96256590Sshin } 96356590Sshin break; 96456590Sshin#ifdef INET6 96556590Sshin case AF_INET6: 96656590Sshin if (no_v6bind != 0) { 96756590Sshin sep->se_fd = -1; 96856590Sshin continue; 96956590Sshin } 97056590Sshin break; 97156590Sshin#endif 97256590Sshin } 9732657Scsgr if (!sep->se_rpc) { 9742657Scsgr sp = getservbyname(sep->se_service, sep->se_proto); 9752657Scsgr if (sp == 0) { 9762657Scsgr syslog(LOG_ERR, "%s/%s: unknown service", 97756590Sshin sep->se_service, sep->se_proto); 9782657Scsgr sep->se_checked = 0; 9792657Scsgr continue; 9802657Scsgr } 98156590Sshin switch (sep->se_family) { 98256590Sshin case AF_INET: 98356590Sshin if (sep->se_ctladdrinitok == 0) { 98456590Sshin memcpy(&sep->se_ctrladdr4, bind_sa4, 98556590Sshin sizeof(sep->se_ctrladdr4)); 98656590Sshin sep->se_ctrladdr_size = 98756590Sshin sizeof(sep->se_ctrladdr4); 98856590Sshin } 98956590Sshin if (sp->s_port != sep->se_ctrladdr4.sin_port) { 99056590Sshin sep->se_ctrladdr4.sin_port = 99156590Sshin sp->s_port; 99256590Sshin sep->se_reset = 1; 99356590Sshin } 99456590Sshin break; 99556590Sshin#ifdef INET6 99656590Sshin case AF_INET6: 99756590Sshin if (sep->se_ctladdrinitok == 0) { 99856590Sshin memcpy(&sep->se_ctrladdr6, bind_sa6, 99956590Sshin sizeof(sep->se_ctrladdr6)); 100056590Sshin sep->se_ctrladdr_size = 100156590Sshin sizeof(sep->se_ctrladdr6); 100256590Sshin } 100356590Sshin if (sp->s_port != 100456590Sshin sep->se_ctrladdr6.sin6_port) { 100556590Sshin sep->se_ctrladdr6.sin6_port = 100656590Sshin sp->s_port; 100756590Sshin sep->se_reset = 1; 100856590Sshin } 100956590Sshin break; 101056590Sshin#endif 10112657Scsgr } 101256590Sshin if (sep->se_reset != 0 && sep->se_fd >= 0) 101356590Sshin close_sep(sep); 10142657Scsgr } else { 10152657Scsgr rpc = getrpcbyname(sep->se_service); 10162657Scsgr if (rpc == 0) { 101752219Scharnier syslog(LOG_ERR, "%s/%s unknown RPC service", 10182657Scsgr sep->se_service, sep->se_proto); 10192657Scsgr if (sep->se_fd != -1) 10202657Scsgr (void) close(sep->se_fd); 10212657Scsgr sep->se_fd = -1; 10222657Scsgr continue; 10232657Scsgr } 10242657Scsgr if (rpc->r_number != sep->se_rpc_prog) { 10252657Scsgr if (sep->se_rpc_prog) 10262657Scsgr unregisterrpc(sep); 10272657Scsgr sep->se_rpc_prog = rpc->r_number; 10282657Scsgr if (sep->se_fd != -1) 10292657Scsgr (void) close(sep->se_fd); 10302657Scsgr sep->se_fd = -1; 10312657Scsgr } 10321553Srgrimes } 10331553Srgrimes if (sep->se_fd == -1) 10341553Srgrimes setup(sep); 10351553Srgrimes } 10361553Srgrimes endconfig(); 10371553Srgrimes /* 10381553Srgrimes * Purge anything not looked at above. 10391553Srgrimes */ 104042122Sdes omask = sigblock(SIGBLOCK); 10411553Srgrimes sepp = &servtab; 104219617Sjulian while ((sep = *sepp)) { 10431553Srgrimes if (sep->se_checked) { 10441553Srgrimes sepp = &sep->se_next; 10451553Srgrimes continue; 10461553Srgrimes } 10471553Srgrimes *sepp = sep->se_next; 10481553Srgrimes if (sep->se_fd >= 0) 10491553Srgrimes close_sep(sep); 10501553Srgrimes if (debug) 10511553Srgrimes print_service("FREE", sep); 10522657Scsgr if (sep->se_rpc && sep->se_rpc_prog > 0) 10532657Scsgr unregisterrpc(sep); 10541553Srgrimes freeconfig(sep); 10551553Srgrimes free((char *)sep); 10561553Srgrimes } 105742122Sdes (void) sigsetmask(omask); 10581553Srgrimes} 10591553Srgrimes 10601553Srgrimesvoid 10612657Scsgrunregisterrpc(sep) 10622657Scsgr struct servtab *sep; 10632657Scsgr{ 10642657Scsgr int i; 10652657Scsgr struct servtab *sepp; 106642122Sdes long omask; 10672657Scsgr 106842122Sdes omask = sigblock(SIGBLOCK); 10692657Scsgr for (sepp = servtab; sepp; sepp = sepp->se_next) { 10702657Scsgr if (sepp == sep) 10712657Scsgr continue; 10722657Scsgr if (sep->se_checked == 0 || 10732657Scsgr !sepp->se_rpc || 10742657Scsgr sep->se_rpc_prog != sepp->se_rpc_prog) 10752657Scsgr continue; 10762657Scsgr return; 10772657Scsgr } 10782657Scsgr if (debug) 10792657Scsgr print_service("UNREG", sep); 10802657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) 10812657Scsgr pmap_unset(sep->se_rpc_prog, i); 10822657Scsgr if (sep->se_fd != -1) 10832657Scsgr (void) close(sep->se_fd); 10842657Scsgr sep->se_fd = -1; 108542122Sdes (void) sigsetmask(omask); 10862657Scsgr} 10872657Scsgr 10882657Scsgrvoid 108942122Sdesflag_retry(signo) 10901553Srgrimes int signo; 10911553Srgrimes{ 109242250Sdes flag_signal('A'); 109342122Sdes} 109442122Sdes 109542122Sdesvoid 109642122Sdesretry() 109742122Sdes{ 10981553Srgrimes struct servtab *sep; 10991553Srgrimes 11001553Srgrimes timingout = 0; 11011553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 110219617Sjulian if (sep->se_fd == -1 && !ISMUX(sep)) 11031553Srgrimes setup(sep); 11041553Srgrimes} 11051553Srgrimes 11061553Srgrimesvoid 11071553Srgrimessetup(sep) 11081553Srgrimes struct servtab *sep; 11091553Srgrimes{ 11101553Srgrimes int on = 1; 11111553Srgrimes 111256590Sshin if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { 11131553Srgrimes if (debug) 111429602Scharnier warn("socket failed on %s/%s", 111529602Scharnier sep->se_service, sep->se_proto); 11161553Srgrimes syslog(LOG_ERR, "%s/%s: socket: %m", 11171553Srgrimes sep->se_service, sep->se_proto); 11181553Srgrimes return; 11191553Srgrimes } 11201553Srgrimes#define turnon(fd, opt) \ 11211553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) 11221553Srgrimes if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && 11231553Srgrimes turnon(sep->se_fd, SO_DEBUG) < 0) 11241553Srgrimes syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 11251553Srgrimes if (turnon(sep->se_fd, SO_REUSEADDR) < 0) 11261553Srgrimes syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); 112725253Swollman#ifdef SO_PRIVSTATE 112813956Swollman if (turnon(sep->se_fd, SO_PRIVSTATE) < 0) 112913956Swollman syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m"); 113025253Swollman#endif 113156590Sshin /* tftpd opens a new connection then needs more infos */ 113256590Sshin if ((sep->se_family == AF_INET6) && 113356590Sshin (strcmp(sep->se_proto, "udp") == 0) && 113456590Sshin (sep->se_accept == 0) && 113556590Sshin (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_PKTINFO, 113656590Sshin (char *)&on, sizeof (on)) < 0)) 113756590Sshin syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m"); 113856590Sshin#ifdef IPV6_BINDV6ONLY 113958935Sume if (sep->se_family == AF_INET6) { 114058935Sume int flag = sep->se_nomapped ? 1 : 0; 114158935Sume if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_BINDV6ONLY, 114258935Sume (char *)&flag, sizeof (flag)) < 0) 114358935Sume syslog(LOG_ERR, "setsockopt (IPV6_BINDV6ONLY): %m"); 114458935Sume } 114556590Sshin#endif /* IPV6_BINDV6ONLY */ 11461553Srgrimes#undef turnon 114736042Sguido if (sep->se_type == TTCP_TYPE) 114836042Sguido if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH, 114936042Sguido (char *)&on, sizeof (on)) < 0) 115036042Sguido syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m"); 115156590Sshin#ifdef IPV6_FAITH 115256590Sshin if (sep->se_type == FAITH_TYPE) { 115356590Sshin if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH, &on, 115456590Sshin sizeof(on)) < 0) { 115556590Sshin syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m"); 115656590Sshin } 115756590Sshin } 115856590Sshin#endif 115956590Sshin#ifdef IPSEC 116056590Sshin ipsecsetup(sep); 116156590Sshin#endif 11621553Srgrimes if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, 116356590Sshin sep->se_ctrladdr_size) < 0) { 11641553Srgrimes if (debug) 116529602Scharnier warn("bind failed on %s/%s", 116629602Scharnier sep->se_service, sep->se_proto); 11671553Srgrimes syslog(LOG_ERR, "%s/%s: bind: %m", 11681553Srgrimes sep->se_service, sep->se_proto); 11691553Srgrimes (void) close(sep->se_fd); 11701553Srgrimes sep->se_fd = -1; 11711553Srgrimes if (!timingout) { 11721553Srgrimes timingout = 1; 11731553Srgrimes alarm(RETRYTIME); 11741553Srgrimes } 11751553Srgrimes return; 11761553Srgrimes } 11772657Scsgr if (sep->se_rpc) { 117856590Sshin int i, len = sep->se_ctrladdr_size; 11792657Scsgr 118056590Sshin if (sep->se_family != AF_INET) { 118156590Sshin syslog(LOG_ERR, 118256590Sshin "%s/%s: unsupported address family for rpc", 118356590Sshin sep->se_service, sep->se_proto); 118456590Sshin (void) close(sep->se_fd); 118556590Sshin sep->se_fd = -1; 118656590Sshin return; 118756590Sshin } 11888857Srgrimes if (getsockname(sep->se_fd, 11892657Scsgr (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){ 11902657Scsgr syslog(LOG_ERR, "%s/%s: getsockname: %m", 11912657Scsgr sep->se_service, sep->se_proto); 11922657Scsgr (void) close(sep->se_fd); 11932657Scsgr sep->se_fd = -1; 11948857Srgrimes return; 11952657Scsgr } 11962657Scsgr if (debug) 11972657Scsgr print_service("REG ", sep); 11982657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { 11992657Scsgr pmap_unset(sep->se_rpc_prog, i); 12002657Scsgr pmap_set(sep->se_rpc_prog, i, 12012657Scsgr (sep->se_socktype == SOCK_DGRAM) 12022657Scsgr ? IPPROTO_UDP : IPPROTO_TCP, 120356590Sshin ntohs(sep->se_ctrladdr4.sin_port)); 12042657Scsgr } 12052657Scsgr } 12061553Srgrimes if (sep->se_socktype == SOCK_STREAM) 120717197Sdg listen(sep->se_fd, 64); 120819618Sjulian enable(sep); 12091553Srgrimes if (debug) { 121029602Scharnier warnx("registered %s on %d", 12111553Srgrimes sep->se_server, sep->se_fd); 12121553Srgrimes } 12131553Srgrimes} 12141553Srgrimes 121556590Sshin#ifdef IPSEC 121656590Sshinvoid 121756590Sshinipsecsetup(sep) 121856590Sshin struct servtab *sep; 121956590Sshin{ 122056590Sshin char *buf; 122156590Sshin char *policy_in = NULL; 122256590Sshin char *policy_out = NULL; 122356590Sshin int level; 122456590Sshin int opt; 122556590Sshin 122656590Sshin switch (sep->se_family) { 122756590Sshin case AF_INET: 122856590Sshin level = IPPROTO_IP; 122956590Sshin opt = IP_IPSEC_POLICY; 123056590Sshin break; 123156590Sshin#ifdef INET6 123256590Sshin case AF_INET6: 123356590Sshin level = IPPROTO_IPV6; 123456590Sshin opt = IPV6_IPSEC_POLICY; 123556590Sshin break; 123656590Sshin#endif 123756590Sshin default: 123856590Sshin return; 123956590Sshin } 124056590Sshin 124156590Sshin if (!sep->se_policy || sep->se_policy[0] == '\0') { 124256590Sshin policy_in = "in entrust"; 124356590Sshin policy_out = "out entrust"; 124456590Sshin } else { 124556590Sshin if (!strncmp("in", sep->se_policy, 2)) 124656590Sshin policy_in = sep->se_policy; 124756590Sshin else if (!strncmp("out", sep->se_policy, 3)) 124856590Sshin policy_out = sep->se_policy; 124956590Sshin else { 125056590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 125156590Sshin sep->se_policy); 125256590Sshin return; 125356590Sshin } 125456590Sshin } 125556590Sshin 125656590Sshin if (policy_in != NULL) { 125756590Sshin buf = ipsec_set_policy(policy_in, strlen(policy_in)); 125856590Sshin if (buf != NULL) { 125956590Sshin if (setsockopt(sep->se_fd, level, opt, 126056675Sshin buf, ipsec_get_policylen(buf)) < 0 && 126156759Sshin debug != 0) 126256759Sshin warnx("%s/%s: ipsec initialization failed; %s", 126356759Sshin sep->se_service, sep->se_proto, 126456759Sshin policy_in); 126556590Sshin free(buf); 126656590Sshin } else 126756590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 126856590Sshin policy_in); 126956590Sshin } 127056590Sshin if (policy_out != NULL) { 127156590Sshin buf = ipsec_set_policy(policy_out, strlen(policy_out)); 127256590Sshin if (buf != NULL) { 127356590Sshin if (setsockopt(sep->se_fd, level, opt, 127456675Sshin buf, ipsec_get_policylen(buf)) < 0 && 127556759Sshin debug != 0) 127656759Sshin warnx("%s/%s: ipsec initialization failed; %s", 127756759Sshin sep->se_service, sep->se_proto, 127856759Sshin policy_out); 127956590Sshin free(buf); 128056590Sshin } else 128156590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 128256590Sshin policy_out); 128356590Sshin } 128456590Sshin} 128556590Sshin#endif 128656590Sshin 12871553Srgrimes/* 12881553Srgrimes * Finish with a service and its socket. 12891553Srgrimes */ 12901553Srgrimesvoid 12911553Srgrimesclose_sep(sep) 12921553Srgrimes struct servtab *sep; 12931553Srgrimes{ 12941553Srgrimes if (sep->se_fd >= 0) { 129519618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 129619618Sjulian disable(sep); 12971553Srgrimes (void) close(sep->se_fd); 12981553Srgrimes sep->se_fd = -1; 12991553Srgrimes } 13001553Srgrimes sep->se_count = 0; 130119618Sjulian sep->se_numchild = 0; /* forget about any existing children */ 13021553Srgrimes} 13031553Srgrimes 130448467Ssheldonhint 130548467Ssheldonhmatchservent(name1, name2, proto) 130648467Ssheldonh char *name1, *name2, *proto; 130748467Ssheldonh{ 130848467Ssheldonh char **alias; 130948467Ssheldonh struct servent *se; 131048467Ssheldonh 131149026Sdes if (strcmp(name1, name2) == 0) 131249026Sdes return(1); 131348467Ssheldonh if ((se = getservbyname(name1, proto)) != NULL) { 131448467Ssheldonh if (strcmp(name2, se->s_name) == 0) 131548467Ssheldonh return(1); 131648467Ssheldonh for (alias = se->s_aliases; *alias; alias++) 131748467Ssheldonh if (strcmp(name2, *alias) == 0) 131848467Ssheldonh return(1); 131948467Ssheldonh } 132048467Ssheldonh return(0); 132148467Ssheldonh} 132248467Ssheldonh 13231553Srgrimesstruct servtab * 13241553Srgrimesenter(cp) 13251553Srgrimes struct servtab *cp; 13261553Srgrimes{ 13271553Srgrimes struct servtab *sep; 132842122Sdes long omask; 13291553Srgrimes 13301553Srgrimes sep = (struct servtab *)malloc(sizeof (*sep)); 13311553Srgrimes if (sep == (struct servtab *)0) { 133249102Ssheldonh syslog(LOG_ERR, "malloc: %m"); 133319617Sjulian exit(EX_OSERR); 13341553Srgrimes } 13351553Srgrimes *sep = *cp; 13361553Srgrimes sep->se_fd = -1; 133742122Sdes omask = sigblock(SIGBLOCK); 13381553Srgrimes sep->se_next = servtab; 13391553Srgrimes servtab = sep; 134042122Sdes sigsetmask(omask); 13411553Srgrimes return (sep); 13421553Srgrimes} 13431553Srgrimes 134419618Sjulianvoid 134519618Sjulianenable(struct servtab *sep) 134619618Sjulian{ 134719618Sjulian if (debug) 134829602Scharnier warnx( 134919618Sjulian "enabling %s, fd %d", sep->se_service, sep->se_fd); 135019618Sjulian#ifdef SANITY_CHECK 135119618Sjulian if (sep->se_fd < 0) { 135219618Sjulian syslog(LOG_ERR, 135319618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 135419618Sjulian exit(EX_SOFTWARE); 135519618Sjulian } 135619618Sjulian if (ISMUX(sep)) { 135719618Sjulian syslog(LOG_ERR, 135819618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 135919618Sjulian exit(EX_SOFTWARE); 136019618Sjulian } 136119618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) { 136219618Sjulian syslog(LOG_ERR, 136319618Sjulian "%s: %s: not off", __FUNCTION__, sep->se_service); 136419618Sjulian exit(EX_SOFTWARE); 136519618Sjulian } 136648991Ssheldonh nsock++; 136719618Sjulian#endif 136819618Sjulian FD_SET(sep->se_fd, &allsock); 136919618Sjulian if (sep->se_fd > maxsock) 137019618Sjulian maxsock = sep->se_fd; 137119618Sjulian} 137219618Sjulian 137319618Sjulianvoid 137419618Sjuliandisable(struct servtab *sep) 137519618Sjulian{ 137619618Sjulian if (debug) 137729602Scharnier warnx( 137819618Sjulian "disabling %s, fd %d", sep->se_service, sep->se_fd); 137919618Sjulian#ifdef SANITY_CHECK 138019618Sjulian if (sep->se_fd < 0) { 138119618Sjulian syslog(LOG_ERR, 138219618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 138319618Sjulian exit(EX_SOFTWARE); 138419618Sjulian } 138519618Sjulian if (ISMUX(sep)) { 138619618Sjulian syslog(LOG_ERR, 138719618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 138819618Sjulian exit(EX_SOFTWARE); 138919618Sjulian } 139019618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) { 139119618Sjulian syslog(LOG_ERR, 139219618Sjulian "%s: %s: not on", __FUNCTION__, sep->se_service); 139319618Sjulian exit(EX_SOFTWARE); 139419618Sjulian } 139519618Sjulian if (nsock == 0) { 139619618Sjulian syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 139719618Sjulian exit(EX_SOFTWARE); 139819618Sjulian } 139948991Ssheldonh nsock--; 140019618Sjulian#endif 140119618Sjulian FD_CLR(sep->se_fd, &allsock); 140219618Sjulian if (sep->se_fd == maxsock) 140319618Sjulian maxsock--; 140419618Sjulian} 140519618Sjulian 14061553SrgrimesFILE *fconfig = NULL; 14071553Srgrimesstruct servtab serv; 14081553Srgrimeschar line[LINE_MAX]; 14091553Srgrimes 14101553Srgrimesint 14111553Srgrimessetconfig() 14121553Srgrimes{ 14131553Srgrimes 14141553Srgrimes if (fconfig != NULL) { 14151553Srgrimes fseek(fconfig, 0L, SEEK_SET); 14161553Srgrimes return (1); 14171553Srgrimes } 14181553Srgrimes fconfig = fopen(CONFIG, "r"); 14191553Srgrimes return (fconfig != NULL); 14201553Srgrimes} 14211553Srgrimes 14221553Srgrimesvoid 14231553Srgrimesendconfig() 14241553Srgrimes{ 14251553Srgrimes if (fconfig) { 14261553Srgrimes (void) fclose(fconfig); 14271553Srgrimes fconfig = NULL; 14281553Srgrimes } 14291553Srgrimes} 14301553Srgrimes 14311553Srgrimesstruct servtab * 14321553Srgrimesgetconfigent() 14331553Srgrimes{ 14341553Srgrimes struct servtab *sep = &serv; 14351553Srgrimes int argc; 143619618Sjulian char *cp, *arg, *s; 14372657Scsgr char *versp; 14381553Srgrimes static char TCPMUX_TOKEN[] = "tcpmux/"; 14391553Srgrimes#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1) 144056590Sshin#ifdef IPSEC 144156590Sshin char *policy = NULL; 144256590Sshin#endif 144356590Sshin int v4bind = 0; 144456590Sshin#ifdef INET6 144556590Sshin int v6bind = 0; 144656590Sshin#endif 14471553Srgrimes 14481553Srgrimesmore: 144956590Sshin while ((cp = nextline(fconfig)) != NULL) { 145056590Sshin#ifdef IPSEC 145156590Sshin /* lines starting with #@ is not a comment, but the policy */ 145256590Sshin if (cp[0] == '#' && cp[1] == '@') { 145356590Sshin char *p; 145456590Sshin for (p = cp + 2; p && *p && isspace(*p); p++) 145556590Sshin ; 145656590Sshin if (*p == '\0') { 145756590Sshin if (policy) 145856590Sshin free(policy); 145956590Sshin policy = NULL; 146056590Sshin } else if (ipsec_get_policylen(p) >= 0) { 146156590Sshin if (policy) 146256590Sshin free(policy); 146356590Sshin policy = newstr(p); 146456590Sshin } else { 146556590Sshin syslog(LOG_ERR, 146656590Sshin "%s: invalid ipsec policy \"%s\"", 146756590Sshin CONFIG, p); 146856590Sshin exit(EX_CONFIG); 146956590Sshin } 147056590Sshin } 147156590Sshin#endif 147256590Sshin if (*cp == '#' || *cp == '\0') 147356590Sshin continue; 147456590Sshin break; 147556590Sshin } 14761553Srgrimes if (cp == NULL) 14771553Srgrimes return ((struct servtab *)0); 14781553Srgrimes /* 14791553Srgrimes * clear the static buffer, since some fields (se_ctrladdr, 14801553Srgrimes * for example) don't get initialized here. 14811553Srgrimes */ 14821553Srgrimes memset((caddr_t)sep, 0, sizeof *sep); 14831553Srgrimes arg = skip(&cp); 14841553Srgrimes if (cp == NULL) { 14851553Srgrimes /* got an empty line containing just blanks/tabs. */ 14861553Srgrimes goto more; 14871553Srgrimes } 14881553Srgrimes if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { 14891553Srgrimes char *c = arg + MUX_LEN; 14901553Srgrimes if (*c == '+') { 14911553Srgrimes sep->se_type = MUXPLUS_TYPE; 14921553Srgrimes c++; 14931553Srgrimes } else 14941553Srgrimes sep->se_type = MUX_TYPE; 14951553Srgrimes sep->se_service = newstr(c); 14961553Srgrimes } else { 14971553Srgrimes sep->se_service = newstr(arg); 14981553Srgrimes sep->se_type = NORM_TYPE; 14991553Srgrimes } 15001553Srgrimes arg = sskip(&cp); 15011553Srgrimes if (strcmp(arg, "stream") == 0) 15021553Srgrimes sep->se_socktype = SOCK_STREAM; 15031553Srgrimes else if (strcmp(arg, "dgram") == 0) 15041553Srgrimes sep->se_socktype = SOCK_DGRAM; 15051553Srgrimes else if (strcmp(arg, "rdm") == 0) 15061553Srgrimes sep->se_socktype = SOCK_RDM; 15071553Srgrimes else if (strcmp(arg, "seqpacket") == 0) 15081553Srgrimes sep->se_socktype = SOCK_SEQPACKET; 15091553Srgrimes else if (strcmp(arg, "raw") == 0) 15101553Srgrimes sep->se_socktype = SOCK_RAW; 15111553Srgrimes else 15121553Srgrimes sep->se_socktype = -1; 151336042Sguido 151436042Sguido arg = sskip(&cp); 151556590Sshin if (strncmp(arg, "tcp", 3) == 0) { 151656590Sshin sep->se_proto = newstr(strsep(&arg, "/")); 151756590Sshin if (arg != NULL) { 151856590Sshin if (strcmp(arg, "ttcp") == 0) 151956590Sshin sep->se_type = TTCP_TYPE; 152056590Sshin else if (strcmp(arg, "faith") == 0) 152156590Sshin sep->se_type = FAITH_TYPE; 152256590Sshin } 152356590Sshin } else 152436042Sguido sep->se_proto = newstr(arg); 15252657Scsgr if (strncmp(sep->se_proto, "rpc/", 4) == 0) { 152656973Sshin if (no_v4bind != 0) { 152756973Sshin syslog(LOG_INFO, "IPv4 bind is ignored for %s", 152856973Sshin sep->se_service); 152956590Sshin freeconfig(sep); 153056590Sshin goto more; 153156590Sshin } 153219237Sjoerg memmove(sep->se_proto, sep->se_proto + 4, 153319237Sjoerg strlen(sep->se_proto) + 1 - 4); 15342657Scsgr sep->se_rpc = 1; 15352657Scsgr sep->se_rpc_prog = sep->se_rpc_lowvers = 15362657Scsgr sep->se_rpc_lowvers = 0; 153756590Sshin memcpy(&sep->se_ctrladdr4, bind_sa4, 153856590Sshin sizeof(sep->se_ctrladdr4)); 15392657Scsgr if ((versp = rindex(sep->se_service, '/'))) { 15402657Scsgr *versp++ = '\0'; 15412657Scsgr switch (sscanf(versp, "%d-%d", 15422657Scsgr &sep->se_rpc_lowvers, 15432657Scsgr &sep->se_rpc_highvers)) { 15442657Scsgr case 2: 15452657Scsgr break; 15462657Scsgr case 1: 15472657Scsgr sep->se_rpc_highvers = 15482657Scsgr sep->se_rpc_lowvers; 15492657Scsgr break; 15502657Scsgr default: 15518857Srgrimes syslog(LOG_ERR, 155252219Scharnier "bad RPC version specifier; %s", 15532657Scsgr sep->se_service); 15542657Scsgr freeconfig(sep); 15552657Scsgr goto more; 15562657Scsgr } 15572657Scsgr } 15582657Scsgr else { 15592657Scsgr sep->se_rpc_lowvers = 15602657Scsgr sep->se_rpc_highvers = 1; 15612657Scsgr } 15622657Scsgr } 156356590Sshin sep->se_nomapped = 0; 156456590Sshin while (isdigit(sep->se_proto[strlen(sep->se_proto) - 1])) { 156556590Sshin#ifdef INET6 156656590Sshin if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') { 156756590Sshin if (no_v6bind != 0) { 156856590Sshin syslog(LOG_INFO, "IPv6 bind is ignored for %s", 156956590Sshin sep->se_service); 157056590Sshin freeconfig(sep); 157156590Sshin goto more; 157256590Sshin } 157356590Sshin sep->se_proto[strlen(sep->se_proto) - 1] = '\0'; 157456590Sshin v6bind = 1; 157556590Sshin continue; 157656590Sshin } 157756590Sshin#endif 157856590Sshin if (sep->se_proto[strlen(sep->se_proto) - 1] == '4') { 157956590Sshin sep->se_proto[strlen(sep->se_proto) - 1] = '\0'; 158056590Sshin v4bind = 1; 158156590Sshin continue; 158256590Sshin } 158356590Sshin /* illegal version num */ 158456590Sshin syslog(LOG_ERR, "bad IP version for %s", sep->se_proto); 158556590Sshin freeconfig(sep); 158656590Sshin goto more; 158756590Sshin } 158856590Sshin#ifdef INET6 158956590Sshin if (v6bind != 0) { 159056590Sshin sep->se_family = AF_INET6; 159156590Sshin if (v4bind == 0 || no_v4bind != 0) 159256590Sshin sep->se_nomapped = 1; 159357906Sshin } else 159456590Sshin#endif 159557906Sshin { /* default to v4 bind if not v6 bind */ 159656590Sshin if (no_v4bind != 0) { 159756590Sshin syslog(LOG_INFO, "IPv4 bind is ignored for %s", 159856590Sshin sep->se_service); 159956590Sshin freeconfig(sep); 160056590Sshin goto more; 160156590Sshin } 160256590Sshin sep->se_family = AF_INET; 160356590Sshin } 160456590Sshin /* init ctladdr */ 160556590Sshin switch(sep->se_family) { 160656590Sshin case AF_INET: 160756590Sshin memcpy(&sep->se_ctrladdr4, bind_sa4, 160856590Sshin sizeof(sep->se_ctrladdr4)); 160956590Sshin sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr4); 161056590Sshin sep->se_ctladdrinitok = 1; 161156590Sshin break; 161256590Sshin#ifdef INET6 161356590Sshin case AF_INET6: 161456590Sshin memcpy(&sep->se_ctrladdr6, bind_sa6, 161556590Sshin sizeof(sep->se_ctrladdr6)); 161656590Sshin sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr6); 161756590Sshin sep->se_ctladdrinitok = 1; 161856590Sshin break; 161956590Sshin#endif 162056590Sshin } 16211553Srgrimes arg = sskip(&cp); 162219618Sjulian if (!strncmp(arg, "wait", 4)) 162319618Sjulian sep->se_accept = 0; 162419618Sjulian else if (!strncmp(arg, "nowait", 6)) 162519618Sjulian sep->se_accept = 1; 162619618Sjulian else { 162719618Sjulian syslog(LOG_ERR, 162819618Sjulian "%s: bad wait/nowait for service %s", 162919618Sjulian CONFIG, sep->se_service); 163019618Sjulian goto more; 163119618Sjulian } 163248069Ssheldonh sep->se_maxchild = -1; 163348069Ssheldonh sep->se_maxcpm = -1; 163419618Sjulian if ((s = strchr(arg, '/')) != NULL) { 163519618Sjulian char *eptr; 163619618Sjulian u_long val; 163719618Sjulian 163819618Sjulian val = strtoul(s + 1, &eptr, 10); 163930847Sdima if (eptr == s + 1 || val > MAX_MAXCHLD) { 164019618Sjulian syslog(LOG_ERR, 164119618Sjulian "%s: bad max-child for service %s", 164219618Sjulian CONFIG, sep->se_service); 164319618Sjulian goto more; 164419618Sjulian } 164548069Ssheldonh if (debug) 164648069Ssheldonh if (!sep->se_accept && val != 1) 164748069Ssheldonh warnx("maxchild=%lu for wait service %s" 164848069Ssheldonh " not recommended", val, sep->se_service); 164919618Sjulian sep->se_maxchild = val; 165030847Sdima if (*eptr == '/') 165130847Sdima sep->se_maxcpm = strtol(eptr + 1, &eptr, 10); 165230847Sdima /* 165330847Sdima * explicitly do not check for \0 for future expansion / 165430847Sdima * backwards compatibility 165530847Sdima */ 165619618Sjulian } 16571553Srgrimes if (ISMUX(sep)) { 16581553Srgrimes /* 165919618Sjulian * Silently enforce "nowait" mode for TCPMUX services 166019618Sjulian * since they don't have an assigned port to listen on. 16611553Srgrimes */ 166219618Sjulian sep->se_accept = 1; 16631553Srgrimes if (strcmp(sep->se_proto, "tcp")) { 16648857Srgrimes syslog(LOG_ERR, 16651553Srgrimes "%s: bad protocol for tcpmux service %s", 16661553Srgrimes CONFIG, sep->se_service); 16671553Srgrimes goto more; 16681553Srgrimes } 16691553Srgrimes if (sep->se_socktype != SOCK_STREAM) { 16708857Srgrimes syslog(LOG_ERR, 16711553Srgrimes "%s: bad socket type for tcpmux service %s", 16721553Srgrimes CONFIG, sep->se_service); 16731553Srgrimes goto more; 16741553Srgrimes } 16751553Srgrimes } 16761553Srgrimes sep->se_user = newstr(sskip(&cp)); 167730792Sache#ifdef LOGIN_CAP 167830792Sache if ((s = strrchr(sep->se_user, '/')) != NULL) { 167930792Sache *s = '\0'; 168030792Sache sep->se_class = newstr(s + 1); 168130792Sache } else 168230792Sache sep->se_class = newstr(RESOURCE_RC); 168330792Sache#endif 168430807Sache if ((s = strrchr(sep->se_user, ':')) != NULL) { 168530807Sache *s = '\0'; 168630807Sache sep->se_group = newstr(s + 1); 168730807Sache } else 168830807Sache sep->se_group = NULL; 16891553Srgrimes sep->se_server = newstr(sskip(&cp)); 169045588Smarkm if ((sep->se_server_name = rindex(sep->se_server, '/'))) 169145588Smarkm sep->se_server_name++; 16921553Srgrimes if (strcmp(sep->se_server, "internal") == 0) { 16931553Srgrimes struct biltin *bi; 16941553Srgrimes 16951553Srgrimes for (bi = biltins; bi->bi_service; bi++) 169649026Sdes if (bi->bi_socktype == sep->se_socktype && 169748467Ssheldonh matchservent(bi->bi_service, sep->se_service, 169848467Ssheldonh sep->se_proto)) 16991553Srgrimes break; 17001553Srgrimes if (bi->bi_service == 0) { 17011553Srgrimes syslog(LOG_ERR, "internal service %s unknown", 17021553Srgrimes sep->se_service); 17031553Srgrimes goto more; 17041553Srgrimes } 170519618Sjulian sep->se_accept = 1; /* force accept mode for built-ins */ 17061553Srgrimes sep->se_bi = bi; 17071553Srgrimes } else 17081553Srgrimes sep->se_bi = NULL; 170948069Ssheldonh if (sep->se_maxcpm < 0) 171048069Ssheldonh sep->se_maxcpm = maxcpm; 171145588Smarkm if (sep->se_maxchild < 0) { /* apply default max-children */ 171248069Ssheldonh if (sep->se_bi && sep->se_bi->bi_maxchild >= 0) 171319618Sjulian sep->se_maxchild = sep->se_bi->bi_maxchild; 171448069Ssheldonh else if (sep->se_accept) 171548069Ssheldonh sep->se_maxchild = maxchild > 0 ? maxchild : 0; 171619618Sjulian else 171748069Ssheldonh sep->se_maxchild = 1; 171845588Smarkm } 171919618Sjulian if (sep->se_maxchild) { 172019618Sjulian sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); 172119618Sjulian if (sep->se_pids == NULL) { 172249102Ssheldonh syslog(LOG_ERR, "malloc: %m"); 172319618Sjulian exit(EX_OSERR); 172419618Sjulian } 172519618Sjulian } 17261553Srgrimes argc = 0; 17271553Srgrimes for (arg = skip(&cp); cp; arg = skip(&cp)) 172819618Sjulian if (argc < MAXARGV) { 17291553Srgrimes sep->se_argv[argc++] = newstr(arg); 173019618Sjulian } else { 173119618Sjulian syslog(LOG_ERR, 173219618Sjulian "%s: too many arguments for service %s", 173319618Sjulian CONFIG, sep->se_service); 173419618Sjulian goto more; 173519618Sjulian } 17361553Srgrimes while (argc <= MAXARGV) 17371553Srgrimes sep->se_argv[argc++] = NULL; 173856590Sshin#ifdef IPSEC 173956590Sshin sep->se_policy = policy ? newstr(policy) : NULL; 174056590Sshin#endif 17411553Srgrimes return (sep); 17421553Srgrimes} 17431553Srgrimes 17441553Srgrimesvoid 17451553Srgrimesfreeconfig(cp) 17461553Srgrimes struct servtab *cp; 17471553Srgrimes{ 17481553Srgrimes int i; 17491553Srgrimes 17501553Srgrimes if (cp->se_service) 17511553Srgrimes free(cp->se_service); 17521553Srgrimes if (cp->se_proto) 17531553Srgrimes free(cp->se_proto); 17541553Srgrimes if (cp->se_user) 17551553Srgrimes free(cp->se_user); 175630807Sache if (cp->se_group) 175730807Sache free(cp->se_group); 175830792Sache#ifdef LOGIN_CAP 175930792Sache if (cp->se_class) 176030792Sache free(cp->se_class); 176130792Sache#endif 17621553Srgrimes if (cp->se_server) 17631553Srgrimes free(cp->se_server); 176419618Sjulian if (cp->se_pids) 176519618Sjulian free(cp->se_pids); 17661553Srgrimes for (i = 0; i < MAXARGV; i++) 17671553Srgrimes if (cp->se_argv[i]) 17681553Srgrimes free(cp->se_argv[i]); 176956590Sshin#ifdef IPSEC 177056590Sshin if (cp->se_policy) 177156590Sshin free(cp->se_policy); 177256590Sshin#endif 17731553Srgrimes} 17741553Srgrimes 17751553Srgrimes 17761553Srgrimes/* 17771553Srgrimes * Safe skip - if skip returns null, log a syntax error in the 17781553Srgrimes * configuration file and exit. 17791553Srgrimes */ 17801553Srgrimeschar * 17811553Srgrimessskip(cpp) 17821553Srgrimes char **cpp; 17831553Srgrimes{ 17841553Srgrimes char *cp; 17851553Srgrimes 17861553Srgrimes cp = skip(cpp); 17871553Srgrimes if (cp == NULL) { 17881553Srgrimes syslog(LOG_ERR, "%s: syntax error", CONFIG); 178919617Sjulian exit(EX_DATAERR); 17901553Srgrimes } 17911553Srgrimes return (cp); 17921553Srgrimes} 17931553Srgrimes 17941553Srgrimeschar * 17951553Srgrimesskip(cpp) 17961553Srgrimes char **cpp; 17971553Srgrimes{ 17981553Srgrimes char *cp = *cpp; 17991553Srgrimes char *start; 180011933Sadam char quote = '\0'; 18011553Srgrimes 18021553Srgrimesagain: 18031553Srgrimes while (*cp == ' ' || *cp == '\t') 18041553Srgrimes cp++; 18051553Srgrimes if (*cp == '\0') { 18061553Srgrimes int c; 18071553Srgrimes 18081553Srgrimes c = getc(fconfig); 18091553Srgrimes (void) ungetc(c, fconfig); 18101553Srgrimes if (c == ' ' || c == '\t') 181119617Sjulian if ((cp = nextline(fconfig))) 18121553Srgrimes goto again; 18131553Srgrimes *cpp = (char *)0; 18141553Srgrimes return ((char *)0); 18151553Srgrimes } 181611933Sadam if (*cp == '"' || *cp == '\'') 181711933Sadam quote = *cp++; 18181553Srgrimes start = cp; 181911933Sadam if (quote) 182011933Sadam while (*cp && *cp != quote) 182111933Sadam cp++; 182211933Sadam else 182311933Sadam while (*cp && *cp != ' ' && *cp != '\t') 182411933Sadam cp++; 18251553Srgrimes if (*cp != '\0') 18261553Srgrimes *cp++ = '\0'; 18271553Srgrimes *cpp = cp; 18281553Srgrimes return (start); 18291553Srgrimes} 18301553Srgrimes 18311553Srgrimeschar * 18321553Srgrimesnextline(fd) 18331553Srgrimes FILE *fd; 18341553Srgrimes{ 18351553Srgrimes char *cp; 18361553Srgrimes 18371553Srgrimes if (fgets(line, sizeof (line), fd) == NULL) 18381553Srgrimes return ((char *)0); 18391553Srgrimes cp = strchr(line, '\n'); 18401553Srgrimes if (cp) 18411553Srgrimes *cp = '\0'; 18421553Srgrimes return (line); 18431553Srgrimes} 18441553Srgrimes 18451553Srgrimeschar * 18461553Srgrimesnewstr(cp) 18471553Srgrimes char *cp; 18481553Srgrimes{ 184919617Sjulian if ((cp = strdup(cp ? cp : ""))) 18501553Srgrimes return (cp); 18511553Srgrimes syslog(LOG_ERR, "strdup: %m"); 185219617Sjulian exit(EX_OSERR); 18531553Srgrimes} 18541553Srgrimes 185513142Speter#ifdef OLD_SETPROCTITLE 18561553Srgrimesvoid 185713142Speterinetd_setproctitle(a, s) 18581553Srgrimes char *a; 18591553Srgrimes int s; 18601553Srgrimes{ 18611553Srgrimes int size; 18621553Srgrimes char *cp; 186356590Sshin struct sockaddr_storage ss; 186456590Sshin char buf[80], pbuf[INET6_ADDRSTRLEN]; 18651553Srgrimes 18661553Srgrimes cp = Argv[0]; 186756590Sshin size = sizeof(ss); 186856590Sshin if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) { 186956590Sshin getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf), 187056590Sshin NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); 187156590Sshin (void) sprintf(buf, "-%s [%s]", a, pbuf); 187256590Sshin } else 18738857Srgrimes (void) sprintf(buf, "-%s", a); 18741553Srgrimes strncpy(cp, buf, LastArg - cp); 18751553Srgrimes cp += strlen(cp); 18761553Srgrimes while (cp < LastArg) 18771553Srgrimes *cp++ = ' '; 18781553Srgrimes} 187913142Speter#else 188013142Spetervoid 188113142Speterinetd_setproctitle(a, s) 188213142Speter char *a; 188313142Speter int s; 188413142Speter{ 188513142Speter int size; 188656590Sshin struct sockaddr_storage ss; 188756590Sshin char buf[80], pbuf[INET6_ADDRSTRLEN]; 18881553Srgrimes 188956590Sshin size = sizeof(ss); 189056590Sshin if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) { 189156590Sshin getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf), 189256590Sshin NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); 189356590Sshin (void) sprintf(buf, "%s [%s]", a, pbuf); 189456590Sshin } else 189513142Speter (void) sprintf(buf, "%s", a); 189613142Speter setproctitle("%s", buf); 189713142Speter} 189813142Speter#endif 189913142Speter 190013142Speter 19011553Srgrimes/* 19021553Srgrimes * Internet services provided internally by inetd: 19031553Srgrimes */ 19041553Srgrimes 190556590Sshinint check_loop(sa, sep) 190656590Sshin struct sockaddr *sa; 19075182Swollman struct servtab *sep; 19085182Swollman{ 19095182Swollman struct servtab *se2; 191056590Sshin char pname[INET6_ADDRSTRLEN]; 19115182Swollman 19125182Swollman for (se2 = servtab; se2; se2 = se2->se_next) { 19135182Swollman if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM) 19145182Swollman continue; 19155182Swollman 191656590Sshin switch (se2->se_family) { 191756590Sshin case AF_INET: 191856590Sshin if (((struct sockaddr_in *)sa)->sin_port == 191956590Sshin se2->se_ctrladdr4.sin_port) 192056590Sshin goto isloop; 192156590Sshin continue; 192256590Sshin#ifdef INET6 192356590Sshin case AF_INET6: 192456590Sshin if (((struct sockaddr_in *)sa)->sin_port == 192556590Sshin se2->se_ctrladdr4.sin_port) 192656590Sshin goto isloop; 192756590Sshin continue; 192856590Sshin#endif 192956590Sshin default: 193056590Sshin continue; 19315182Swollman } 193256590Sshin isloop: 193356590Sshin getnameinfo(sa, sa->sa_len, pname, sizeof(pname), NULL, 0, 193456590Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 193556590Sshin syslog(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s", 193656590Sshin sep->se_service, sep->se_proto, 193756590Sshin se2->se_service, se2->se_proto, 193856590Sshin pname); 193956590Sshin return 1; 19405182Swollman } 19415182Swollman return 0; 19425182Swollman} 19435182Swollman 19441553Srgrimes/* 19451553Srgrimes * print_service: 19461553Srgrimes * Dump relevant information to stderr 19471553Srgrimes */ 19481553Srgrimesvoid 19491553Srgrimesprint_service(action, sep) 19501553Srgrimes char *action; 19511553Srgrimes struct servtab *sep; 19521553Srgrimes{ 195319617Sjulian fprintf(stderr, 195456590Sshin "%s: %s proto=%s accept=%d max=%d user=%s group=%s" 195530792Sache#ifdef LOGIN_CAP 195656590Sshin "class=%s" 195730792Sache#endif 195856590Sshin " builtin=%p server=%s" 195956590Sshin#ifdef IPSEC 196056590Sshin " policy=\"%s\"" 196156590Sshin#endif 196256590Sshin "\n", 196319617Sjulian action, sep->se_service, sep->se_proto, 196430807Sache sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group, 196530792Sache#ifdef LOGIN_CAP 196630792Sache sep->se_class, 196730792Sache#endif 196856590Sshin (void *) sep->se_bi, sep->se_server 196956590Sshin#ifdef IPSEC 197056590Sshin , (sep->se_policy ? sep->se_policy : "") 197156590Sshin#endif 197256590Sshin ); 19731553Srgrimes} 19741553Srgrimes 197530847Sdima#define CPMHSIZE 256 197630847Sdima#define CPMHMASK (CPMHSIZE-1) 197730847Sdima#define CHTGRAN 10 197830847Sdima#define CHTSIZE 6 197930847Sdima 198030847Sdimatypedef struct CTime { 198130847Sdima unsigned long ct_Ticks; 198230847Sdima int ct_Count; 198330847Sdima} CTime; 198430847Sdima 198530847Sdimatypedef struct CHash { 198656590Sshin union { 198756590Sshin struct in_addr c4_Addr; 198856590Sshin struct in6_addr c6_Addr; 198956590Sshin } cu_Addr; 199056590Sshin#define ch_Addr4 cu_Addr.c4_Addr 199156590Sshin#define ch_Addr6 cu_Addr.c6_Addr 199256590Sshin int ch_Family; 199330847Sdima time_t ch_LTime; 199430847Sdima char *ch_Service; 199530847Sdima CTime ch_Times[CHTSIZE]; 199630847Sdima} CHash; 199730847Sdima 199830847SdimaCHash CHashAry[CPMHSIZE]; 199930847Sdima 200030847Sdimaint 200130847Sdimacpmip(sep, ctrl) 200230847Sdima struct servtab *sep; 200330847Sdima int ctrl; 200430847Sdima{ 200556590Sshin struct sockaddr_storage rss; 200656590Sshin int rssLen = sizeof(rss); 200730847Sdima int r = 0; 200830847Sdima 200930847Sdima /* 201030847Sdima * If getpeername() fails, just let it through (if logging is 201130847Sdima * enabled the condition is caught elsewhere) 201230847Sdima */ 201330847Sdima 201430847Sdima if (sep->se_maxcpm > 0 && 201556590Sshin getpeername(ctrl, (struct sockaddr *)&rss, &rssLen) == 0 ) { 201630847Sdima time_t t = time(NULL); 201730847Sdima int hv = 0xABC3D20F; 201830847Sdima int i; 201930847Sdima int cnt = 0; 202030847Sdima CHash *chBest = NULL; 202130847Sdima unsigned int ticks = t / CHTGRAN; 202256590Sshin struct sockaddr_in *sin; 202356590Sshin#ifdef INET6 202456590Sshin struct sockaddr_in6 *sin6; 202556590Sshin#endif 202630847Sdima 202756590Sshin sin = (struct sockaddr_in *)&rss; 202856590Sshin#ifdef INET6 202956590Sshin sin6 = (struct sockaddr_in6 *)&rss; 203056590Sshin#endif 203130847Sdima { 203230847Sdima char *p; 203356590Sshin int i, addrlen; 203430847Sdima 203556590Sshin switch (rss.ss_family) { 203656590Sshin case AF_INET: 203756590Sshin p = (char *)&sin->sin_addr; 203856590Sshin addrlen = sizeof(struct in_addr); 203956590Sshin break; 204056590Sshin#ifdef INET6 204156590Sshin case AF_INET6: 204256590Sshin p = (char *)&sin6->sin6_addr; 204356590Sshin addrlen = sizeof(struct in6_addr); 204456590Sshin break; 204556590Sshin#endif 204656590Sshin default: 204756590Sshin /* should not happen */ 204856590Sshin return -1; 204956590Sshin } 205056590Sshin 205156590Sshin for (i = 0; i < addrlen; ++i, ++p) { 205230847Sdima hv = (hv << 5) ^ (hv >> 23) ^ *p; 205330847Sdima } 205430847Sdima hv = (hv ^ (hv >> 16)); 205530847Sdima } 205630847Sdima for (i = 0; i < 5; ++i) { 205730847Sdima CHash *ch = &CHashAry[(hv + i) & CPMHMASK]; 205830847Sdima 205956590Sshin if (rss.ss_family == AF_INET && 206056590Sshin ch->ch_Family == AF_INET && 206156590Sshin sin->sin_addr.s_addr == ch->ch_Addr4.s_addr && 206230847Sdima ch->ch_Service && strcmp(sep->se_service, 206330847Sdima ch->ch_Service) == 0) { 206430847Sdima chBest = ch; 206530847Sdima break; 206630847Sdima } 206756590Sshin#ifdef INET6 206856590Sshin if (rss.ss_family == AF_INET6 && 206956590Sshin ch->ch_Family == AF_INET6 && 207056590Sshin IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 207156590Sshin &ch->ch_Addr6) != 0 && 207256590Sshin ch->ch_Service && strcmp(sep->se_service, 207356590Sshin ch->ch_Service) == 0) { 207456590Sshin chBest = ch; 207556590Sshin break; 207656590Sshin } 207756590Sshin#endif 207830847Sdima if (chBest == NULL || ch->ch_LTime == 0 || 207930847Sdima ch->ch_LTime < chBest->ch_LTime) { 208030847Sdima chBest = ch; 208130847Sdima } 208230847Sdima } 208356590Sshin if ((rss.ss_family == AF_INET && 208456590Sshin (chBest->ch_Family != AF_INET || 208556590Sshin sin->sin_addr.s_addr != chBest->ch_Addr4.s_addr)) || 208630847Sdima chBest->ch_Service == NULL || 208730847Sdima strcmp(sep->se_service, chBest->ch_Service) != 0) { 208856590Sshin chBest->ch_Family = sin->sin_family; 208956590Sshin chBest->ch_Addr4 = sin->sin_addr; 209030847Sdima if (chBest->ch_Service) 209130847Sdima free(chBest->ch_Service); 209230847Sdima chBest->ch_Service = strdup(sep->se_service); 209330847Sdima bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 209430847Sdima } 209556590Sshin#ifdef INET6 209656590Sshin if ((rss.ss_family == AF_INET6 && 209756590Sshin (chBest->ch_Family != AF_INET6 || 209856590Sshin IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 209956590Sshin &chBest->ch_Addr6) == 0)) || 210056590Sshin chBest->ch_Service == NULL || 210156590Sshin strcmp(sep->se_service, chBest->ch_Service) != 0) { 210256590Sshin chBest->ch_Family = sin6->sin6_family; 210356590Sshin chBest->ch_Addr6 = sin6->sin6_addr; 210456590Sshin if (chBest->ch_Service) 210556590Sshin free(chBest->ch_Service); 210656590Sshin chBest->ch_Service = strdup(sep->se_service); 210756590Sshin bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 210856590Sshin } 210956590Sshin#endif 211030847Sdima chBest->ch_LTime = t; 211130847Sdima { 211230847Sdima CTime *ct = &chBest->ch_Times[ticks % CHTSIZE]; 211330847Sdima if (ct->ct_Ticks != ticks) { 211430847Sdima ct->ct_Ticks = ticks; 211530847Sdima ct->ct_Count = 0; 211630847Sdima } 211730847Sdima ++ct->ct_Count; 211830847Sdima } 211930847Sdima for (i = 0; i < CHTSIZE; ++i) { 212030847Sdima CTime *ct = &chBest->ch_Times[i]; 212130847Sdima if (ct->ct_Ticks <= ticks && 212230847Sdima ct->ct_Ticks >= ticks - CHTSIZE) { 212330847Sdima cnt += ct->ct_Count; 212430847Sdima } 212530847Sdima } 212630847Sdima if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) { 212756590Sshin char pname[INET6_ADDRSTRLEN]; 212856590Sshin 212956590Sshin getnameinfo((struct sockaddr *)&rss, 213056590Sshin ((struct sockaddr *)&rss)->sa_len, 213156590Sshin pname, sizeof(pname), NULL, 0, 213256590Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 213330847Sdima r = -1; 213430847Sdima syslog(LOG_ERR, 213533794Spst "%s from %s exceeded counts/min (limit %d/min)", 213656590Sshin sep->se_service, pname, 213733794Spst sep->se_maxcpm); 213830847Sdima } 213930847Sdima } 214030847Sdima return(r); 214130847Sdima} 2142