inetd.c revision 67415
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 67415 2000-10-21 09:43:12Z dwmalone $"; 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 19464197Sdwmalone#ifndef TOOMANY 1952659Scsgr#define TOOMANY 256 /* don't start more than TOOMANY */ 19664197Sdwmalone#endif 1971553Srgrimes#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ 1981553Srgrimes#define RETRYTIME (60*10) /* retry after bind or server fail */ 19919618Sjulian#define MAX_MAXCHLD 32767 /* max allowable max children */ 2001553Srgrimes 2011553Srgrimes#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) 2021553Srgrimes 20348279Ssheldonhint allow_severity; 20448279Ssheldonhint deny_severity; 20548697Ssheldonhint wrap_ex = 0; 20648279Ssheldonhint wrap_bi = 0; 2071553Srgrimesint debug = 0; 2082659Scsgrint log = 0; 20948988Ssheldonhint maxsock; /* highest-numbered descriptor */ 2101553Srgrimesfd_set allsock; 2111553Srgrimesint options; 2121553Srgrimesint timingout; 2131553Srgrimesint toomany = TOOMANY; 21448069Ssheldonhint maxchild = MAXCHILD; 21548069Ssheldonhint maxcpm = MAXCPM; 2161553Srgrimesstruct servent *sp; 2172657Scsgrstruct rpcent *rpc; 21856590Sshinchar *hostname = NULL; 21956590Sshinstruct sockaddr_in *bind_sa4; 22056590Sshinint no_v4bind = 1; 22156590Sshin#ifdef INET6 22256590Sshinstruct sockaddr_in6 *bind_sa6; 22356590Sshinint no_v6bind = 1; 22456590Sshin#endif 22542122Sdesint signalpipe[2]; 22648991Ssheldonh#ifdef SANITY_CHECK 22748991Ssheldonhint nsock; 22848991Ssheldonh#endif 2291553Srgrimes 23048981Ssheldonhstruct servtab *servtab; 2311553Srgrimes 23248981Ssheldonhextern struct biltin biltins[]; 2331553Srgrimes 2341553Srgrimes#define NUMINT (sizeof(intab) / sizeof(struct inent)) 2351553Srgrimeschar *CONFIG = _PATH_INETDCONF; 23617482Sjulianchar *pid_file = _PATH_INETDPID; 23713142Speter 23813142Speter#ifdef OLD_SETPROCTITLE 2391553Srgrimeschar **Argv; 2401553Srgrimeschar *LastArg; 24113142Speter#endif 2421553Srgrimes 2431553Srgrimesint 24433794Spstgetvalue(arg, value, whine) 24533794Spst char *arg, *whine; 24633794Spst int *value; 24733794Spst{ 24833794Spst int tmp; 24933794Spst char *p; 25033794Spst 25133794Spst tmp = strtol(arg, &p, 0); 25264197Sdwmalone if (tmp < 0 || *p) { 25333794Spst syslog(LOG_ERR, whine, arg); 25433794Spst return 1; /* failure */ 25533794Spst } 25633794Spst *value = tmp; 25733794Spst return 0; /* success */ 25833794Spst} 25933794Spst 26033794Spstint 2611553Srgrimesmain(argc, argv, envp) 2621553Srgrimes int argc; 2631553Srgrimes char *argv[], *envp[]; 2641553Srgrimes{ 2651553Srgrimes struct servtab *sep; 2661553Srgrimes struct passwd *pwd; 26730807Sache struct group *grp; 26848962Ssheldonh struct sigaction sa, saalrm, sachld, sahup, sapipe; 2691553Srgrimes int tmpint, ch, dofork; 2701553Srgrimes pid_t pid; 2711553Srgrimes char buf[50]; 27221640Speter#ifdef LOGIN_CAP 27321640Speter login_cap_t *lc = NULL; 27421640Speter#endif 27545089Smarkm struct request_info req; 27645089Smarkm int denied; 27745089Smarkm char *service = NULL; 27848382Ssheldonh char *pnm; 27956590Sshin union { 28056590Sshin struct sockaddr peer_un; 28156590Sshin struct sockaddr_in peer_un4; 28256590Sshin struct sockaddr_in6 peer_un6; 28356590Sshin struct sockaddr_storage peer_max; 28456590Sshin } p_un; 28556590Sshin#define peer p_un.peer_un 28656590Sshin#define peer4 p_un.peer_un4 28756590Sshin#define peer6 p_un.peer_un6 28856590Sshin#define peermax p_un.peer_max 28947972Ssheldonh int i; 29056590Sshin struct addrinfo hints, *res; 29156590Sshin char *servname; 29256590Sshin int error; 2931553Srgrimes 29413142Speter 29513142Speter#ifdef OLD_SETPROCTITLE 2961553Srgrimes Argv = argv; 2971553Srgrimes if (envp == 0 || *envp == 0) 2981553Srgrimes envp = argv; 2991553Srgrimes while (*envp) 3001553Srgrimes envp++; 3011553Srgrimes LastArg = envp[-1] + strlen(envp[-1]); 30213142Speter#endif 3031553Srgrimes 3041553Srgrimes openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); 3051553Srgrimes 30648697Ssheldonh while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:")) != -1) 3071553Srgrimes switch(ch) { 3081553Srgrimes case 'd': 3091553Srgrimes debug = 1; 3101553Srgrimes options |= SO_DEBUG; 3111553Srgrimes break; 3122659Scsgr case 'l': 3132659Scsgr log = 1; 3142659Scsgr break; 31533794Spst case 'R': 31633794Spst getvalue(optarg, &toomany, 31733794Spst "-R %s: bad value for service invocation rate"); 3181553Srgrimes break; 31933794Spst case 'c': 32033794Spst getvalue(optarg, &maxchild, 32133794Spst "-c %s: bad value for maximum children"); 32233794Spst break; 32333794Spst case 'C': 32433794Spst getvalue(optarg, &maxcpm, 32533794Spst "-C %s: bad value for maximum children/minute"); 32633794Spst break; 32717482Sjulian case 'a': 32856590Sshin hostname = optarg; 32917482Sjulian break; 33017482Sjulian case 'p': 33117482Sjulian pid_file = optarg; 33217482Sjulian break; 33348279Ssheldonh case 'w': 33448697Ssheldonh wrap_ex++; 33548279Ssheldonh break; 33648697Ssheldonh case 'W': 33748697Ssheldonh wrap_bi++; 33848697Ssheldonh break; 3391553Srgrimes case '?': 3401553Srgrimes default: 3411553Srgrimes syslog(LOG_ERR, 34248697Ssheldonh "usage: inetd [-dlwW] [-a address] [-R rate]" 34333794Spst " [-c maximum] [-C rate]" 34417482Sjulian " [-p pidfile] [conf-file]"); 34519617Sjulian exit(EX_USAGE); 3461553Srgrimes } 34756590Sshin /* 34856590Sshin * Initialize Bind Addrs. 34956590Sshin * When hostname is NULL, wild card bind addrs are obtained from 35056590Sshin * getaddrinfo(). But getaddrinfo() requires at least one of 35156590Sshin * hostname or servname is non NULL. 35256590Sshin * So when hostname is NULL, set dummy value to servname. 35356590Sshin */ 35456590Sshin servname = (hostname == NULL) ? "discard" /* dummy */ : NULL; 35556590Sshin 35656590Sshin bzero(&hints, sizeof(struct addrinfo)); 35756590Sshin hints.ai_flags = AI_PASSIVE; 35856590Sshin hints.ai_family = AF_UNSPEC; 35956590Sshin error = getaddrinfo(hostname, servname, &hints, &res); 36056590Sshin if (error != 0) { 36156590Sshin syslog(LOG_ERR, "-a %s: %s", hostname, gai_strerror(error)); 36256590Sshin if (error == EAI_SYSTEM) 36356590Sshin syslog(LOG_ERR, "%s", strerror(errno)); 36456590Sshin exit(EX_USAGE); 36556590Sshin } 36656590Sshin do { 36756590Sshin if (res->ai_addr == NULL) { 36856590Sshin syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname); 36956590Sshin exit(EX_USAGE); 37056590Sshin } 37156590Sshin switch (res->ai_addr->sa_family) { 37256590Sshin case AF_INET: 37356590Sshin if (no_v4bind == 0) 37456590Sshin continue; 37556590Sshin bind_sa4 = (struct sockaddr_in *)res->ai_addr; 37656590Sshin /* init port num in case servname is dummy */ 37756590Sshin bind_sa4->sin_port = 0; 37856590Sshin no_v4bind = 0; 37956590Sshin continue; 38056590Sshin#ifdef INET6 38156590Sshin case AF_INET6: 38256590Sshin if (no_v6bind == 0) 38356590Sshin continue; 38456590Sshin bind_sa6 = (struct sockaddr_in6 *)res->ai_addr; 38556590Sshin /* init port num in case servname is dummy */ 38656590Sshin bind_sa6->sin6_port = 0; 38756590Sshin no_v6bind = 0; 38856590Sshin continue; 38956590Sshin#endif 39056590Sshin } 39156590Sshin if (no_v4bind == 0 39256590Sshin#ifdef INET6 39356590Sshin && no_v6bind == 0 39456590Sshin#endif 39556590Sshin ) 39656590Sshin break; 39756590Sshin } while ((res = res->ai_next) != NULL); 39856590Sshin if (no_v4bind != 0 39956590Sshin#ifdef INET6 40056590Sshin && no_v6bind != 0 40156590Sshin#endif 40256590Sshin ) { 40356590Sshin syslog(LOG_ERR, "-a %s: unknown address family", hostname); 40456590Sshin exit(EX_USAGE); 40556590Sshin } 40656590Sshin 4071553Srgrimes argc -= optind; 4081553Srgrimes argv += optind; 4091553Srgrimes 4101553Srgrimes if (argc > 0) 4111553Srgrimes CONFIG = argv[0]; 4121553Srgrimes if (debug == 0) { 41311447Swollman FILE *fp; 41412024Speter if (daemon(0, 0) < 0) { 41512024Speter syslog(LOG_WARNING, "daemon(0,0) failed: %m"); 41612024Speter } 41712024Speter /* 41812024Speter * In case somebody has started inetd manually, we need to 41912024Speter * clear the logname, so that old servers run as root do not 42012024Speter * get the user's logname.. 42112024Speter */ 42212024Speter if (setlogin("") < 0) { 42312024Speter syslog(LOG_WARNING, "cannot clear logname: %m"); 42412024Speter /* no big deal if it fails.. */ 42512024Speter } 42611447Swollman pid = getpid(); 42717482Sjulian fp = fopen(pid_file, "w"); 42811447Swollman if (fp) { 42911447Swollman fprintf(fp, "%ld\n", (long)pid); 43011447Swollman fclose(fp); 43111447Swollman } else { 43217482Sjulian syslog(LOG_WARNING, "%s: %m", pid_file); 43311447Swollman } 4341553Srgrimes } 43535948Sbde sa.sa_flags = 0; 43635948Sbde sigemptyset(&sa.sa_mask); 43735948Sbde sigaddset(&sa.sa_mask, SIGALRM); 43835948Sbde sigaddset(&sa.sa_mask, SIGCHLD); 43935948Sbde sigaddset(&sa.sa_mask, SIGHUP); 44042122Sdes sa.sa_handler = flag_retry; 44148962Ssheldonh sigaction(SIGALRM, &sa, &saalrm); 44242122Sdes config(); 44342122Sdes sa.sa_handler = flag_config; 44448962Ssheldonh sigaction(SIGHUP, &sa, &sahup); 44542122Sdes sa.sa_handler = flag_reapchild; 44648962Ssheldonh sigaction(SIGCHLD, &sa, &sachld); 44735848Sguido sa.sa_handler = SIG_IGN; 44835948Sbde sigaction(SIGPIPE, &sa, &sapipe); 4491553Srgrimes 4501553Srgrimes { 4511553Srgrimes /* space for daemons to overwrite environment for ps */ 4521553Srgrimes#define DUMMYSIZE 100 4531553Srgrimes char dummy[DUMMYSIZE]; 4541553Srgrimes 45519298Salex (void)memset(dummy, 'x', DUMMYSIZE - 1); 4561553Srgrimes dummy[DUMMYSIZE - 1] = '\0'; 4571553Srgrimes (void)setenv("inetd_dummy", dummy, 1); 4581553Srgrimes } 4591553Srgrimes 46042250Sdes if (pipe(signalpipe) != 0) { 46152219Scharnier syslog(LOG_ERR, "pipe: %m"); 46242250Sdes exit(EX_OSERR); 46342122Sdes } 46442122Sdes FD_SET(signalpipe[0], &allsock); 46548991Ssheldonh#ifdef SANITY_CHECK 46647015Sdes nsock++; 46748991Ssheldonh#endif 46848989Ssheldonh if (signalpipe[0] > maxsock) 46948989Ssheldonh maxsock = signalpipe[0]; 47048989Ssheldonh if (signalpipe[1] > maxsock) 47148989Ssheldonh maxsock = signalpipe[1]; 47241685Sdillon 4731553Srgrimes for (;;) { 4741553Srgrimes int n, ctrl; 4751553Srgrimes fd_set readable; 4761553Srgrimes 47748991Ssheldonh#ifdef SANITY_CHECK 4781553Srgrimes if (nsock == 0) { 47947015Sdes syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 48047015Sdes exit(EX_SOFTWARE); 4811553Srgrimes } 48248991Ssheldonh#endif 4831553Srgrimes readable = allsock; 48442122Sdes if ((n = select(maxsock + 1, &readable, (fd_set *)0, 48542122Sdes (fd_set *)0, (struct timeval *)0)) <= 0) { 48642122Sdes if (n < 0 && errno != EINTR) { 4871553Srgrimes syslog(LOG_WARNING, "select: %m"); 48828907Simp sleep(1); 48928907Simp } 4901553Srgrimes continue; 4911553Srgrimes } 49242122Sdes /* handle any queued signal flags */ 49342250Sdes if (FD_ISSET(signalpipe[0], &readable)) { 49442122Sdes int n; 49542250Sdes if (ioctl(signalpipe[0], FIONREAD, &n) != 0) { 49642122Sdes syslog(LOG_ERR, "ioctl: %m"); 49742122Sdes exit(EX_OSERR); 49842122Sdes } 49942250Sdes while (--n >= 0) { 50042250Sdes char c; 50142250Sdes if (read(signalpipe[0], &c, 1) != 1) { 50242250Sdes syslog(LOG_ERR, "read: %m"); 50342250Sdes exit(EX_OSERR); 50442250Sdes } 50542250Sdes if (debug) 50656482Scharnier warnx("handling signal flag %c", c); 50742250Sdes switch(c) { 50842250Sdes case 'A': /* sigalrm */ 50942250Sdes retry(); 51042250Sdes break; 51142250Sdes case 'C': /* sigchld */ 51242250Sdes reapchild(); 51342250Sdes break; 51442250Sdes case 'H': /* sighup */ 51542250Sdes config(); 51642250Sdes break; 51742250Sdes } 51842250Sdes } 51942122Sdes } 5201553Srgrimes for (sep = servtab; n && sep; sep = sep->se_next) 5211553Srgrimes if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { 5221553Srgrimes n--; 5231553Srgrimes if (debug) 52429602Scharnier warnx("someone wants %s", sep->se_service); 52519618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) { 52653256Speter i = 1; 52753256Speter if (ioctl(sep->se_fd, FIONBIO, &i) < 0) 52853256Speter syslog(LOG_ERR, "ioctl (FIONBIO, 1): %m"); 5291553Srgrimes ctrl = accept(sep->se_fd, (struct sockaddr *)0, 5301553Srgrimes (int *)0); 5311553Srgrimes if (debug) 53229602Scharnier warnx("accept, ctrl %d", ctrl); 5331553Srgrimes if (ctrl < 0) { 5341553Srgrimes if (errno != EINTR) 5351553Srgrimes syslog(LOG_WARNING, 5361553Srgrimes "accept (for %s): %m", 53737844Sphk sep->se_service); 53837816Sphk if (sep->se_accept && 53937816Sphk sep->se_socktype == SOCK_STREAM) 54037816Sphk close(ctrl); 5411553Srgrimes continue; 5421553Srgrimes } 54353256Speter i = 0; 54453256Speter if (ioctl(sep->se_fd, FIONBIO, &i) < 0) 54553256Speter syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m"); 54653256Speter if (ioctl(ctrl, FIONBIO, &i) < 0) 54753256Speter syslog(LOG_ERR, "ioctl2(FIONBIO, 0): %m"); 54830847Sdima if (cpmip(sep, ctrl) < 0) { 54930847Sdima close(ctrl); 55030847Sdima continue; 55130847Sdima } 5521553Srgrimes } else 5531553Srgrimes ctrl = sep->se_fd; 55448382Ssheldonh if (log && !ISWRAP(sep)) { 55556590Sshin char pname[INET6_ADDRSTRLEN]; 55648382Ssheldonh pnm = "unknown"; 55756590Sshin i = sizeof peermax; 55848382Ssheldonh if (getpeername(ctrl, (struct sockaddr *) 55956590Sshin &peermax, &i)) { 56056590Sshin i = sizeof peermax; 56148382Ssheldonh if (recvfrom(ctrl, buf, sizeof(buf), 56248382Ssheldonh MSG_PEEK, 56356590Sshin (struct sockaddr *)&peermax, 56456590Sshin &i) >= 0) { 56556590Sshin getnameinfo((struct sockaddr *)&peermax, 56657383Sshin peer.sa_len, 56756590Sshin pname, sizeof(pname), 56856590Sshin NULL, 0, 56956590Sshin NI_NUMERICHOST| 57056590Sshin NI_WITHSCOPEID); 57156590Sshin pnm = pname; 57256590Sshin } 57356590Sshin } else { 57456590Sshin getnameinfo((struct sockaddr *)&peermax, 57557383Sshin peer.sa_len, 57656590Sshin pname, sizeof(pname), 57756590Sshin NULL, 0, 57856590Sshin NI_NUMERICHOST| 57956590Sshin NI_WITHSCOPEID); 58056590Sshin pnm = pname; 58148382Ssheldonh } 58248382Ssheldonh syslog(LOG_INFO,"%s from %s", sep->se_service, pnm); 58348382Ssheldonh } 58442122Sdes (void) sigblock(SIGBLOCK); 5851553Srgrimes pid = 0; 58647972Ssheldonh /* 58748958Ssheldonh * Fork for all external services, builtins which need to 58848958Ssheldonh * fork and anything we're wrapping (as wrapping might 58948958Ssheldonh * block or use hosts_options(5) twist). 59047972Ssheldonh */ 59148382Ssheldonh dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep); 5921553Srgrimes if (dofork) { 5931553Srgrimes if (sep->se_count++ == 0) 59437856Sache (void)gettimeofday(&sep->se_time, (struct timezone *)NULL); 59564197Sdwmalone else if (toomany > 0 && sep->se_count >= toomany) { 5961553Srgrimes struct timeval now; 5971553Srgrimes 59837856Sache (void)gettimeofday(&now, (struct timezone *)NULL); 5991553Srgrimes if (now.tv_sec - sep->se_time.tv_sec > 6001553Srgrimes CNT_INTVL) { 6011553Srgrimes sep->se_time = now; 6021553Srgrimes sep->se_count = 1; 6031553Srgrimes } else { 6041553Srgrimes syslog(LOG_ERR, 6051553Srgrimes "%s/%s server failing (looping), service terminated", 6061553Srgrimes sep->se_service, sep->se_proto); 60767415Sdwmalone if (sep->se_accept && 60867415Sdwmalone sep->se_socktype == SOCK_STREAM) 60967415Sdwmalone close(ctrl); 6101553Srgrimes close_sep(sep); 61142122Sdes sigsetmask(0L); 6121553Srgrimes if (!timingout) { 6131553Srgrimes timingout = 1; 6141553Srgrimes alarm(RETRYTIME); 6151553Srgrimes } 6161553Srgrimes continue; 6171553Srgrimes } 6181553Srgrimes } 6191553Srgrimes pid = fork(); 6201553Srgrimes } 6211553Srgrimes if (pid < 0) { 6221553Srgrimes syslog(LOG_ERR, "fork: %m"); 62319618Sjulian if (sep->se_accept && 6241553Srgrimes sep->se_socktype == SOCK_STREAM) 6251553Srgrimes close(ctrl); 62642122Sdes sigsetmask(0L); 6271553Srgrimes sleep(1); 6281553Srgrimes continue; 6291553Srgrimes } 63019618Sjulian if (pid) 63119618Sjulian addchild(sep, pid); 63242122Sdes sigsetmask(0L); 6331553Srgrimes if (pid == 0) { 6341553Srgrimes if (dofork) { 6351553Srgrimes if (debug) 63629602Scharnier warnx("+ closing from %d", maxsock); 6371553Srgrimes for (tmpint = maxsock; tmpint > 2; tmpint--) 6381553Srgrimes if (tmpint != ctrl) 63919617Sjulian (void) close(tmpint); 64048962Ssheldonh sigaction(SIGALRM, &saalrm, (struct sigaction *)0); 64148962Ssheldonh sigaction(SIGCHLD, &sachld, (struct sigaction *)0); 64248962Ssheldonh sigaction(SIGHUP, &sahup, (struct sigaction *)0); 64348962Ssheldonh /* SIGPIPE reset before exec */ 6441553Srgrimes } 64535829Sguido /* 64635829Sguido * Call tcpmux to find the real service to exec. 64735829Sguido */ 64835829Sguido if (sep->se_bi && 64935829Sguido sep->se_bi->bi_fn == (void (*)()) tcpmux) { 65035829Sguido sep = tcpmux(ctrl); 65135829Sguido if (sep == NULL) { 65235829Sguido close(ctrl); 65335829Sguido _exit(0); 65435829Sguido } 65535829Sguido } 65648382Ssheldonh if (ISWRAP(sep)) { 65748698Ssheldonh inetd_setproctitle("wrapping", ctrl); 65847972Ssheldonh service = sep->se_server_name ? 65947972Ssheldonh sep->se_server_name : sep->se_service; 66047972Ssheldonh request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, NULL); 66145089Smarkm fromhost(&req); 66247972Ssheldonh deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY; 66347972Ssheldonh allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY; 66445089Smarkm denied = !hosts_access(&req); 66545089Smarkm if (denied) { 66645089Smarkm syslog(deny_severity, 66745089Smarkm "refused connection from %.500s, service %s (%s)", 66845089Smarkm eval_client(&req), service, sep->se_proto); 66948382Ssheldonh if (sep->se_socktype != SOCK_STREAM) 67048382Ssheldonh recv(ctrl, buf, sizeof (buf), 0); 67164059Sdwmalone if (dofork) { 67264059Sdwmalone sleep(1); 67348382Ssheldonh _exit(0); 67464059Sdwmalone } 67545089Smarkm } 67645089Smarkm if (log) { 67745089Smarkm syslog(allow_severity, 67845089Smarkm "connection from %.500s, service %s (%s)", 67945089Smarkm eval_client(&req), service, sep->se_proto); 68045089Smarkm } 68145089Smarkm } 68219617Sjulian if (sep->se_bi) { 6831553Srgrimes (*sep->se_bi->bi_fn)(ctrl, sep); 68419617Sjulian } else { 6851553Srgrimes if (debug) 68629602Scharnier warnx("%d execl %s", 68729602Scharnier getpid(), sep->se_server); 6881553Srgrimes dup2(ctrl, 0); 6891553Srgrimes close(ctrl); 6901553Srgrimes dup2(0, 1); 6911553Srgrimes dup2(0, 2); 6921553Srgrimes if ((pwd = getpwnam(sep->se_user)) == NULL) { 6931553Srgrimes syslog(LOG_ERR, 69456482Scharnier "%s/%s: %s: no such user", 6951553Srgrimes sep->se_service, sep->se_proto, 6961553Srgrimes sep->se_user); 6971553Srgrimes if (sep->se_socktype != SOCK_STREAM) 6981553Srgrimes recv(0, buf, sizeof (buf), 0); 69919617Sjulian _exit(EX_NOUSER); 7001553Srgrimes } 70130807Sache grp = NULL; 70230807Sache if ( sep->se_group != NULL 70330807Sache && (grp = getgrnam(sep->se_group)) == NULL 70430807Sache ) { 70530807Sache syslog(LOG_ERR, 70656482Scharnier "%s/%s: %s: no such group", 70730807Sache sep->se_service, sep->se_proto, 70830807Sache sep->se_group); 70930807Sache if (sep->se_socktype != SOCK_STREAM) 71030807Sache recv(0, buf, sizeof (buf), 0); 71130807Sache _exit(EX_NOUSER); 71230807Sache } 71330807Sache if (grp != NULL) 71430807Sache pwd->pw_gid = grp->gr_gid; 71521640Speter#ifdef LOGIN_CAP 71630792Sache if ((lc = login_getclass(sep->se_class)) == NULL) { 71730792Sache /* error syslogged by getclass */ 71830792Sache syslog(LOG_ERR, 71930792Sache "%s/%s: %s: login class error", 72037850Sache sep->se_service, sep->se_proto, 72137850Sache sep->se_class); 72230792Sache if (sep->se_socktype != SOCK_STREAM) 72330792Sache recv(0, buf, sizeof (buf), 0); 72430792Sache _exit(EX_NOUSER); 72530792Sache } 72621640Speter#endif 72712024Speter if (setsid() < 0) { 72812024Speter syslog(LOG_ERR, 72912024Speter "%s: can't setsid(): %m", 73012024Speter sep->se_service); 73119617Sjulian /* _exit(EX_OSERR); not fatal yet */ 73212024Speter } 73321640Speter#ifdef LOGIN_CAP 73421640Speter if (setusercontext(lc, pwd, pwd->pw_uid, 73521640Speter LOGIN_SETALL) != 0) { 73621640Speter syslog(LOG_ERR, 73721640Speter "%s: can't setusercontext(..%s..): %m", 73821640Speter sep->se_service, sep->se_user); 73921640Speter _exit(EX_OSERR); 74021640Speter } 74121640Speter#else 7421553Srgrimes if (pwd->pw_uid) { 74312024Speter if (setlogin(sep->se_user) < 0) { 74412024Speter syslog(LOG_ERR, 74512024Speter "%s: can't setlogin(%s): %m", 74612024Speter sep->se_service, sep->se_user); 74719617Sjulian /* _exit(EX_OSERR); not yet */ 74812024Speter } 7491553Srgrimes if (setgid(pwd->pw_gid) < 0) { 7501553Srgrimes syslog(LOG_ERR, 7518857Srgrimes "%s: can't set gid %d: %m", 7521553Srgrimes sep->se_service, pwd->pw_gid); 75319617Sjulian _exit(EX_OSERR); 7541553Srgrimes } 7551553Srgrimes (void) initgroups(pwd->pw_name, 7561553Srgrimes pwd->pw_gid); 7571553Srgrimes if (setuid(pwd->pw_uid) < 0) { 7581553Srgrimes syslog(LOG_ERR, 7598857Srgrimes "%s: can't set uid %d: %m", 7601553Srgrimes sep->se_service, pwd->pw_uid); 76119617Sjulian _exit(EX_OSERR); 7621553Srgrimes } 7631553Srgrimes } 76421640Speter#endif 76535948Sbde sigaction(SIGPIPE, &sapipe, 76635948Sbde (struct sigaction *)0); 7671553Srgrimes execv(sep->se_server, sep->se_argv); 76845089Smarkm syslog(LOG_ERR, 76945089Smarkm "cannot execute %s: %m", sep->se_server); 7701553Srgrimes if (sep->se_socktype != SOCK_STREAM) 7711553Srgrimes recv(0, buf, sizeof (buf), 0); 7721553Srgrimes } 77347972Ssheldonh if (dofork) 77447972Ssheldonh _exit(0); 7751553Srgrimes } 77619618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) 7771553Srgrimes close(ctrl); 7781553Srgrimes } 7791553Srgrimes } 7801553Srgrimes} 7811553Srgrimes 78219618Sjulian/* 78342122Sdes * Add a signal flag to the signal flag queue for later handling 78442122Sdes */ 78542122Sdes 78642122Sdesvoid flag_signal(c) 78742122Sdes char c; 78842122Sdes{ 78942250Sdes if (write(signalpipe[1], &c, 1) != 1) { 79042250Sdes syslog(LOG_ERR, "write: %m"); 79148985Ssheldonh _exit(EX_OSERR); 79242250Sdes } 79342122Sdes} 79442122Sdes 79542122Sdes/* 79619618Sjulian * Record a new child pid for this service. If we've reached the 79719618Sjulian * limit on children, then stop accepting incoming requests. 79819618Sjulian */ 79919618Sjulian 8001553Srgrimesvoid 80119618Sjulianaddchild(struct servtab *sep, pid_t pid) 80219618Sjulian{ 80364197Sdwmalone if (sep->se_maxchild <= 0) 80464197Sdwmalone return; 80519618Sjulian#ifdef SANITY_CHECK 80619618Sjulian if (sep->se_numchild >= sep->se_maxchild) { 80719618Sjulian syslog(LOG_ERR, "%s: %d >= %d", 80819618Sjulian __FUNCTION__, sep->se_numchild, sep->se_maxchild); 80919618Sjulian exit(EX_SOFTWARE); 81019618Sjulian } 81119618Sjulian#endif 81219618Sjulian sep->se_pids[sep->se_numchild++] = pid; 81319618Sjulian if (sep->se_numchild == sep->se_maxchild) 81419618Sjulian disable(sep); 81519618Sjulian} 81619618Sjulian 81719618Sjulian/* 81819618Sjulian * Some child process has exited. See if it's on somebody's list. 81919618Sjulian */ 82019618Sjulian 82119618Sjulianvoid 82242122Sdesflag_reapchild(signo) 8231553Srgrimes int signo; 8241553Srgrimes{ 82542250Sdes flag_signal('C'); 82642122Sdes} 82742122Sdes 82842122Sdesvoid 82942122Sdesreapchild() 83042122Sdes{ 83119618Sjulian int k, status; 8321553Srgrimes pid_t pid; 8331553Srgrimes struct servtab *sep; 8341553Srgrimes 8351553Srgrimes for (;;) { 8361553Srgrimes pid = wait3(&status, WNOHANG, (struct rusage *)0); 8371553Srgrimes if (pid <= 0) 8381553Srgrimes break; 8391553Srgrimes if (debug) 84029602Scharnier warnx("%d reaped, status %#x", pid, status); 84119618Sjulian for (sep = servtab; sep; sep = sep->se_next) { 84219618Sjulian for (k = 0; k < sep->se_numchild; k++) 84319618Sjulian if (sep->se_pids[k] == pid) 84419618Sjulian break; 84519618Sjulian if (k == sep->se_numchild) 84619618Sjulian continue; 84719618Sjulian if (sep->se_numchild == sep->se_maxchild) 84819618Sjulian enable(sep); 84919618Sjulian sep->se_pids[k] = sep->se_pids[--sep->se_numchild]; 85019618Sjulian if (status) 85119618Sjulian syslog(LOG_WARNING, 85219618Sjulian "%s[%d]: exit status 0x%x", 85319618Sjulian sep->se_server, pid, status); 85419618Sjulian break; 85519618Sjulian } 8561553Srgrimes } 8571553Srgrimes} 8581553Srgrimes 8591553Srgrimesvoid 86042122Sdesflag_config(signo) 8611553Srgrimes int signo; 8621553Srgrimes{ 86342250Sdes flag_signal('H'); 86442122Sdes} 86542122Sdes 86642122Sdesvoid config() 86742122Sdes{ 86819618Sjulian struct servtab *sep, *new, **sepp; 86942122Sdes long omask; 8701553Srgrimes 8711553Srgrimes if (!setconfig()) { 8721553Srgrimes syslog(LOG_ERR, "%s: %m", CONFIG); 8731553Srgrimes return; 8741553Srgrimes } 8751553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 8761553Srgrimes sep->se_checked = 0; 87719618Sjulian while ((new = getconfigent())) { 87830807Sache if (getpwnam(new->se_user) == NULL) { 8791553Srgrimes syslog(LOG_ERR, 88056482Scharnier "%s/%s: no such user '%s', service ignored", 88119618Sjulian new->se_service, new->se_proto, new->se_user); 8821553Srgrimes continue; 8831553Srgrimes } 88430807Sache if (new->se_group && getgrnam(new->se_group) == NULL) { 88530807Sache syslog(LOG_ERR, 88656482Scharnier "%s/%s: no such group '%s', service ignored", 88730807Sache new->se_service, new->se_proto, new->se_group); 88830807Sache continue; 88930807Sache } 89030792Sache#ifdef LOGIN_CAP 89130792Sache if (login_getclass(new->se_class) == NULL) { 89230792Sache /* error syslogged by getclass */ 89330792Sache syslog(LOG_ERR, 89437850Sache "%s/%s: %s: login class error, service ignored", 89537850Sache new->se_service, new->se_proto, new->se_class); 89630792Sache continue; 89730792Sache } 89830792Sache#endif 8991553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 90019618Sjulian if (strcmp(sep->se_service, new->se_service) == 0 && 90156590Sshin strcmp(sep->se_proto, new->se_proto) == 0 && 90256590Sshin sep->se_family == new->se_family) 9031553Srgrimes break; 9041553Srgrimes if (sep != 0) { 9051553Srgrimes int i; 9061553Srgrimes 90719618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; } 90842122Sdes omask = sigblock(SIGBLOCK); 90956590Sshin if (sep->se_nomapped != new->se_nomapped) { 91056590Sshin sep->se_nomapped = new->se_nomapped; 91156590Sshin sep->se_reset = 1; 91256590Sshin } 91319618Sjulian /* copy over outstanding child pids */ 91464197Sdwmalone if (sep->se_maxchild > 0 && new->se_maxchild > 0) { 91519618Sjulian new->se_numchild = sep->se_numchild; 91619618Sjulian if (new->se_numchild > new->se_maxchild) 91719618Sjulian new->se_numchild = new->se_maxchild; 91819618Sjulian memcpy(new->se_pids, sep->se_pids, 91919618Sjulian new->se_numchild * sizeof(*new->se_pids)); 92019618Sjulian } 92119618Sjulian SWAP(sep->se_pids, new->se_pids); 92219618Sjulian sep->se_maxchild = new->se_maxchild; 92319618Sjulian sep->se_numchild = new->se_numchild; 92430847Sdima sep->se_maxcpm = new->se_maxcpm; 92566544Sdwmalone sep->se_bi = new->se_bi; 92619618Sjulian /* might need to turn on or off service now */ 92719618Sjulian if (sep->se_fd >= 0) { 92864197Sdwmalone if (sep->se_maxchild > 0 92919618Sjulian && sep->se_numchild == sep->se_maxchild) { 93019618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 93119618Sjulian disable(sep); 93219618Sjulian } else { 93319618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) 93419618Sjulian enable(sep); 93519618Sjulian } 93619618Sjulian } 93719618Sjulian sep->se_accept = new->se_accept; 93830807Sache SWAP(sep->se_user, new->se_user); 93930807Sache SWAP(sep->se_group, new->se_group); 94030792Sache#ifdef LOGIN_CAP 94130807Sache SWAP(sep->se_class, new->se_class); 94230792Sache#endif 94330807Sache SWAP(sep->se_server, new->se_server); 94447972Ssheldonh SWAP(sep->se_server_name, new->se_server_name); 9451553Srgrimes for (i = 0; i < MAXARGV; i++) 94619618Sjulian SWAP(sep->se_argv[i], new->se_argv[i]); 94756590Sshin#ifdef IPSEC 94856590Sshin SWAP(sep->se_policy, new->se_policy); 94956590Sshin ipsecsetup(sep); 95056590Sshin#endif 95142122Sdes sigsetmask(omask); 95219618Sjulian freeconfig(new); 9531553Srgrimes if (debug) 9541553Srgrimes print_service("REDO", sep); 9551553Srgrimes } else { 95619618Sjulian sep = enter(new); 9571553Srgrimes if (debug) 9581553Srgrimes print_service("ADD ", sep); 9591553Srgrimes } 9601553Srgrimes sep->se_checked = 1; 9611553Srgrimes if (ISMUX(sep)) { 9621553Srgrimes sep->se_fd = -1; 9631553Srgrimes continue; 9641553Srgrimes } 96556590Sshin switch (sep->se_family) { 96656590Sshin case AF_INET: 96756590Sshin if (no_v4bind != 0) { 96856590Sshin sep->se_fd = -1; 96956590Sshin continue; 97056590Sshin } 97156590Sshin break; 97256590Sshin#ifdef INET6 97356590Sshin case AF_INET6: 97456590Sshin if (no_v6bind != 0) { 97556590Sshin sep->se_fd = -1; 97656590Sshin continue; 97756590Sshin } 97856590Sshin break; 97956590Sshin#endif 98056590Sshin } 9812657Scsgr if (!sep->se_rpc) { 9822657Scsgr sp = getservbyname(sep->se_service, sep->se_proto); 9832657Scsgr if (sp == 0) { 9842657Scsgr syslog(LOG_ERR, "%s/%s: unknown service", 98556590Sshin sep->se_service, sep->se_proto); 9862657Scsgr sep->se_checked = 0; 9872657Scsgr continue; 9882657Scsgr } 98956590Sshin switch (sep->se_family) { 99056590Sshin case AF_INET: 99156590Sshin if (sep->se_ctladdrinitok == 0) { 99256590Sshin memcpy(&sep->se_ctrladdr4, bind_sa4, 99356590Sshin sizeof(sep->se_ctrladdr4)); 99456590Sshin sep->se_ctrladdr_size = 99556590Sshin sizeof(sep->se_ctrladdr4); 99656590Sshin } 99756590Sshin if (sp->s_port != sep->se_ctrladdr4.sin_port) { 99856590Sshin sep->se_ctrladdr4.sin_port = 99956590Sshin sp->s_port; 100056590Sshin sep->se_reset = 1; 100156590Sshin } 100256590Sshin break; 100356590Sshin#ifdef INET6 100456590Sshin case AF_INET6: 100556590Sshin if (sep->se_ctladdrinitok == 0) { 100656590Sshin memcpy(&sep->se_ctrladdr6, bind_sa6, 100756590Sshin sizeof(sep->se_ctrladdr6)); 100856590Sshin sep->se_ctrladdr_size = 100956590Sshin sizeof(sep->se_ctrladdr6); 101056590Sshin } 101156590Sshin if (sp->s_port != 101256590Sshin sep->se_ctrladdr6.sin6_port) { 101356590Sshin sep->se_ctrladdr6.sin6_port = 101456590Sshin sp->s_port; 101556590Sshin sep->se_reset = 1; 101656590Sshin } 101756590Sshin break; 101856590Sshin#endif 10192657Scsgr } 102056590Sshin if (sep->se_reset != 0 && sep->se_fd >= 0) 102156590Sshin close_sep(sep); 10222657Scsgr } else { 10232657Scsgr rpc = getrpcbyname(sep->se_service); 10242657Scsgr if (rpc == 0) { 102552219Scharnier syslog(LOG_ERR, "%s/%s unknown RPC service", 10262657Scsgr sep->se_service, sep->se_proto); 10272657Scsgr if (sep->se_fd != -1) 10282657Scsgr (void) close(sep->se_fd); 10292657Scsgr sep->se_fd = -1; 10302657Scsgr continue; 10312657Scsgr } 10322657Scsgr if (rpc->r_number != sep->se_rpc_prog) { 10332657Scsgr if (sep->se_rpc_prog) 10342657Scsgr unregisterrpc(sep); 10352657Scsgr sep->se_rpc_prog = rpc->r_number; 10362657Scsgr if (sep->se_fd != -1) 10372657Scsgr (void) close(sep->se_fd); 10382657Scsgr sep->se_fd = -1; 10392657Scsgr } 10401553Srgrimes } 10411553Srgrimes if (sep->se_fd == -1) 10421553Srgrimes setup(sep); 10431553Srgrimes } 10441553Srgrimes endconfig(); 10451553Srgrimes /* 10461553Srgrimes * Purge anything not looked at above. 10471553Srgrimes */ 104842122Sdes omask = sigblock(SIGBLOCK); 10491553Srgrimes sepp = &servtab; 105019617Sjulian while ((sep = *sepp)) { 10511553Srgrimes if (sep->se_checked) { 10521553Srgrimes sepp = &sep->se_next; 10531553Srgrimes continue; 10541553Srgrimes } 10551553Srgrimes *sepp = sep->se_next; 10561553Srgrimes if (sep->se_fd >= 0) 10571553Srgrimes close_sep(sep); 10581553Srgrimes if (debug) 10591553Srgrimes print_service("FREE", sep); 10602657Scsgr if (sep->se_rpc && sep->se_rpc_prog > 0) 10612657Scsgr unregisterrpc(sep); 10621553Srgrimes freeconfig(sep); 10631553Srgrimes free((char *)sep); 10641553Srgrimes } 106542122Sdes (void) sigsetmask(omask); 10661553Srgrimes} 10671553Srgrimes 10681553Srgrimesvoid 10692657Scsgrunregisterrpc(sep) 10702657Scsgr struct servtab *sep; 10712657Scsgr{ 10722657Scsgr int i; 10732657Scsgr struct servtab *sepp; 107442122Sdes long omask; 10752657Scsgr 107642122Sdes omask = sigblock(SIGBLOCK); 10772657Scsgr for (sepp = servtab; sepp; sepp = sepp->se_next) { 10782657Scsgr if (sepp == sep) 10792657Scsgr continue; 10802657Scsgr if (sep->se_checked == 0 || 10812657Scsgr !sepp->se_rpc || 10822657Scsgr sep->se_rpc_prog != sepp->se_rpc_prog) 10832657Scsgr continue; 10842657Scsgr return; 10852657Scsgr } 10862657Scsgr if (debug) 10872657Scsgr print_service("UNREG", sep); 10882657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) 10892657Scsgr pmap_unset(sep->se_rpc_prog, i); 10902657Scsgr if (sep->se_fd != -1) 10912657Scsgr (void) close(sep->se_fd); 10922657Scsgr sep->se_fd = -1; 109342122Sdes (void) sigsetmask(omask); 10942657Scsgr} 10952657Scsgr 10962657Scsgrvoid 109742122Sdesflag_retry(signo) 10981553Srgrimes int signo; 10991553Srgrimes{ 110042250Sdes flag_signal('A'); 110142122Sdes} 110242122Sdes 110342122Sdesvoid 110442122Sdesretry() 110542122Sdes{ 11061553Srgrimes struct servtab *sep; 11071553Srgrimes 11081553Srgrimes timingout = 0; 11091553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 111019617Sjulian if (sep->se_fd == -1 && !ISMUX(sep)) 11111553Srgrimes setup(sep); 11121553Srgrimes} 11131553Srgrimes 11141553Srgrimesvoid 11151553Srgrimessetup(sep) 11161553Srgrimes struct servtab *sep; 11171553Srgrimes{ 11181553Srgrimes int on = 1; 11191553Srgrimes 112056590Sshin if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { 11211553Srgrimes if (debug) 112229602Scharnier warn("socket failed on %s/%s", 112329602Scharnier sep->se_service, sep->se_proto); 11241553Srgrimes syslog(LOG_ERR, "%s/%s: socket: %m", 11251553Srgrimes sep->se_service, sep->se_proto); 11261553Srgrimes return; 11271553Srgrimes } 11281553Srgrimes#define turnon(fd, opt) \ 11291553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) 11301553Srgrimes if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && 11311553Srgrimes turnon(sep->se_fd, SO_DEBUG) < 0) 11321553Srgrimes syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 11331553Srgrimes if (turnon(sep->se_fd, SO_REUSEADDR) < 0) 11341553Srgrimes syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); 113525253Swollman#ifdef SO_PRIVSTATE 113613956Swollman if (turnon(sep->se_fd, SO_PRIVSTATE) < 0) 113713956Swollman syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m"); 113825253Swollman#endif 113956590Sshin /* tftpd opens a new connection then needs more infos */ 114056590Sshin if ((sep->se_family == AF_INET6) && 114156590Sshin (strcmp(sep->se_proto, "udp") == 0) && 114256590Sshin (sep->se_accept == 0) && 114356590Sshin (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_PKTINFO, 114456590Sshin (char *)&on, sizeof (on)) < 0)) 114556590Sshin syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m"); 114656590Sshin#ifdef IPV6_BINDV6ONLY 114758935Sume if (sep->se_family == AF_INET6) { 114858935Sume int flag = sep->se_nomapped ? 1 : 0; 114958935Sume if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_BINDV6ONLY, 115058935Sume (char *)&flag, sizeof (flag)) < 0) 115158935Sume syslog(LOG_ERR, "setsockopt (IPV6_BINDV6ONLY): %m"); 115258935Sume } 115356590Sshin#endif /* IPV6_BINDV6ONLY */ 11541553Srgrimes#undef turnon 115536042Sguido if (sep->se_type == TTCP_TYPE) 115636042Sguido if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH, 115736042Sguido (char *)&on, sizeof (on)) < 0) 115836042Sguido syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m"); 115956590Sshin#ifdef IPV6_FAITH 116056590Sshin if (sep->se_type == FAITH_TYPE) { 116156590Sshin if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH, &on, 116256590Sshin sizeof(on)) < 0) { 116356590Sshin syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m"); 116456590Sshin } 116556590Sshin } 116656590Sshin#endif 116756590Sshin#ifdef IPSEC 116856590Sshin ipsecsetup(sep); 116956590Sshin#endif 11701553Srgrimes if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, 117156590Sshin sep->se_ctrladdr_size) < 0) { 11721553Srgrimes if (debug) 117329602Scharnier warn("bind failed on %s/%s", 117429602Scharnier sep->se_service, sep->se_proto); 11751553Srgrimes syslog(LOG_ERR, "%s/%s: bind: %m", 11761553Srgrimes sep->se_service, sep->se_proto); 11771553Srgrimes (void) close(sep->se_fd); 11781553Srgrimes sep->se_fd = -1; 11791553Srgrimes if (!timingout) { 11801553Srgrimes timingout = 1; 11811553Srgrimes alarm(RETRYTIME); 11821553Srgrimes } 11831553Srgrimes return; 11841553Srgrimes } 11852657Scsgr if (sep->se_rpc) { 118656590Sshin int i, len = sep->se_ctrladdr_size; 11872657Scsgr 118856590Sshin if (sep->se_family != AF_INET) { 118956590Sshin syslog(LOG_ERR, 119056590Sshin "%s/%s: unsupported address family for rpc", 119156590Sshin sep->se_service, sep->se_proto); 119256590Sshin (void) close(sep->se_fd); 119356590Sshin sep->se_fd = -1; 119456590Sshin return; 119556590Sshin } 11968857Srgrimes if (getsockname(sep->se_fd, 11972657Scsgr (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){ 11982657Scsgr syslog(LOG_ERR, "%s/%s: getsockname: %m", 11992657Scsgr sep->se_service, sep->se_proto); 12002657Scsgr (void) close(sep->se_fd); 12012657Scsgr sep->se_fd = -1; 12028857Srgrimes return; 12032657Scsgr } 12042657Scsgr if (debug) 12052657Scsgr print_service("REG ", sep); 12062657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { 12072657Scsgr pmap_unset(sep->se_rpc_prog, i); 12082657Scsgr pmap_set(sep->se_rpc_prog, i, 12092657Scsgr (sep->se_socktype == SOCK_DGRAM) 12102657Scsgr ? IPPROTO_UDP : IPPROTO_TCP, 121156590Sshin ntohs(sep->se_ctrladdr4.sin_port)); 12122657Scsgr } 12132657Scsgr } 12141553Srgrimes if (sep->se_socktype == SOCK_STREAM) 121517197Sdg listen(sep->se_fd, 64); 121619618Sjulian enable(sep); 12171553Srgrimes if (debug) { 121829602Scharnier warnx("registered %s on %d", 12191553Srgrimes sep->se_server, sep->se_fd); 12201553Srgrimes } 12211553Srgrimes} 12221553Srgrimes 122356590Sshin#ifdef IPSEC 122456590Sshinvoid 122556590Sshinipsecsetup(sep) 122656590Sshin struct servtab *sep; 122756590Sshin{ 122856590Sshin char *buf; 122956590Sshin char *policy_in = NULL; 123056590Sshin char *policy_out = NULL; 123156590Sshin int level; 123256590Sshin int opt; 123356590Sshin 123456590Sshin switch (sep->se_family) { 123556590Sshin case AF_INET: 123656590Sshin level = IPPROTO_IP; 123756590Sshin opt = IP_IPSEC_POLICY; 123856590Sshin break; 123956590Sshin#ifdef INET6 124056590Sshin case AF_INET6: 124156590Sshin level = IPPROTO_IPV6; 124256590Sshin opt = IPV6_IPSEC_POLICY; 124356590Sshin break; 124456590Sshin#endif 124556590Sshin default: 124656590Sshin return; 124756590Sshin } 124856590Sshin 124956590Sshin if (!sep->se_policy || sep->se_policy[0] == '\0') { 125056590Sshin policy_in = "in entrust"; 125156590Sshin policy_out = "out entrust"; 125256590Sshin } else { 125356590Sshin if (!strncmp("in", sep->se_policy, 2)) 125456590Sshin policy_in = sep->se_policy; 125556590Sshin else if (!strncmp("out", sep->se_policy, 3)) 125656590Sshin policy_out = sep->se_policy; 125756590Sshin else { 125856590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 125956590Sshin sep->se_policy); 126056590Sshin return; 126156590Sshin } 126256590Sshin } 126356590Sshin 126456590Sshin if (policy_in != NULL) { 126556590Sshin buf = ipsec_set_policy(policy_in, strlen(policy_in)); 126656590Sshin if (buf != NULL) { 126756590Sshin if (setsockopt(sep->se_fd, level, opt, 126856675Sshin buf, ipsec_get_policylen(buf)) < 0 && 126956759Sshin debug != 0) 127056759Sshin warnx("%s/%s: ipsec initialization failed; %s", 127156759Sshin sep->se_service, sep->se_proto, 127256759Sshin policy_in); 127356590Sshin free(buf); 127456590Sshin } else 127556590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 127656590Sshin policy_in); 127756590Sshin } 127856590Sshin if (policy_out != NULL) { 127956590Sshin buf = ipsec_set_policy(policy_out, strlen(policy_out)); 128056590Sshin if (buf != NULL) { 128156590Sshin if (setsockopt(sep->se_fd, level, opt, 128256675Sshin buf, ipsec_get_policylen(buf)) < 0 && 128356759Sshin debug != 0) 128456759Sshin warnx("%s/%s: ipsec initialization failed; %s", 128556759Sshin sep->se_service, sep->se_proto, 128656759Sshin policy_out); 128756590Sshin free(buf); 128856590Sshin } else 128956590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 129056590Sshin policy_out); 129156590Sshin } 129256590Sshin} 129356590Sshin#endif 129456590Sshin 12951553Srgrimes/* 12961553Srgrimes * Finish with a service and its socket. 12971553Srgrimes */ 12981553Srgrimesvoid 12991553Srgrimesclose_sep(sep) 13001553Srgrimes struct servtab *sep; 13011553Srgrimes{ 13021553Srgrimes if (sep->se_fd >= 0) { 130319618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 130419618Sjulian disable(sep); 13051553Srgrimes (void) close(sep->se_fd); 13061553Srgrimes sep->se_fd = -1; 13071553Srgrimes } 13081553Srgrimes sep->se_count = 0; 130919618Sjulian sep->se_numchild = 0; /* forget about any existing children */ 13101553Srgrimes} 13111553Srgrimes 131248467Ssheldonhint 131348467Ssheldonhmatchservent(name1, name2, proto) 131448467Ssheldonh char *name1, *name2, *proto; 131548467Ssheldonh{ 131648467Ssheldonh char **alias; 131748467Ssheldonh struct servent *se; 131848467Ssheldonh 131949026Sdes if (strcmp(name1, name2) == 0) 132049026Sdes return(1); 132148467Ssheldonh if ((se = getservbyname(name1, proto)) != NULL) { 132248467Ssheldonh if (strcmp(name2, se->s_name) == 0) 132348467Ssheldonh return(1); 132448467Ssheldonh for (alias = se->s_aliases; *alias; alias++) 132548467Ssheldonh if (strcmp(name2, *alias) == 0) 132648467Ssheldonh return(1); 132748467Ssheldonh } 132848467Ssheldonh return(0); 132948467Ssheldonh} 133048467Ssheldonh 13311553Srgrimesstruct servtab * 13321553Srgrimesenter(cp) 13331553Srgrimes struct servtab *cp; 13341553Srgrimes{ 13351553Srgrimes struct servtab *sep; 133642122Sdes long omask; 13371553Srgrimes 13381553Srgrimes sep = (struct servtab *)malloc(sizeof (*sep)); 13391553Srgrimes if (sep == (struct servtab *)0) { 134049102Ssheldonh syslog(LOG_ERR, "malloc: %m"); 134119617Sjulian exit(EX_OSERR); 13421553Srgrimes } 13431553Srgrimes *sep = *cp; 13441553Srgrimes sep->se_fd = -1; 134542122Sdes omask = sigblock(SIGBLOCK); 13461553Srgrimes sep->se_next = servtab; 13471553Srgrimes servtab = sep; 134842122Sdes sigsetmask(omask); 13491553Srgrimes return (sep); 13501553Srgrimes} 13511553Srgrimes 135219618Sjulianvoid 135319618Sjulianenable(struct servtab *sep) 135419618Sjulian{ 135519618Sjulian if (debug) 135629602Scharnier warnx( 135719618Sjulian "enabling %s, fd %d", sep->se_service, sep->se_fd); 135819618Sjulian#ifdef SANITY_CHECK 135919618Sjulian if (sep->se_fd < 0) { 136019618Sjulian syslog(LOG_ERR, 136119618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 136219618Sjulian exit(EX_SOFTWARE); 136319618Sjulian } 136419618Sjulian if (ISMUX(sep)) { 136519618Sjulian syslog(LOG_ERR, 136619618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 136719618Sjulian exit(EX_SOFTWARE); 136819618Sjulian } 136919618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) { 137019618Sjulian syslog(LOG_ERR, 137119618Sjulian "%s: %s: not off", __FUNCTION__, sep->se_service); 137219618Sjulian exit(EX_SOFTWARE); 137319618Sjulian } 137448991Ssheldonh nsock++; 137519618Sjulian#endif 137619618Sjulian FD_SET(sep->se_fd, &allsock); 137719618Sjulian if (sep->se_fd > maxsock) 137819618Sjulian maxsock = sep->se_fd; 137919618Sjulian} 138019618Sjulian 138119618Sjulianvoid 138219618Sjuliandisable(struct servtab *sep) 138319618Sjulian{ 138419618Sjulian if (debug) 138529602Scharnier warnx( 138619618Sjulian "disabling %s, fd %d", sep->se_service, sep->se_fd); 138719618Sjulian#ifdef SANITY_CHECK 138819618Sjulian if (sep->se_fd < 0) { 138919618Sjulian syslog(LOG_ERR, 139019618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 139119618Sjulian exit(EX_SOFTWARE); 139219618Sjulian } 139319618Sjulian if (ISMUX(sep)) { 139419618Sjulian syslog(LOG_ERR, 139519618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 139619618Sjulian exit(EX_SOFTWARE); 139719618Sjulian } 139819618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) { 139919618Sjulian syslog(LOG_ERR, 140019618Sjulian "%s: %s: not on", __FUNCTION__, sep->se_service); 140119618Sjulian exit(EX_SOFTWARE); 140219618Sjulian } 140319618Sjulian if (nsock == 0) { 140419618Sjulian syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 140519618Sjulian exit(EX_SOFTWARE); 140619618Sjulian } 140748991Ssheldonh nsock--; 140819618Sjulian#endif 140919618Sjulian FD_CLR(sep->se_fd, &allsock); 141019618Sjulian if (sep->se_fd == maxsock) 141119618Sjulian maxsock--; 141219618Sjulian} 141319618Sjulian 14141553SrgrimesFILE *fconfig = NULL; 14151553Srgrimesstruct servtab serv; 14161553Srgrimeschar line[LINE_MAX]; 14171553Srgrimes 14181553Srgrimesint 14191553Srgrimessetconfig() 14201553Srgrimes{ 14211553Srgrimes 14221553Srgrimes if (fconfig != NULL) { 14231553Srgrimes fseek(fconfig, 0L, SEEK_SET); 14241553Srgrimes return (1); 14251553Srgrimes } 14261553Srgrimes fconfig = fopen(CONFIG, "r"); 14271553Srgrimes return (fconfig != NULL); 14281553Srgrimes} 14291553Srgrimes 14301553Srgrimesvoid 14311553Srgrimesendconfig() 14321553Srgrimes{ 14331553Srgrimes if (fconfig) { 14341553Srgrimes (void) fclose(fconfig); 14351553Srgrimes fconfig = NULL; 14361553Srgrimes } 14371553Srgrimes} 14381553Srgrimes 14391553Srgrimesstruct servtab * 14401553Srgrimesgetconfigent() 14411553Srgrimes{ 14421553Srgrimes struct servtab *sep = &serv; 14431553Srgrimes int argc; 144419618Sjulian char *cp, *arg, *s; 14452657Scsgr char *versp; 14461553Srgrimes static char TCPMUX_TOKEN[] = "tcpmux/"; 14471553Srgrimes#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1) 144856590Sshin#ifdef IPSEC 144956590Sshin char *policy = NULL; 145056590Sshin#endif 145156590Sshin int v4bind = 0; 145256590Sshin#ifdef INET6 145356590Sshin int v6bind = 0; 145456590Sshin#endif 14551553Srgrimes 14561553Srgrimesmore: 145756590Sshin while ((cp = nextline(fconfig)) != NULL) { 145856590Sshin#ifdef IPSEC 145956590Sshin /* lines starting with #@ is not a comment, but the policy */ 146056590Sshin if (cp[0] == '#' && cp[1] == '@') { 146156590Sshin char *p; 146256590Sshin for (p = cp + 2; p && *p && isspace(*p); p++) 146356590Sshin ; 146456590Sshin if (*p == '\0') { 146556590Sshin if (policy) 146656590Sshin free(policy); 146756590Sshin policy = NULL; 146856590Sshin } else if (ipsec_get_policylen(p) >= 0) { 146956590Sshin if (policy) 147056590Sshin free(policy); 147156590Sshin policy = newstr(p); 147256590Sshin } else { 147356590Sshin syslog(LOG_ERR, 147456590Sshin "%s: invalid ipsec policy \"%s\"", 147556590Sshin CONFIG, p); 147656590Sshin exit(EX_CONFIG); 147756590Sshin } 147856590Sshin } 147956590Sshin#endif 148056590Sshin if (*cp == '#' || *cp == '\0') 148156590Sshin continue; 148256590Sshin break; 148356590Sshin } 14841553Srgrimes if (cp == NULL) 14851553Srgrimes return ((struct servtab *)0); 14861553Srgrimes /* 14871553Srgrimes * clear the static buffer, since some fields (se_ctrladdr, 14881553Srgrimes * for example) don't get initialized here. 14891553Srgrimes */ 14901553Srgrimes memset((caddr_t)sep, 0, sizeof *sep); 14911553Srgrimes arg = skip(&cp); 14921553Srgrimes if (cp == NULL) { 14931553Srgrimes /* got an empty line containing just blanks/tabs. */ 14941553Srgrimes goto more; 14951553Srgrimes } 14961553Srgrimes if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { 14971553Srgrimes char *c = arg + MUX_LEN; 14981553Srgrimes if (*c == '+') { 14991553Srgrimes sep->se_type = MUXPLUS_TYPE; 15001553Srgrimes c++; 15011553Srgrimes } else 15021553Srgrimes sep->se_type = MUX_TYPE; 15031553Srgrimes sep->se_service = newstr(c); 15041553Srgrimes } else { 15051553Srgrimes sep->se_service = newstr(arg); 15061553Srgrimes sep->se_type = NORM_TYPE; 15071553Srgrimes } 15081553Srgrimes arg = sskip(&cp); 15091553Srgrimes if (strcmp(arg, "stream") == 0) 15101553Srgrimes sep->se_socktype = SOCK_STREAM; 15111553Srgrimes else if (strcmp(arg, "dgram") == 0) 15121553Srgrimes sep->se_socktype = SOCK_DGRAM; 15131553Srgrimes else if (strcmp(arg, "rdm") == 0) 15141553Srgrimes sep->se_socktype = SOCK_RDM; 15151553Srgrimes else if (strcmp(arg, "seqpacket") == 0) 15161553Srgrimes sep->se_socktype = SOCK_SEQPACKET; 15171553Srgrimes else if (strcmp(arg, "raw") == 0) 15181553Srgrimes sep->se_socktype = SOCK_RAW; 15191553Srgrimes else 15201553Srgrimes sep->se_socktype = -1; 152136042Sguido 152236042Sguido arg = sskip(&cp); 152356590Sshin if (strncmp(arg, "tcp", 3) == 0) { 152456590Sshin sep->se_proto = newstr(strsep(&arg, "/")); 152556590Sshin if (arg != NULL) { 152656590Sshin if (strcmp(arg, "ttcp") == 0) 152756590Sshin sep->se_type = TTCP_TYPE; 152856590Sshin else if (strcmp(arg, "faith") == 0) 152956590Sshin sep->se_type = FAITH_TYPE; 153056590Sshin } 153156590Sshin } else 153236042Sguido sep->se_proto = newstr(arg); 15332657Scsgr if (strncmp(sep->se_proto, "rpc/", 4) == 0) { 153456973Sshin if (no_v4bind != 0) { 153556973Sshin syslog(LOG_INFO, "IPv4 bind is ignored for %s", 153656973Sshin sep->se_service); 153756590Sshin freeconfig(sep); 153856590Sshin goto more; 153956590Sshin } 154019237Sjoerg memmove(sep->se_proto, sep->se_proto + 4, 154119237Sjoerg strlen(sep->se_proto) + 1 - 4); 15422657Scsgr sep->se_rpc = 1; 15432657Scsgr sep->se_rpc_prog = sep->se_rpc_lowvers = 15442657Scsgr sep->se_rpc_lowvers = 0; 154556590Sshin memcpy(&sep->se_ctrladdr4, bind_sa4, 154656590Sshin sizeof(sep->se_ctrladdr4)); 15472657Scsgr if ((versp = rindex(sep->se_service, '/'))) { 15482657Scsgr *versp++ = '\0'; 15492657Scsgr switch (sscanf(versp, "%d-%d", 15502657Scsgr &sep->se_rpc_lowvers, 15512657Scsgr &sep->se_rpc_highvers)) { 15522657Scsgr case 2: 15532657Scsgr break; 15542657Scsgr case 1: 15552657Scsgr sep->se_rpc_highvers = 15562657Scsgr sep->se_rpc_lowvers; 15572657Scsgr break; 15582657Scsgr default: 15598857Srgrimes syslog(LOG_ERR, 156052219Scharnier "bad RPC version specifier; %s", 15612657Scsgr sep->se_service); 15622657Scsgr freeconfig(sep); 15632657Scsgr goto more; 15642657Scsgr } 15652657Scsgr } 15662657Scsgr else { 15672657Scsgr sep->se_rpc_lowvers = 15682657Scsgr sep->se_rpc_highvers = 1; 15692657Scsgr } 15702657Scsgr } 157156590Sshin sep->se_nomapped = 0; 157256590Sshin while (isdigit(sep->se_proto[strlen(sep->se_proto) - 1])) { 157356590Sshin#ifdef INET6 157456590Sshin if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') { 157556590Sshin if (no_v6bind != 0) { 157656590Sshin syslog(LOG_INFO, "IPv6 bind is ignored for %s", 157756590Sshin sep->se_service); 157856590Sshin freeconfig(sep); 157956590Sshin goto more; 158056590Sshin } 158156590Sshin sep->se_proto[strlen(sep->se_proto) - 1] = '\0'; 158256590Sshin v6bind = 1; 158356590Sshin continue; 158456590Sshin } 158556590Sshin#endif 158656590Sshin if (sep->se_proto[strlen(sep->se_proto) - 1] == '4') { 158756590Sshin sep->se_proto[strlen(sep->se_proto) - 1] = '\0'; 158856590Sshin v4bind = 1; 158956590Sshin continue; 159056590Sshin } 159156590Sshin /* illegal version num */ 159256590Sshin syslog(LOG_ERR, "bad IP version for %s", sep->se_proto); 159356590Sshin freeconfig(sep); 159456590Sshin goto more; 159556590Sshin } 159656590Sshin#ifdef INET6 159756590Sshin if (v6bind != 0) { 159856590Sshin sep->se_family = AF_INET6; 159956590Sshin if (v4bind == 0 || no_v4bind != 0) 160056590Sshin sep->se_nomapped = 1; 160157906Sshin } else 160256590Sshin#endif 160357906Sshin { /* default to v4 bind if not v6 bind */ 160456590Sshin if (no_v4bind != 0) { 160556590Sshin syslog(LOG_INFO, "IPv4 bind is ignored for %s", 160656590Sshin sep->se_service); 160756590Sshin freeconfig(sep); 160856590Sshin goto more; 160956590Sshin } 161056590Sshin sep->se_family = AF_INET; 161156590Sshin } 161256590Sshin /* init ctladdr */ 161356590Sshin switch(sep->se_family) { 161456590Sshin case AF_INET: 161556590Sshin memcpy(&sep->se_ctrladdr4, bind_sa4, 161656590Sshin sizeof(sep->se_ctrladdr4)); 161756590Sshin sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr4); 161856590Sshin sep->se_ctladdrinitok = 1; 161956590Sshin break; 162056590Sshin#ifdef INET6 162156590Sshin case AF_INET6: 162256590Sshin memcpy(&sep->se_ctrladdr6, bind_sa6, 162356590Sshin sizeof(sep->se_ctrladdr6)); 162456590Sshin sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr6); 162556590Sshin sep->se_ctladdrinitok = 1; 162656590Sshin break; 162756590Sshin#endif 162856590Sshin } 16291553Srgrimes arg = sskip(&cp); 163019618Sjulian if (!strncmp(arg, "wait", 4)) 163119618Sjulian sep->se_accept = 0; 163219618Sjulian else if (!strncmp(arg, "nowait", 6)) 163319618Sjulian sep->se_accept = 1; 163419618Sjulian else { 163519618Sjulian syslog(LOG_ERR, 163619618Sjulian "%s: bad wait/nowait for service %s", 163719618Sjulian CONFIG, sep->se_service); 163819618Sjulian goto more; 163919618Sjulian } 164048069Ssheldonh sep->se_maxchild = -1; 164148069Ssheldonh sep->se_maxcpm = -1; 164219618Sjulian if ((s = strchr(arg, '/')) != NULL) { 164319618Sjulian char *eptr; 164419618Sjulian u_long val; 164519618Sjulian 164619618Sjulian val = strtoul(s + 1, &eptr, 10); 164730847Sdima if (eptr == s + 1 || val > MAX_MAXCHLD) { 164819618Sjulian syslog(LOG_ERR, 164919618Sjulian "%s: bad max-child for service %s", 165019618Sjulian CONFIG, sep->se_service); 165119618Sjulian goto more; 165219618Sjulian } 165348069Ssheldonh if (debug) 165448069Ssheldonh if (!sep->se_accept && val != 1) 165548069Ssheldonh warnx("maxchild=%lu for wait service %s" 165648069Ssheldonh " not recommended", val, sep->se_service); 165719618Sjulian sep->se_maxchild = val; 165830847Sdima if (*eptr == '/') 165930847Sdima sep->se_maxcpm = strtol(eptr + 1, &eptr, 10); 166030847Sdima /* 166130847Sdima * explicitly do not check for \0 for future expansion / 166230847Sdima * backwards compatibility 166330847Sdima */ 166419618Sjulian } 16651553Srgrimes if (ISMUX(sep)) { 16661553Srgrimes /* 166719618Sjulian * Silently enforce "nowait" mode for TCPMUX services 166819618Sjulian * since they don't have an assigned port to listen on. 16691553Srgrimes */ 167019618Sjulian sep->se_accept = 1; 16711553Srgrimes if (strcmp(sep->se_proto, "tcp")) { 16728857Srgrimes syslog(LOG_ERR, 16731553Srgrimes "%s: bad protocol for tcpmux service %s", 16741553Srgrimes CONFIG, sep->se_service); 16751553Srgrimes goto more; 16761553Srgrimes } 16771553Srgrimes if (sep->se_socktype != SOCK_STREAM) { 16788857Srgrimes syslog(LOG_ERR, 16791553Srgrimes "%s: bad socket type for tcpmux service %s", 16801553Srgrimes CONFIG, sep->se_service); 16811553Srgrimes goto more; 16821553Srgrimes } 16831553Srgrimes } 16841553Srgrimes sep->se_user = newstr(sskip(&cp)); 168530792Sache#ifdef LOGIN_CAP 168630792Sache if ((s = strrchr(sep->se_user, '/')) != NULL) { 168730792Sache *s = '\0'; 168830792Sache sep->se_class = newstr(s + 1); 168930792Sache } else 169030792Sache sep->se_class = newstr(RESOURCE_RC); 169130792Sache#endif 169230807Sache if ((s = strrchr(sep->se_user, ':')) != NULL) { 169330807Sache *s = '\0'; 169430807Sache sep->se_group = newstr(s + 1); 169530807Sache } else 169630807Sache sep->se_group = NULL; 16971553Srgrimes sep->se_server = newstr(sskip(&cp)); 169845588Smarkm if ((sep->se_server_name = rindex(sep->se_server, '/'))) 169945588Smarkm sep->se_server_name++; 17001553Srgrimes if (strcmp(sep->se_server, "internal") == 0) { 17011553Srgrimes struct biltin *bi; 17021553Srgrimes 17031553Srgrimes for (bi = biltins; bi->bi_service; bi++) 170449026Sdes if (bi->bi_socktype == sep->se_socktype && 170548467Ssheldonh matchservent(bi->bi_service, sep->se_service, 170648467Ssheldonh sep->se_proto)) 17071553Srgrimes break; 17081553Srgrimes if (bi->bi_service == 0) { 17091553Srgrimes syslog(LOG_ERR, "internal service %s unknown", 17101553Srgrimes sep->se_service); 17111553Srgrimes goto more; 17121553Srgrimes } 171319618Sjulian sep->se_accept = 1; /* force accept mode for built-ins */ 17141553Srgrimes sep->se_bi = bi; 17151553Srgrimes } else 17161553Srgrimes sep->se_bi = NULL; 171748069Ssheldonh if (sep->se_maxcpm < 0) 171848069Ssheldonh sep->se_maxcpm = maxcpm; 171945588Smarkm if (sep->se_maxchild < 0) { /* apply default max-children */ 172048069Ssheldonh if (sep->se_bi && sep->se_bi->bi_maxchild >= 0) 172119618Sjulian sep->se_maxchild = sep->se_bi->bi_maxchild; 172248069Ssheldonh else if (sep->se_accept) 172348069Ssheldonh sep->se_maxchild = maxchild > 0 ? maxchild : 0; 172419618Sjulian else 172548069Ssheldonh sep->se_maxchild = 1; 172645588Smarkm } 172764197Sdwmalone if (sep->se_maxchild > 0) { 172819618Sjulian sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); 172919618Sjulian if (sep->se_pids == NULL) { 173049102Ssheldonh syslog(LOG_ERR, "malloc: %m"); 173119618Sjulian exit(EX_OSERR); 173219618Sjulian } 173319618Sjulian } 17341553Srgrimes argc = 0; 17351553Srgrimes for (arg = skip(&cp); cp; arg = skip(&cp)) 173619618Sjulian if (argc < MAXARGV) { 17371553Srgrimes sep->se_argv[argc++] = newstr(arg); 173819618Sjulian } else { 173919618Sjulian syslog(LOG_ERR, 174019618Sjulian "%s: too many arguments for service %s", 174119618Sjulian CONFIG, sep->se_service); 174219618Sjulian goto more; 174319618Sjulian } 17441553Srgrimes while (argc <= MAXARGV) 17451553Srgrimes sep->se_argv[argc++] = NULL; 174656590Sshin#ifdef IPSEC 174756590Sshin sep->se_policy = policy ? newstr(policy) : NULL; 174856590Sshin#endif 17491553Srgrimes return (sep); 17501553Srgrimes} 17511553Srgrimes 17521553Srgrimesvoid 17531553Srgrimesfreeconfig(cp) 17541553Srgrimes struct servtab *cp; 17551553Srgrimes{ 17561553Srgrimes int i; 17571553Srgrimes 17581553Srgrimes if (cp->se_service) 17591553Srgrimes free(cp->se_service); 17601553Srgrimes if (cp->se_proto) 17611553Srgrimes free(cp->se_proto); 17621553Srgrimes if (cp->se_user) 17631553Srgrimes free(cp->se_user); 176430807Sache if (cp->se_group) 176530807Sache free(cp->se_group); 176630792Sache#ifdef LOGIN_CAP 176730792Sache if (cp->se_class) 176830792Sache free(cp->se_class); 176930792Sache#endif 17701553Srgrimes if (cp->se_server) 17711553Srgrimes free(cp->se_server); 177219618Sjulian if (cp->se_pids) 177319618Sjulian free(cp->se_pids); 17741553Srgrimes for (i = 0; i < MAXARGV; i++) 17751553Srgrimes if (cp->se_argv[i]) 17761553Srgrimes free(cp->se_argv[i]); 177756590Sshin#ifdef IPSEC 177856590Sshin if (cp->se_policy) 177956590Sshin free(cp->se_policy); 178056590Sshin#endif 17811553Srgrimes} 17821553Srgrimes 17831553Srgrimes 17841553Srgrimes/* 17851553Srgrimes * Safe skip - if skip returns null, log a syntax error in the 17861553Srgrimes * configuration file and exit. 17871553Srgrimes */ 17881553Srgrimeschar * 17891553Srgrimessskip(cpp) 17901553Srgrimes char **cpp; 17911553Srgrimes{ 17921553Srgrimes char *cp; 17931553Srgrimes 17941553Srgrimes cp = skip(cpp); 17951553Srgrimes if (cp == NULL) { 17961553Srgrimes syslog(LOG_ERR, "%s: syntax error", CONFIG); 179719617Sjulian exit(EX_DATAERR); 17981553Srgrimes } 17991553Srgrimes return (cp); 18001553Srgrimes} 18011553Srgrimes 18021553Srgrimeschar * 18031553Srgrimesskip(cpp) 18041553Srgrimes char **cpp; 18051553Srgrimes{ 18061553Srgrimes char *cp = *cpp; 18071553Srgrimes char *start; 180811933Sadam char quote = '\0'; 18091553Srgrimes 18101553Srgrimesagain: 18111553Srgrimes while (*cp == ' ' || *cp == '\t') 18121553Srgrimes cp++; 18131553Srgrimes if (*cp == '\0') { 18141553Srgrimes int c; 18151553Srgrimes 18161553Srgrimes c = getc(fconfig); 18171553Srgrimes (void) ungetc(c, fconfig); 18181553Srgrimes if (c == ' ' || c == '\t') 181919617Sjulian if ((cp = nextline(fconfig))) 18201553Srgrimes goto again; 18211553Srgrimes *cpp = (char *)0; 18221553Srgrimes return ((char *)0); 18231553Srgrimes } 182411933Sadam if (*cp == '"' || *cp == '\'') 182511933Sadam quote = *cp++; 18261553Srgrimes start = cp; 182711933Sadam if (quote) 182811933Sadam while (*cp && *cp != quote) 182911933Sadam cp++; 183011933Sadam else 183111933Sadam while (*cp && *cp != ' ' && *cp != '\t') 183211933Sadam cp++; 18331553Srgrimes if (*cp != '\0') 18341553Srgrimes *cp++ = '\0'; 18351553Srgrimes *cpp = cp; 18361553Srgrimes return (start); 18371553Srgrimes} 18381553Srgrimes 18391553Srgrimeschar * 18401553Srgrimesnextline(fd) 18411553Srgrimes FILE *fd; 18421553Srgrimes{ 18431553Srgrimes char *cp; 18441553Srgrimes 18451553Srgrimes if (fgets(line, sizeof (line), fd) == NULL) 18461553Srgrimes return ((char *)0); 18471553Srgrimes cp = strchr(line, '\n'); 18481553Srgrimes if (cp) 18491553Srgrimes *cp = '\0'; 18501553Srgrimes return (line); 18511553Srgrimes} 18521553Srgrimes 18531553Srgrimeschar * 18541553Srgrimesnewstr(cp) 18551553Srgrimes char *cp; 18561553Srgrimes{ 185719617Sjulian if ((cp = strdup(cp ? cp : ""))) 18581553Srgrimes return (cp); 18591553Srgrimes syslog(LOG_ERR, "strdup: %m"); 186019617Sjulian exit(EX_OSERR); 18611553Srgrimes} 18621553Srgrimes 186313142Speter#ifdef OLD_SETPROCTITLE 18641553Srgrimesvoid 186513142Speterinetd_setproctitle(a, s) 18661553Srgrimes char *a; 18671553Srgrimes int s; 18681553Srgrimes{ 18691553Srgrimes int size; 18701553Srgrimes char *cp; 187156590Sshin struct sockaddr_storage ss; 187256590Sshin char buf[80], pbuf[INET6_ADDRSTRLEN]; 18731553Srgrimes 18741553Srgrimes cp = Argv[0]; 187556590Sshin size = sizeof(ss); 187656590Sshin if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) { 187756590Sshin getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf), 187856590Sshin NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); 187956590Sshin (void) sprintf(buf, "-%s [%s]", a, pbuf); 188056590Sshin } else 18818857Srgrimes (void) sprintf(buf, "-%s", a); 18821553Srgrimes strncpy(cp, buf, LastArg - cp); 18831553Srgrimes cp += strlen(cp); 18841553Srgrimes while (cp < LastArg) 18851553Srgrimes *cp++ = ' '; 18861553Srgrimes} 188713142Speter#else 188813142Spetervoid 188913142Speterinetd_setproctitle(a, s) 189013142Speter char *a; 189113142Speter int s; 189213142Speter{ 189313142Speter int size; 189456590Sshin struct sockaddr_storage ss; 189556590Sshin char buf[80], pbuf[INET6_ADDRSTRLEN]; 18961553Srgrimes 189756590Sshin size = sizeof(ss); 189856590Sshin if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) { 189956590Sshin getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf), 190056590Sshin NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); 190156590Sshin (void) sprintf(buf, "%s [%s]", a, pbuf); 190256590Sshin } else 190313142Speter (void) sprintf(buf, "%s", a); 190413142Speter setproctitle("%s", buf); 190513142Speter} 190613142Speter#endif 190713142Speter 190813142Speter 19091553Srgrimes/* 19101553Srgrimes * Internet services provided internally by inetd: 19111553Srgrimes */ 19121553Srgrimes 191356590Sshinint check_loop(sa, sep) 191456590Sshin struct sockaddr *sa; 19155182Swollman struct servtab *sep; 19165182Swollman{ 19175182Swollman struct servtab *se2; 191856590Sshin char pname[INET6_ADDRSTRLEN]; 19195182Swollman 19205182Swollman for (se2 = servtab; se2; se2 = se2->se_next) { 19215182Swollman if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM) 19225182Swollman continue; 19235182Swollman 192456590Sshin switch (se2->se_family) { 192556590Sshin case AF_INET: 192656590Sshin if (((struct sockaddr_in *)sa)->sin_port == 192756590Sshin se2->se_ctrladdr4.sin_port) 192856590Sshin goto isloop; 192956590Sshin continue; 193056590Sshin#ifdef INET6 193156590Sshin case AF_INET6: 193256590Sshin if (((struct sockaddr_in *)sa)->sin_port == 193356590Sshin se2->se_ctrladdr4.sin_port) 193456590Sshin goto isloop; 193556590Sshin continue; 193656590Sshin#endif 193756590Sshin default: 193856590Sshin continue; 19395182Swollman } 194056590Sshin isloop: 194156590Sshin getnameinfo(sa, sa->sa_len, pname, sizeof(pname), NULL, 0, 194256590Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 194356590Sshin syslog(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s", 194456590Sshin sep->se_service, sep->se_proto, 194556590Sshin se2->se_service, se2->se_proto, 194656590Sshin pname); 194756590Sshin return 1; 19485182Swollman } 19495182Swollman return 0; 19505182Swollman} 19515182Swollman 19521553Srgrimes/* 19531553Srgrimes * print_service: 19541553Srgrimes * Dump relevant information to stderr 19551553Srgrimes */ 19561553Srgrimesvoid 19571553Srgrimesprint_service(action, sep) 19581553Srgrimes char *action; 19591553Srgrimes struct servtab *sep; 19601553Srgrimes{ 196119617Sjulian fprintf(stderr, 196256590Sshin "%s: %s proto=%s accept=%d max=%d user=%s group=%s" 196330792Sache#ifdef LOGIN_CAP 196456590Sshin "class=%s" 196530792Sache#endif 196656590Sshin " builtin=%p server=%s" 196756590Sshin#ifdef IPSEC 196856590Sshin " policy=\"%s\"" 196956590Sshin#endif 197056590Sshin "\n", 197119617Sjulian action, sep->se_service, sep->se_proto, 197230807Sache sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group, 197330792Sache#ifdef LOGIN_CAP 197430792Sache sep->se_class, 197530792Sache#endif 197656590Sshin (void *) sep->se_bi, sep->se_server 197756590Sshin#ifdef IPSEC 197856590Sshin , (sep->se_policy ? sep->se_policy : "") 197956590Sshin#endif 198056590Sshin ); 19811553Srgrimes} 19821553Srgrimes 198330847Sdima#define CPMHSIZE 256 198430847Sdima#define CPMHMASK (CPMHSIZE-1) 198530847Sdima#define CHTGRAN 10 198630847Sdima#define CHTSIZE 6 198730847Sdima 198830847Sdimatypedef struct CTime { 198930847Sdima unsigned long ct_Ticks; 199030847Sdima int ct_Count; 199130847Sdima} CTime; 199230847Sdima 199330847Sdimatypedef struct CHash { 199456590Sshin union { 199556590Sshin struct in_addr c4_Addr; 199656590Sshin struct in6_addr c6_Addr; 199756590Sshin } cu_Addr; 199856590Sshin#define ch_Addr4 cu_Addr.c4_Addr 199956590Sshin#define ch_Addr6 cu_Addr.c6_Addr 200056590Sshin int ch_Family; 200130847Sdima time_t ch_LTime; 200230847Sdima char *ch_Service; 200330847Sdima CTime ch_Times[CHTSIZE]; 200430847Sdima} CHash; 200530847Sdima 200630847SdimaCHash CHashAry[CPMHSIZE]; 200730847Sdima 200830847Sdimaint 200930847Sdimacpmip(sep, ctrl) 201030847Sdima struct servtab *sep; 201130847Sdima int ctrl; 201230847Sdima{ 201356590Sshin struct sockaddr_storage rss; 201456590Sshin int rssLen = sizeof(rss); 201530847Sdima int r = 0; 201630847Sdima 201730847Sdima /* 201830847Sdima * If getpeername() fails, just let it through (if logging is 201930847Sdima * enabled the condition is caught elsewhere) 202030847Sdima */ 202130847Sdima 202230847Sdima if (sep->se_maxcpm > 0 && 202356590Sshin getpeername(ctrl, (struct sockaddr *)&rss, &rssLen) == 0 ) { 202430847Sdima time_t t = time(NULL); 202530847Sdima int hv = 0xABC3D20F; 202630847Sdima int i; 202730847Sdima int cnt = 0; 202830847Sdima CHash *chBest = NULL; 202930847Sdima unsigned int ticks = t / CHTGRAN; 203056590Sshin struct sockaddr_in *sin; 203156590Sshin#ifdef INET6 203256590Sshin struct sockaddr_in6 *sin6; 203356590Sshin#endif 203430847Sdima 203556590Sshin sin = (struct sockaddr_in *)&rss; 203656590Sshin#ifdef INET6 203756590Sshin sin6 = (struct sockaddr_in6 *)&rss; 203856590Sshin#endif 203930847Sdima { 204030847Sdima char *p; 204156590Sshin int i, addrlen; 204230847Sdima 204356590Sshin switch (rss.ss_family) { 204456590Sshin case AF_INET: 204556590Sshin p = (char *)&sin->sin_addr; 204656590Sshin addrlen = sizeof(struct in_addr); 204756590Sshin break; 204856590Sshin#ifdef INET6 204956590Sshin case AF_INET6: 205056590Sshin p = (char *)&sin6->sin6_addr; 205156590Sshin addrlen = sizeof(struct in6_addr); 205256590Sshin break; 205356590Sshin#endif 205456590Sshin default: 205556590Sshin /* should not happen */ 205656590Sshin return -1; 205756590Sshin } 205856590Sshin 205956590Sshin for (i = 0; i < addrlen; ++i, ++p) { 206030847Sdima hv = (hv << 5) ^ (hv >> 23) ^ *p; 206130847Sdima } 206230847Sdima hv = (hv ^ (hv >> 16)); 206330847Sdima } 206430847Sdima for (i = 0; i < 5; ++i) { 206530847Sdima CHash *ch = &CHashAry[(hv + i) & CPMHMASK]; 206630847Sdima 206756590Sshin if (rss.ss_family == AF_INET && 206856590Sshin ch->ch_Family == AF_INET && 206956590Sshin sin->sin_addr.s_addr == ch->ch_Addr4.s_addr && 207030847Sdima ch->ch_Service && strcmp(sep->se_service, 207130847Sdima ch->ch_Service) == 0) { 207230847Sdima chBest = ch; 207330847Sdima break; 207430847Sdima } 207556590Sshin#ifdef INET6 207656590Sshin if (rss.ss_family == AF_INET6 && 207756590Sshin ch->ch_Family == AF_INET6 && 207856590Sshin IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 207956590Sshin &ch->ch_Addr6) != 0 && 208056590Sshin ch->ch_Service && strcmp(sep->se_service, 208156590Sshin ch->ch_Service) == 0) { 208256590Sshin chBest = ch; 208356590Sshin break; 208456590Sshin } 208556590Sshin#endif 208630847Sdima if (chBest == NULL || ch->ch_LTime == 0 || 208730847Sdima ch->ch_LTime < chBest->ch_LTime) { 208830847Sdima chBest = ch; 208930847Sdima } 209030847Sdima } 209156590Sshin if ((rss.ss_family == AF_INET && 209256590Sshin (chBest->ch_Family != AF_INET || 209356590Sshin sin->sin_addr.s_addr != chBest->ch_Addr4.s_addr)) || 209430847Sdima chBest->ch_Service == NULL || 209530847Sdima strcmp(sep->se_service, chBest->ch_Service) != 0) { 209656590Sshin chBest->ch_Family = sin->sin_family; 209756590Sshin chBest->ch_Addr4 = sin->sin_addr; 209830847Sdima if (chBest->ch_Service) 209930847Sdima free(chBest->ch_Service); 210030847Sdima chBest->ch_Service = strdup(sep->se_service); 210130847Sdima bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 210230847Sdima } 210356590Sshin#ifdef INET6 210456590Sshin if ((rss.ss_family == AF_INET6 && 210556590Sshin (chBest->ch_Family != AF_INET6 || 210656590Sshin IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 210756590Sshin &chBest->ch_Addr6) == 0)) || 210856590Sshin chBest->ch_Service == NULL || 210956590Sshin strcmp(sep->se_service, chBest->ch_Service) != 0) { 211056590Sshin chBest->ch_Family = sin6->sin6_family; 211156590Sshin chBest->ch_Addr6 = sin6->sin6_addr; 211256590Sshin if (chBest->ch_Service) 211356590Sshin free(chBest->ch_Service); 211456590Sshin chBest->ch_Service = strdup(sep->se_service); 211556590Sshin bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 211656590Sshin } 211756590Sshin#endif 211830847Sdima chBest->ch_LTime = t; 211930847Sdima { 212030847Sdima CTime *ct = &chBest->ch_Times[ticks % CHTSIZE]; 212130847Sdima if (ct->ct_Ticks != ticks) { 212230847Sdima ct->ct_Ticks = ticks; 212330847Sdima ct->ct_Count = 0; 212430847Sdima } 212530847Sdima ++ct->ct_Count; 212630847Sdima } 212730847Sdima for (i = 0; i < CHTSIZE; ++i) { 212830847Sdima CTime *ct = &chBest->ch_Times[i]; 212930847Sdima if (ct->ct_Ticks <= ticks && 213030847Sdima ct->ct_Ticks >= ticks - CHTSIZE) { 213130847Sdima cnt += ct->ct_Count; 213230847Sdima } 213330847Sdima } 213430847Sdima if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) { 213556590Sshin char pname[INET6_ADDRSTRLEN]; 213656590Sshin 213756590Sshin getnameinfo((struct sockaddr *)&rss, 213856590Sshin ((struct sockaddr *)&rss)->sa_len, 213956590Sshin pname, sizeof(pname), NULL, 0, 214056590Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 214130847Sdima r = -1; 214230847Sdima syslog(LOG_ERR, 214333794Spst "%s from %s exceeded counts/min (limit %d/min)", 214456590Sshin sep->se_service, pname, 214533794Spst sep->se_maxcpm); 214630847Sdima } 214730847Sdima } 214830847Sdima return(r); 214930847Sdima} 2150