inetd.c revision 45089
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[] = 4545089Smarkm "$Id: inetd.c,v 1.46 1999/01/05 11:56:35 danny Exp $"; 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. 1051553Srgrimes */ 1061553Srgrimes#include <sys/param.h> 1071553Srgrimes#include <sys/stat.h> 1081553Srgrimes#include <sys/ioctl.h> 1091553Srgrimes#include <sys/socket.h> 1101553Srgrimes#include <sys/wait.h> 1111553Srgrimes#include <sys/time.h> 1121553Srgrimes#include <sys/resource.h> 1131553Srgrimes 1141553Srgrimes#include <netinet/in.h> 11536042Sguido#include <netinet/tcp.h> 1161553Srgrimes#include <arpa/inet.h> 1172657Scsgr#include <rpc/rpc.h> 11819617Sjulian#include <rpc/pmap_clnt.h> 1191553Srgrimes 1201553Srgrimes#include <errno.h> 12129602Scharnier#include <err.h> 1221553Srgrimes#include <fcntl.h> 12330807Sache#include <grp.h> 1241553Srgrimes#include <netdb.h> 1251553Srgrimes#include <pwd.h> 1261553Srgrimes#include <signal.h> 1271553Srgrimes#include <stdio.h> 1281553Srgrimes#include <stdlib.h> 1291553Srgrimes#include <string.h> 1301553Srgrimes#include <syslog.h> 1311553Srgrimes#include <unistd.h> 13213142Speter#include <libutil.h> 13319617Sjulian#include <sysexits.h> 1341553Srgrimes 13545089Smarkm#ifdef LIBWRAP 13645089Smarkm# include <tcpd.h> 13745089Smarkm#ifndef LIBWRAP_ALLOW_FACILITY 13845089Smarkm# define LIBWRAP_ALLOW_FACILITY LOG_AUTH 13945089Smarkm#endif 14045089Smarkm#ifndef LIBWRAP_ALLOW_SEVERITY 14145089Smarkm# define LIBWRAP_ALLOW_SEVERITY LOG_INFO 14245089Smarkm#endif 14345089Smarkm#ifndef LIBWRAP_DENY_FACILITY 14445089Smarkm# define LIBWRAP_DENY_FACILITY LOG_AUTH 14545089Smarkm#endif 14645089Smarkm#ifndef LIBWRAP_DENY_SEVERITY 14745089Smarkm# define LIBWRAP_DENY_SEVERITY LOG_WARNING 14845089Smarkm#endif 14945089Smarkmint allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY; 15045089Smarkmint deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY; 15145089Smarkm#endif 15245089Smarkm 15321640Speter#ifdef LOGIN_CAP 15421640Speter#include <login_cap.h> 15530792Sache 15630792Sache/* see init.c */ 15730792Sache#define RESOURCE_RC "daemon" 15830792Sache 15921640Speter#endif 16021640Speter 1611553Srgrimes#include "pathnames.h" 1621553Srgrimes 16333794Spst#ifndef MAXCHILD 16433794Spst#define MAXCHILD -1 /* maximum number of this service 16533794Spst < 0 = no limit */ 16633794Spst#endif 16733794Spst 16833794Spst#ifndef MAXCPM 16933794Spst#define MAXCPM -1 /* rate limit invocations from a 17033794Spst single remote address, 17133794Spst < 0 = no limit */ 17233794Spst#endif 17333794Spst 1742659Scsgr#define TOOMANY 256 /* don't start more than TOOMANY */ 1751553Srgrimes#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ 1761553Srgrimes#define RETRYTIME (60*10) /* retry after bind or server fail */ 17719618Sjulian#define MAX_MAXCHLD 32767 /* max allowable max children */ 1781553Srgrimes 1791553Srgrimes#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) 1801553Srgrimes 1811553Srgrimesint debug = 0; 1822659Scsgrint log = 0; 1831553Srgrimesint nsock, maxsock; 1841553Srgrimesfd_set allsock; 1851553Srgrimesint options; 1861553Srgrimesint timingout; 1871553Srgrimesint toomany = TOOMANY; 18833794Spstint maxchild = MAXCPM; 18933794Spstint maxcpm = MAXCHILD; 1901553Srgrimesstruct servent *sp; 1912657Scsgrstruct rpcent *rpc; 19217482Sjulianstruct in_addr bind_address; 19342122Sdesint signalpipe[2]; 1941553Srgrimes 1951553Srgrimesstruct servtab { 1961553Srgrimes char *se_service; /* name of service */ 1971553Srgrimes int se_socktype; /* type of socket to use */ 1981553Srgrimes char *se_proto; /* protocol used */ 19930847Sdima int se_maxchild; /* max number of children */ 20030847Sdima int se_maxcpm; /* max connects per IP per minute */ 20130847Sdima int se_numchild; /* current number of children */ 20219618Sjulian pid_t *se_pids; /* array of child pids */ 2031553Srgrimes char *se_user; /* user name to run as */ 20430807Sache char *se_group; /* group name to run as */ 20530792Sache#ifdef LOGIN_CAP 20630792Sache char *se_class; /* login class name to run with */ 20730792Sache#endif 2081553Srgrimes struct biltin *se_bi; /* if built-in, description */ 2091553Srgrimes char *se_server; /* server program */ 2101553Srgrimes#define MAXARGV 20 2111553Srgrimes char *se_argv[MAXARGV+1]; /* program arguments */ 2121553Srgrimes int se_fd; /* open descriptor */ 2131553Srgrimes struct sockaddr_in se_ctrladdr;/* bound address */ 21419618Sjulian u_char se_type; /* type: normal, mux, or mux+ */ 21519618Sjulian u_char se_checked; /* looked at during merge */ 21619618Sjulian u_char se_accept; /* i.e., wait/nowait mode */ 21719618Sjulian u_char se_rpc; /* ==1 if RPC service */ 2182657Scsgr int se_rpc_prog; /* RPC program number */ 2192657Scsgr u_int se_rpc_lowvers; /* RPC low version */ 2202657Scsgr u_int se_rpc_highvers; /* RPC high version */ 2211553Srgrimes int se_count; /* number started since se_time */ 2221553Srgrimes struct timeval se_time; /* start of se_count */ 2231553Srgrimes struct servtab *se_next; 2241553Srgrimes} *servtab; 2251553Srgrimes 2261553Srgrimes#define NORM_TYPE 0 2271553Srgrimes#define MUX_TYPE 1 2281553Srgrimes#define MUXPLUS_TYPE 2 22936042Sguido#define TTCP_TYPE 3 2301553Srgrimes#define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \ 2311553Srgrimes ((sep)->se_type == MUXPLUS_TYPE)) 2321553Srgrimes#define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE) 23336042Sguido#define ISTTCP(sep) ((sep)->se_type == TTCP_TYPE) 2341553Srgrimes 2351553Srgrimes 2361553Srgrimesvoid chargen_dg __P((int, struct servtab *)); 2371553Srgrimesvoid chargen_stream __P((int, struct servtab *)); 2381553Srgrimesvoid close_sep __P((struct servtab *)); 23942122Sdesvoid flag_signal __P((char)); 24042122Sdesvoid flag_config __P((int)); 24142122Sdesvoid config __P((void)); 2421553Srgrimesvoid daytime_dg __P((int, struct servtab *)); 2431553Srgrimesvoid daytime_stream __P((int, struct servtab *)); 2441553Srgrimesvoid discard_dg __P((int, struct servtab *)); 2451553Srgrimesvoid discard_stream __P((int, struct servtab *)); 2461553Srgrimesvoid echo_dg __P((int, struct servtab *)); 2471553Srgrimesvoid echo_stream __P((int, struct servtab *)); 2481553Srgrimesvoid endconfig __P((void)); 2491553Srgrimesstruct servtab *enter __P((struct servtab *)); 2501553Srgrimesvoid freeconfig __P((struct servtab *)); 2511553Srgrimesstruct servtab *getconfigent __P((void)); 25240910Sphkvoid ident_stream __P((int, struct servtab *)); 2531553Srgrimesvoid machtime_dg __P((int, struct servtab *)); 2541553Srgrimesvoid machtime_stream __P((int, struct servtab *)); 2551553Srgrimeschar *newstr __P((char *)); 2561553Srgrimeschar *nextline __P((FILE *)); 2571553Srgrimesvoid print_service __P((char *, struct servtab *)); 25819618Sjulianvoid addchild __P((struct servtab *, int)); 25942122Sdesvoid flag_reapchild __P((int)); 26042122Sdesvoid reapchild __P((void)); 26119618Sjulianvoid enable __P((struct servtab *)); 26219618Sjulianvoid disable __P((struct servtab *)); 26342122Sdesvoid flag_retry __P((int)); 26442122Sdesvoid retry __P((void)); 2651553Srgrimesint setconfig __P((void)); 2661553Srgrimesvoid setup __P((struct servtab *)); 2671553Srgrimeschar *sskip __P((char **)); 2681553Srgrimeschar *skip __P((char **)); 2691553Srgrimesstruct servtab *tcpmux __P((int)); 27030847Sdimaint cpmip __P((struct servtab *, int)); 2711553Srgrimes 2722657Scsgrvoid unregisterrpc __P((register struct servtab *sep)); 2732657Scsgr 2741553Srgrimesstruct biltin { 2751553Srgrimes char *bi_service; /* internally provided service name */ 2761553Srgrimes int bi_socktype; /* type of socket supported */ 2771553Srgrimes short bi_fork; /* 1 if should fork before call */ 27830847Sdima int bi_maxchild; /* max number of children (default) */ 2791553Srgrimes void (*bi_fn)(); /* function which performs it */ 2801553Srgrimes} biltins[] = { 2811553Srgrimes /* Echo received data */ 2821553Srgrimes { "echo", SOCK_STREAM, 1, 0, echo_stream }, 2831553Srgrimes { "echo", SOCK_DGRAM, 0, 0, echo_dg }, 2841553Srgrimes 2851553Srgrimes /* Internet /dev/null */ 2861553Srgrimes { "discard", SOCK_STREAM, 1, 0, discard_stream }, 2871553Srgrimes { "discard", SOCK_DGRAM, 0, 0, discard_dg }, 2881553Srgrimes 28945089Smarkm /* Return 32 bit time since 1970 */ 2901553Srgrimes { "time", SOCK_STREAM, 0, 0, machtime_stream }, 2911553Srgrimes { "time", SOCK_DGRAM, 0, 0, machtime_dg }, 2921553Srgrimes 2931553Srgrimes /* Return human-readable time */ 2941553Srgrimes { "daytime", SOCK_STREAM, 0, 0, daytime_stream }, 2951553Srgrimes { "daytime", SOCK_DGRAM, 0, 0, daytime_dg }, 2961553Srgrimes 2971553Srgrimes /* Familiar character generator */ 2981553Srgrimes { "chargen", SOCK_STREAM, 1, 0, chargen_stream }, 2991553Srgrimes { "chargen", SOCK_DGRAM, 0, 0, chargen_dg }, 3001553Srgrimes 3011553Srgrimes { "tcpmux", SOCK_STREAM, 1, 0, (void (*)())tcpmux }, 3021553Srgrimes 30340910Sphk { "ident", SOCK_STREAM, 1, 0, ident_stream }, 30440910Sphk 3051553Srgrimes { NULL } 3061553Srgrimes}; 3071553Srgrimes 3081553Srgrimes#define NUMINT (sizeof(intab) / sizeof(struct inent)) 3091553Srgrimeschar *CONFIG = _PATH_INETDCONF; 31017482Sjulianchar *pid_file = _PATH_INETDPID; 31113142Speter 31213142Speter#ifdef OLD_SETPROCTITLE 3131553Srgrimeschar **Argv; 3141553Srgrimeschar *LastArg; 31513142Speter#endif 3161553Srgrimes 3171553Srgrimesint 31833794Spstgetvalue(arg, value, whine) 31933794Spst char *arg, *whine; 32033794Spst int *value; 32133794Spst{ 32233794Spst int tmp; 32333794Spst char *p; 32433794Spst 32533794Spst tmp = strtol(arg, &p, 0); 32633794Spst if (tmp < 1 || *p) { 32733794Spst syslog(LOG_ERR, whine, arg); 32833794Spst return 1; /* failure */ 32933794Spst } 33033794Spst *value = tmp; 33133794Spst return 0; /* success */ 33233794Spst} 33333794Spst 33433794Spstint 3351553Srgrimesmain(argc, argv, envp) 3361553Srgrimes int argc; 3371553Srgrimes char *argv[], *envp[]; 3381553Srgrimes{ 3391553Srgrimes struct servtab *sep; 3401553Srgrimes struct passwd *pwd; 34130807Sache struct group *grp; 34235848Sguido struct sigaction sa, sapipe; 3431553Srgrimes int tmpint, ch, dofork; 3441553Srgrimes pid_t pid; 3451553Srgrimes char buf[50]; 3462659Scsgr struct sockaddr_in peer; 3472659Scsgr int i; 34821640Speter#ifdef LOGIN_CAP 34921640Speter login_cap_t *lc = NULL; 35021640Speter#endif 35145089Smarkm#ifdef LIBWRAP 35245089Smarkm struct request_info req; 35345089Smarkm int denied; 35445089Smarkm char *service = NULL; 35545089Smarkm#endif 3561553Srgrimes 35713142Speter 35813142Speter#ifdef OLD_SETPROCTITLE 3591553Srgrimes Argv = argv; 3601553Srgrimes if (envp == 0 || *envp == 0) 3611553Srgrimes envp = argv; 3621553Srgrimes while (*envp) 3631553Srgrimes envp++; 3641553Srgrimes LastArg = envp[-1] + strlen(envp[-1]); 36513142Speter#endif 3661553Srgrimes 3671553Srgrimes openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); 3681553Srgrimes 36917482Sjulian bind_address.s_addr = htonl(INADDR_ANY); 37033794Spst while ((ch = getopt(argc, argv, "dlR:a:c:C:p:")) != -1) 3711553Srgrimes switch(ch) { 3721553Srgrimes case 'd': 3731553Srgrimes debug = 1; 3741553Srgrimes options |= SO_DEBUG; 3751553Srgrimes break; 3762659Scsgr case 'l': 3772659Scsgr log = 1; 3782659Scsgr break; 37933794Spst case 'R': 38033794Spst getvalue(optarg, &toomany, 38133794Spst "-R %s: bad value for service invocation rate"); 3821553Srgrimes break; 38333794Spst case 'c': 38433794Spst getvalue(optarg, &maxchild, 38533794Spst "-c %s: bad value for maximum children"); 38633794Spst break; 38733794Spst case 'C': 38833794Spst getvalue(optarg, &maxcpm, 38933794Spst "-C %s: bad value for maximum children/minute"); 39033794Spst break; 39117482Sjulian case 'a': 39217482Sjulian if (!inet_aton(optarg, &bind_address)) { 39317482Sjulian syslog(LOG_ERR, 39417482Sjulian "-a %s: invalid IP address", optarg); 39519617Sjulian exit(EX_USAGE); 39617482Sjulian } 39717482Sjulian break; 39817482Sjulian case 'p': 39917482Sjulian pid_file = optarg; 40017482Sjulian break; 4011553Srgrimes case '?': 4021553Srgrimes default: 4031553Srgrimes syslog(LOG_ERR, 40417482Sjulian "usage: inetd [-dl] [-a address] [-R rate]" 40533794Spst " [-c maximum] [-C rate]" 40617482Sjulian " [-p pidfile] [conf-file]"); 40719617Sjulian exit(EX_USAGE); 4081553Srgrimes } 4091553Srgrimes argc -= optind; 4101553Srgrimes argv += optind; 4111553Srgrimes 4121553Srgrimes if (argc > 0) 4131553Srgrimes CONFIG = argv[0]; 4141553Srgrimes if (debug == 0) { 41511447Swollman FILE *fp; 41612024Speter if (daemon(0, 0) < 0) { 41712024Speter syslog(LOG_WARNING, "daemon(0,0) failed: %m"); 41812024Speter } 41912024Speter /* 42012024Speter * In case somebody has started inetd manually, we need to 42112024Speter * clear the logname, so that old servers run as root do not 42212024Speter * get the user's logname.. 42312024Speter */ 42412024Speter if (setlogin("") < 0) { 42512024Speter syslog(LOG_WARNING, "cannot clear logname: %m"); 42612024Speter /* no big deal if it fails.. */ 42712024Speter } 42811447Swollman pid = getpid(); 42917482Sjulian fp = fopen(pid_file, "w"); 43011447Swollman if (fp) { 43111447Swollman fprintf(fp, "%ld\n", (long)pid); 43211447Swollman fclose(fp); 43311447Swollman } else { 43417482Sjulian syslog(LOG_WARNING, "%s: %m", pid_file); 43511447Swollman } 4361553Srgrimes } 43735948Sbde sa.sa_flags = 0; 43835948Sbde sigemptyset(&sa.sa_mask); 43935948Sbde sigaddset(&sa.sa_mask, SIGALRM); 44035948Sbde sigaddset(&sa.sa_mask, SIGCHLD); 44135948Sbde sigaddset(&sa.sa_mask, SIGHUP); 44242122Sdes sa.sa_handler = flag_retry; 44335848Sguido sigaction(SIGALRM, &sa, (struct sigaction *)0); 44442122Sdes config(); 44542122Sdes sa.sa_handler = flag_config; 44635848Sguido sigaction(SIGHUP, &sa, (struct sigaction *)0); 44742122Sdes sa.sa_handler = flag_reapchild; 44835848Sguido sigaction(SIGCHLD, &sa, (struct sigaction *)0); 44935848Sguido sa.sa_handler = SIG_IGN; 45035948Sbde sigaction(SIGPIPE, &sa, &sapipe); 4511553Srgrimes 4521553Srgrimes { 4531553Srgrimes /* space for daemons to overwrite environment for ps */ 4541553Srgrimes#define DUMMYSIZE 100 4551553Srgrimes char dummy[DUMMYSIZE]; 4561553Srgrimes 45719298Salex (void)memset(dummy, 'x', DUMMYSIZE - 1); 4581553Srgrimes dummy[DUMMYSIZE - 1] = '\0'; 4591553Srgrimes (void)setenv("inetd_dummy", dummy, 1); 4601553Srgrimes } 4611553Srgrimes 46242250Sdes if (pipe(signalpipe) != 0) { 46342250Sdes syslog(LOG_ERR, "pipe: %%m"); 46442250Sdes exit(EX_OSERR); 46542122Sdes } 46642122Sdes FD_SET(signalpipe[0], &allsock); 46742122Sdes if (signalpipe[0]>maxsock) maxsock = signalpipe[0]; 46841685Sdillon 4691553Srgrimes for (;;) { 4701553Srgrimes int n, ctrl; 4711553Srgrimes fd_set readable; 4721553Srgrimes 4731553Srgrimes if (nsock == 0) { 47442122Sdes (void) sigblock(SIGBLOCK); 4751553Srgrimes while (nsock == 0) 4761553Srgrimes sigpause(0L); 47742122Sdes (void) sigsetmask(0L); 4781553Srgrimes } 4791553Srgrimes readable = allsock; 48042122Sdes if ((n = select(maxsock + 1, &readable, (fd_set *)0, 48142122Sdes (fd_set *)0, (struct timeval *)0)) <= 0) { 48242122Sdes if (n < 0 && errno != EINTR) { 4831553Srgrimes syslog(LOG_WARNING, "select: %m"); 48428907Simp sleep(1); 48528907Simp } 4861553Srgrimes continue; 4871553Srgrimes } 48842122Sdes /* handle any queued signal flags */ 48942250Sdes if (FD_ISSET(signalpipe[0], &readable)) { 49042122Sdes int n; 49142250Sdes if (ioctl(signalpipe[0], FIONREAD, &n) != 0) { 49242122Sdes syslog(LOG_ERR, "ioctl: %m"); 49342122Sdes exit(EX_OSERR); 49442122Sdes } 49542250Sdes while (--n >= 0) { 49642250Sdes char c; 49742250Sdes if (read(signalpipe[0], &c, 1) != 1) { 49842250Sdes syslog(LOG_ERR, "read: %m"); 49942250Sdes exit(EX_OSERR); 50042250Sdes } 50142250Sdes if (debug) 50242250Sdes warnx("Handling signal flag %c", c); 50342250Sdes switch(c) { 50442250Sdes case 'A': /* sigalrm */ 50542250Sdes retry(); 50642250Sdes break; 50742250Sdes case 'C': /* sigchld */ 50842250Sdes reapchild(); 50942250Sdes break; 51042250Sdes case 'H': /* sighup */ 51142250Sdes config(); 51242250Sdes break; 51342250Sdes } 51442250Sdes } 51542122Sdes } 5161553Srgrimes for (sep = servtab; n && sep; sep = sep->se_next) 5171553Srgrimes if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { 5181553Srgrimes n--; 5191553Srgrimes if (debug) 52029602Scharnier warnx("someone wants %s", sep->se_service); 52119618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) { 5221553Srgrimes ctrl = accept(sep->se_fd, (struct sockaddr *)0, 5231553Srgrimes (int *)0); 5241553Srgrimes if (debug) 52529602Scharnier warnx("accept, ctrl %d", ctrl); 5261553Srgrimes if (ctrl < 0) { 5271553Srgrimes if (errno != EINTR) 5281553Srgrimes syslog(LOG_WARNING, 5291553Srgrimes "accept (for %s): %m", 53037844Sphk sep->se_service); 53137816Sphk if (sep->se_accept && 53237816Sphk sep->se_socktype == SOCK_STREAM) 53337816Sphk close(ctrl); 5341553Srgrimes continue; 5351553Srgrimes } 53630847Sdima if (cpmip(sep, ctrl) < 0) { 53730847Sdima close(ctrl); 53830847Sdima continue; 53930847Sdima } 54019617Sjulian if (log) { 5412659Scsgr i = sizeof peer; 54230847Sdima if (getpeername(ctrl, (struct sockaddr *) 5432659Scsgr &peer, &i)) { 5442659Scsgr syslog(LOG_WARNING, 5452659Scsgr "getpeername(for %s): %m", 5462659Scsgr sep->se_service); 54730847Sdima close(ctrl); 5482659Scsgr continue; 5492659Scsgr } 5502659Scsgr syslog(LOG_INFO,"%s from %s", 5512659Scsgr sep->se_service, 5522659Scsgr inet_ntoa(peer.sin_addr)); 5532659Scsgr } 5541553Srgrimes } else 5551553Srgrimes ctrl = sep->se_fd; 55642122Sdes (void) sigblock(SIGBLOCK); 5571553Srgrimes pid = 0; 55845089Smarkm#ifdef LIBWRAP_INTERNAL 55945089Smarkm dofork = 1; 56045089Smarkm#else 5611553Srgrimes dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork); 56245089Smarkm#endif 5631553Srgrimes if (dofork) { 5641553Srgrimes if (sep->se_count++ == 0) 56537856Sache (void)gettimeofday(&sep->se_time, (struct timezone *)NULL); 5661553Srgrimes else if (sep->se_count >= toomany) { 5671553Srgrimes struct timeval now; 5681553Srgrimes 56937856Sache (void)gettimeofday(&now, (struct timezone *)NULL); 5701553Srgrimes if (now.tv_sec - sep->se_time.tv_sec > 5711553Srgrimes CNT_INTVL) { 5721553Srgrimes sep->se_time = now; 5731553Srgrimes sep->se_count = 1; 5741553Srgrimes } else { 5751553Srgrimes syslog(LOG_ERR, 5761553Srgrimes "%s/%s server failing (looping), service terminated", 5771553Srgrimes sep->se_service, sep->se_proto); 5781553Srgrimes close_sep(sep); 57942122Sdes sigsetmask(0L); 5801553Srgrimes if (!timingout) { 5811553Srgrimes timingout = 1; 5821553Srgrimes alarm(RETRYTIME); 5831553Srgrimes } 5841553Srgrimes continue; 5851553Srgrimes } 5861553Srgrimes } 5871553Srgrimes pid = fork(); 5881553Srgrimes } 5891553Srgrimes if (pid < 0) { 5901553Srgrimes syslog(LOG_ERR, "fork: %m"); 59119618Sjulian if (sep->se_accept && 5921553Srgrimes sep->se_socktype == SOCK_STREAM) 5931553Srgrimes close(ctrl); 59442122Sdes sigsetmask(0L); 5951553Srgrimes sleep(1); 5961553Srgrimes continue; 5971553Srgrimes } 59819618Sjulian if (pid) 59919618Sjulian addchild(sep, pid); 60042122Sdes sigsetmask(0L); 6011553Srgrimes if (pid == 0) { 6021553Srgrimes if (dofork) { 6031553Srgrimes if (debug) 60429602Scharnier warnx("+ closing from %d", maxsock); 6051553Srgrimes for (tmpint = maxsock; tmpint > 2; tmpint--) 6061553Srgrimes if (tmpint != ctrl) 60719617Sjulian (void) close(tmpint); 6081553Srgrimes } 60935829Sguido /* 61035829Sguido * Call tcpmux to find the real service to exec. 61135829Sguido */ 61235829Sguido if (sep->se_bi && 61335829Sguido sep->se_bi->bi_fn == (void (*)()) tcpmux) { 61435829Sguido sep = tcpmux(ctrl); 61535829Sguido if (sep == NULL) { 61635829Sguido close(ctrl); 61735829Sguido _exit(0); 61835829Sguido } 61935829Sguido } 62045089Smarkm#ifdef LIBWRAP 62145089Smarkm#ifndef LIBWRAP_INTERNAL 62245089Smarkm if (sep->se_bi == 0) 62345089Smarkm#endif 62445089Smarkm if (sep->se_accept 62545089Smarkm && sep->se_socktype == SOCK_STREAM) { 62645089Smarkm request_init(&req, 62745089Smarkm RQ_DAEMON, sep->se_argv[0] ? 62845089Smarkm sep->se_argv[0] : sep->se_service, 62945089Smarkm RQ_FILE, ctrl, NULL); 63045089Smarkm fromhost(&req); 63145089Smarkm denied = !hosts_access(&req); 63245089Smarkm if (denied || log) { 63345089Smarkm sp = getservbyport(sep->se_ctrladdr.sin_port, sep->se_proto); 63445089Smarkm if (sp == NULL) { 63545089Smarkm (void)snprintf(buf, sizeof buf, "%d", 63645089Smarkm ntohs(sep->se_ctrladdr.sin_port)); 63745089Smarkm service = buf; 63845089Smarkm } else 63945089Smarkm service = sp->s_name; 64045089Smarkm } 64145089Smarkm if (denied) { 64245089Smarkm syslog(deny_severity, 64345089Smarkm "refused connection from %.500s, service %s (%s)", 64445089Smarkm eval_client(&req), service, sep->se_proto); 64545089Smarkm goto reject; 64645089Smarkm } 64745089Smarkm if (log) { 64845089Smarkm syslog(allow_severity, 64945089Smarkm "connection from %.500s, service %s (%s)", 65045089Smarkm eval_client(&req), service, sep->se_proto); 65145089Smarkm } 65245089Smarkm } 65345089Smarkm#endif /* LIBWRAP */ 65419617Sjulian if (sep->se_bi) { 6551553Srgrimes (*sep->se_bi->bi_fn)(ctrl, sep); 65619617Sjulian /* NOTREACHED */ 65719617Sjulian } else { 6581553Srgrimes if (debug) 65929602Scharnier warnx("%d execl %s", 66029602Scharnier getpid(), sep->se_server); 6611553Srgrimes dup2(ctrl, 0); 6621553Srgrimes close(ctrl); 6631553Srgrimes dup2(0, 1); 6641553Srgrimes dup2(0, 2); 6651553Srgrimes if ((pwd = getpwnam(sep->se_user)) == NULL) { 6661553Srgrimes syslog(LOG_ERR, 6671553Srgrimes "%s/%s: %s: No such user", 6681553Srgrimes sep->se_service, sep->se_proto, 6691553Srgrimes sep->se_user); 6701553Srgrimes if (sep->se_socktype != SOCK_STREAM) 6711553Srgrimes recv(0, buf, sizeof (buf), 0); 67219617Sjulian _exit(EX_NOUSER); 6731553Srgrimes } 67430807Sache grp = NULL; 67530807Sache if ( sep->se_group != NULL 67630807Sache && (grp = getgrnam(sep->se_group)) == NULL 67730807Sache ) { 67830807Sache syslog(LOG_ERR, 67930807Sache "%s/%s: %s: No such group", 68030807Sache sep->se_service, sep->se_proto, 68130807Sache sep->se_group); 68230807Sache if (sep->se_socktype != SOCK_STREAM) 68330807Sache recv(0, buf, sizeof (buf), 0); 68430807Sache _exit(EX_NOUSER); 68530807Sache } 68630807Sache if (grp != NULL) 68730807Sache pwd->pw_gid = grp->gr_gid; 68821640Speter#ifdef LOGIN_CAP 68930792Sache if ((lc = login_getclass(sep->se_class)) == NULL) { 69030792Sache /* error syslogged by getclass */ 69130792Sache syslog(LOG_ERR, 69230792Sache "%s/%s: %s: login class error", 69337850Sache sep->se_service, sep->se_proto, 69437850Sache sep->se_class); 69530792Sache if (sep->se_socktype != SOCK_STREAM) 69630792Sache recv(0, buf, sizeof (buf), 0); 69730792Sache _exit(EX_NOUSER); 69830792Sache } 69921640Speter#endif 70012024Speter if (setsid() < 0) { 70112024Speter syslog(LOG_ERR, 70212024Speter "%s: can't setsid(): %m", 70312024Speter sep->se_service); 70419617Sjulian /* _exit(EX_OSERR); not fatal yet */ 70512024Speter } 70621640Speter#ifdef LOGIN_CAP 70721640Speter if (setusercontext(lc, pwd, pwd->pw_uid, 70821640Speter LOGIN_SETALL) != 0) { 70921640Speter syslog(LOG_ERR, 71021640Speter "%s: can't setusercontext(..%s..): %m", 71121640Speter sep->se_service, sep->se_user); 71221640Speter _exit(EX_OSERR); 71321640Speter } 71421640Speter#else 7151553Srgrimes if (pwd->pw_uid) { 71612024Speter if (setlogin(sep->se_user) < 0) { 71712024Speter syslog(LOG_ERR, 71812024Speter "%s: can't setlogin(%s): %m", 71912024Speter sep->se_service, sep->se_user); 72019617Sjulian /* _exit(EX_OSERR); not yet */ 72112024Speter } 7221553Srgrimes if (setgid(pwd->pw_gid) < 0) { 7231553Srgrimes syslog(LOG_ERR, 7248857Srgrimes "%s: can't set gid %d: %m", 7251553Srgrimes sep->se_service, pwd->pw_gid); 72619617Sjulian _exit(EX_OSERR); 7271553Srgrimes } 7281553Srgrimes (void) initgroups(pwd->pw_name, 7291553Srgrimes pwd->pw_gid); 7301553Srgrimes if (setuid(pwd->pw_uid) < 0) { 7311553Srgrimes syslog(LOG_ERR, 7328857Srgrimes "%s: can't set uid %d: %m", 7331553Srgrimes sep->se_service, pwd->pw_uid); 73419617Sjulian _exit(EX_OSERR); 7351553Srgrimes } 7361553Srgrimes } 73721640Speter#endif 73835948Sbde sigaction(SIGPIPE, &sapipe, 73935948Sbde (struct sigaction *)0); 7401553Srgrimes execv(sep->se_server, sep->se_argv); 74145089Smarkm syslog(LOG_ERR, 74245089Smarkm "cannot execute %s: %m", sep->se_server); 74345089Smarkm#ifdef LIBWRAP 74445089Smarkm reject: 74545089Smarkm#endif 7461553Srgrimes if (sep->se_socktype != SOCK_STREAM) 7471553Srgrimes recv(0, buf, sizeof (buf), 0); 74819617Sjulian _exit(EX_OSERR); 7491553Srgrimes } 7501553Srgrimes } 75119618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) 7521553Srgrimes close(ctrl); 7531553Srgrimes } 7541553Srgrimes } 7551553Srgrimes} 7561553Srgrimes 75719618Sjulian/* 75842122Sdes * Add a signal flag to the signal flag queue for later handling 75942122Sdes */ 76042122Sdes 76142122Sdesvoid flag_signal(c) 76242122Sdes char c; 76342122Sdes{ 76442250Sdes if (write(signalpipe[1], &c, 1) != 1) { 76542250Sdes syslog(LOG_ERR, "write: %m"); 76642250Sdes exit(EX_OSERR); 76742250Sdes } 76842122Sdes} 76942122Sdes 77042122Sdes/* 77119618Sjulian * Record a new child pid for this service. If we've reached the 77219618Sjulian * limit on children, then stop accepting incoming requests. 77319618Sjulian */ 77419618Sjulian 7751553Srgrimesvoid 77619618Sjulianaddchild(struct servtab *sep, pid_t pid) 77719618Sjulian{ 77819618Sjulian#ifdef SANITY_CHECK 77919618Sjulian if (sep->se_numchild >= sep->se_maxchild) { 78019618Sjulian syslog(LOG_ERR, "%s: %d >= %d", 78119618Sjulian __FUNCTION__, sep->se_numchild, sep->se_maxchild); 78219618Sjulian exit(EX_SOFTWARE); 78319618Sjulian } 78419618Sjulian#endif 78519618Sjulian if (sep->se_maxchild == 0) 78619618Sjulian return; 78719618Sjulian sep->se_pids[sep->se_numchild++] = pid; 78819618Sjulian if (sep->se_numchild == sep->se_maxchild) 78919618Sjulian disable(sep); 79019618Sjulian} 79119618Sjulian 79219618Sjulian/* 79319618Sjulian * Some child process has exited. See if it's on somebody's list. 79419618Sjulian */ 79519618Sjulian 79619618Sjulianvoid 79742122Sdesflag_reapchild(signo) 7981553Srgrimes int signo; 7991553Srgrimes{ 80042250Sdes flag_signal('C'); 80142122Sdes} 80242122Sdes 80342122Sdesvoid 80442122Sdesreapchild() 80542122Sdes{ 80619618Sjulian int k, status; 8071553Srgrimes pid_t pid; 8081553Srgrimes struct servtab *sep; 8091553Srgrimes 8101553Srgrimes for (;;) { 8111553Srgrimes pid = wait3(&status, WNOHANG, (struct rusage *)0); 8121553Srgrimes if (pid <= 0) 8131553Srgrimes break; 8141553Srgrimes if (debug) 81529602Scharnier warnx("%d reaped, status %#x", pid, status); 81619618Sjulian for (sep = servtab; sep; sep = sep->se_next) { 81719618Sjulian for (k = 0; k < sep->se_numchild; k++) 81819618Sjulian if (sep->se_pids[k] == pid) 81919618Sjulian break; 82019618Sjulian if (k == sep->se_numchild) 82119618Sjulian continue; 82219618Sjulian if (sep->se_numchild == sep->se_maxchild) 82319618Sjulian enable(sep); 82419618Sjulian sep->se_pids[k] = sep->se_pids[--sep->se_numchild]; 82519618Sjulian if (status) 82619618Sjulian syslog(LOG_WARNING, 82719618Sjulian "%s[%d]: exit status 0x%x", 82819618Sjulian sep->se_server, pid, status); 82919618Sjulian break; 83019618Sjulian } 8311553Srgrimes } 8321553Srgrimes} 8331553Srgrimes 8341553Srgrimesvoid 83542122Sdesflag_config(signo) 8361553Srgrimes int signo; 8371553Srgrimes{ 83842250Sdes flag_signal('H'); 83942122Sdes} 84042122Sdes 84142122Sdesvoid config() 84242122Sdes{ 84319618Sjulian struct servtab *sep, *new, **sepp; 84442122Sdes long omask; 8451553Srgrimes 8461553Srgrimes if (!setconfig()) { 8471553Srgrimes syslog(LOG_ERR, "%s: %m", CONFIG); 8481553Srgrimes return; 8491553Srgrimes } 8501553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 8511553Srgrimes sep->se_checked = 0; 85219618Sjulian while ((new = getconfigent())) { 85330807Sache if (getpwnam(new->se_user) == NULL) { 8541553Srgrimes syslog(LOG_ERR, 8551553Srgrimes "%s/%s: No such user '%s', service ignored", 85619618Sjulian new->se_service, new->se_proto, new->se_user); 8571553Srgrimes continue; 8581553Srgrimes } 85930807Sache if (new->se_group && getgrnam(new->se_group) == NULL) { 86030807Sache syslog(LOG_ERR, 86130807Sache "%s/%s: No such group '%s', service ignored", 86230807Sache new->se_service, new->se_proto, new->se_group); 86330807Sache continue; 86430807Sache } 86530792Sache#ifdef LOGIN_CAP 86630792Sache if (login_getclass(new->se_class) == NULL) { 86730792Sache /* error syslogged by getclass */ 86830792Sache syslog(LOG_ERR, 86937850Sache "%s/%s: %s: login class error, service ignored", 87037850Sache new->se_service, new->se_proto, new->se_class); 87130792Sache continue; 87230792Sache } 87330792Sache#endif 8741553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 87519618Sjulian if (strcmp(sep->se_service, new->se_service) == 0 && 87619618Sjulian strcmp(sep->se_proto, new->se_proto) == 0) 8771553Srgrimes break; 8781553Srgrimes if (sep != 0) { 8791553Srgrimes int i; 8801553Srgrimes 88119618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; } 88242122Sdes omask = sigblock(SIGBLOCK); 88319618Sjulian /* copy over outstanding child pids */ 88419618Sjulian if (sep->se_maxchild && new->se_maxchild) { 88519618Sjulian new->se_numchild = sep->se_numchild; 88619618Sjulian if (new->se_numchild > new->se_maxchild) 88719618Sjulian new->se_numchild = new->se_maxchild; 88819618Sjulian memcpy(new->se_pids, sep->se_pids, 88919618Sjulian new->se_numchild * sizeof(*new->se_pids)); 89019618Sjulian } 89119618Sjulian SWAP(sep->se_pids, new->se_pids); 89219618Sjulian sep->se_maxchild = new->se_maxchild; 89319618Sjulian sep->se_numchild = new->se_numchild; 89430847Sdima sep->se_maxcpm = new->se_maxcpm; 89519618Sjulian /* might need to turn on or off service now */ 89619618Sjulian if (sep->se_fd >= 0) { 89719618Sjulian if (sep->se_maxchild 89819618Sjulian && sep->se_numchild == sep->se_maxchild) { 89919618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 90019618Sjulian disable(sep); 90119618Sjulian } else { 90219618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) 90319618Sjulian enable(sep); 90419618Sjulian } 90519618Sjulian } 90619618Sjulian sep->se_accept = new->se_accept; 90730807Sache SWAP(sep->se_user, new->se_user); 90830807Sache SWAP(sep->se_group, new->se_group); 90930792Sache#ifdef LOGIN_CAP 91030807Sache SWAP(sep->se_class, new->se_class); 91130792Sache#endif 91230807Sache SWAP(sep->se_server, new->se_server); 9131553Srgrimes for (i = 0; i < MAXARGV; i++) 91419618Sjulian SWAP(sep->se_argv[i], new->se_argv[i]); 91542122Sdes sigsetmask(omask); 91619618Sjulian freeconfig(new); 9171553Srgrimes if (debug) 9181553Srgrimes print_service("REDO", sep); 9191553Srgrimes } else { 92019618Sjulian sep = enter(new); 9211553Srgrimes if (debug) 9221553Srgrimes print_service("ADD ", sep); 9231553Srgrimes } 9241553Srgrimes sep->se_checked = 1; 9251553Srgrimes if (ISMUX(sep)) { 9261553Srgrimes sep->se_fd = -1; 9271553Srgrimes continue; 9281553Srgrimes } 9292657Scsgr if (!sep->se_rpc) { 9302657Scsgr sp = getservbyname(sep->se_service, sep->se_proto); 9312657Scsgr if (sp == 0) { 9322657Scsgr syslog(LOG_ERR, "%s/%s: unknown service", 9332657Scsgr sep->se_service, sep->se_proto); 9342657Scsgr sep->se_checked = 0; 9352657Scsgr continue; 9362657Scsgr } 9372657Scsgr if (sp->s_port != sep->se_ctrladdr.sin_port) { 9382657Scsgr sep->se_ctrladdr.sin_family = AF_INET; 93922306Sjulian sep->se_ctrladdr.sin_addr = bind_address; 9402657Scsgr sep->se_ctrladdr.sin_port = sp->s_port; 9412657Scsgr if (sep->se_fd >= 0) 9422657Scsgr close_sep(sep); 9432657Scsgr } 9442657Scsgr } else { 9452657Scsgr rpc = getrpcbyname(sep->se_service); 9462657Scsgr if (rpc == 0) { 9472657Scsgr syslog(LOG_ERR, "%s/%s unknown RPC service.", 9482657Scsgr sep->se_service, sep->se_proto); 9492657Scsgr if (sep->se_fd != -1) 9502657Scsgr (void) close(sep->se_fd); 9512657Scsgr sep->se_fd = -1; 9522657Scsgr continue; 9532657Scsgr } 9542657Scsgr if (rpc->r_number != sep->se_rpc_prog) { 9552657Scsgr if (sep->se_rpc_prog) 9562657Scsgr unregisterrpc(sep); 9572657Scsgr sep->se_rpc_prog = rpc->r_number; 9582657Scsgr if (sep->se_fd != -1) 9592657Scsgr (void) close(sep->se_fd); 9602657Scsgr sep->se_fd = -1; 9612657Scsgr } 9621553Srgrimes } 9631553Srgrimes if (sep->se_fd == -1) 9641553Srgrimes setup(sep); 9651553Srgrimes } 9661553Srgrimes endconfig(); 9671553Srgrimes /* 9681553Srgrimes * Purge anything not looked at above. 9691553Srgrimes */ 97042122Sdes omask = sigblock(SIGBLOCK); 9711553Srgrimes sepp = &servtab; 97219617Sjulian while ((sep = *sepp)) { 9731553Srgrimes if (sep->se_checked) { 9741553Srgrimes sepp = &sep->se_next; 9751553Srgrimes continue; 9761553Srgrimes } 9771553Srgrimes *sepp = sep->se_next; 9781553Srgrimes if (sep->se_fd >= 0) 9791553Srgrimes close_sep(sep); 9801553Srgrimes if (debug) 9811553Srgrimes print_service("FREE", sep); 9822657Scsgr if (sep->se_rpc && sep->se_rpc_prog > 0) 9832657Scsgr unregisterrpc(sep); 9841553Srgrimes freeconfig(sep); 9851553Srgrimes free((char *)sep); 9861553Srgrimes } 98742122Sdes (void) sigsetmask(omask); 9881553Srgrimes} 9891553Srgrimes 9901553Srgrimesvoid 9912657Scsgrunregisterrpc(sep) 9922657Scsgr struct servtab *sep; 9932657Scsgr{ 9942657Scsgr int i; 9952657Scsgr struct servtab *sepp; 99642122Sdes long omask; 9972657Scsgr 99842122Sdes omask = sigblock(SIGBLOCK); 9992657Scsgr for (sepp = servtab; sepp; sepp = sepp->se_next) { 10002657Scsgr if (sepp == sep) 10012657Scsgr continue; 10022657Scsgr if (sep->se_checked == 0 || 10032657Scsgr !sepp->se_rpc || 10042657Scsgr sep->se_rpc_prog != sepp->se_rpc_prog) 10052657Scsgr continue; 10062657Scsgr return; 10072657Scsgr } 10082657Scsgr if (debug) 10092657Scsgr print_service("UNREG", sep); 10102657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) 10112657Scsgr pmap_unset(sep->se_rpc_prog, i); 10122657Scsgr if (sep->se_fd != -1) 10132657Scsgr (void) close(sep->se_fd); 10142657Scsgr sep->se_fd = -1; 101542122Sdes (void) sigsetmask(omask); 10162657Scsgr} 10172657Scsgr 10182657Scsgrvoid 101942122Sdesflag_retry(signo) 10201553Srgrimes int signo; 10211553Srgrimes{ 102242250Sdes flag_signal('A'); 102342122Sdes} 102442122Sdes 102542122Sdesvoid 102642122Sdesretry() 102742122Sdes{ 10281553Srgrimes struct servtab *sep; 10291553Srgrimes 10301553Srgrimes timingout = 0; 10311553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 103219617Sjulian if (sep->se_fd == -1 && !ISMUX(sep)) 10331553Srgrimes setup(sep); 10341553Srgrimes} 10351553Srgrimes 10361553Srgrimesvoid 10371553Srgrimessetup(sep) 10381553Srgrimes struct servtab *sep; 10391553Srgrimes{ 10401553Srgrimes int on = 1; 10411553Srgrimes 10421553Srgrimes if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) { 10431553Srgrimes if (debug) 104429602Scharnier warn("socket failed on %s/%s", 104529602Scharnier sep->se_service, sep->se_proto); 10461553Srgrimes syslog(LOG_ERR, "%s/%s: socket: %m", 10471553Srgrimes sep->se_service, sep->se_proto); 10481553Srgrimes return; 10491553Srgrimes } 10501553Srgrimes#define turnon(fd, opt) \ 10511553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) 10521553Srgrimes if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && 10531553Srgrimes turnon(sep->se_fd, SO_DEBUG) < 0) 10541553Srgrimes syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 10551553Srgrimes if (turnon(sep->se_fd, SO_REUSEADDR) < 0) 10561553Srgrimes syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); 105725253Swollman#ifdef SO_PRIVSTATE 105813956Swollman if (turnon(sep->se_fd, SO_PRIVSTATE) < 0) 105913956Swollman syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m"); 106025253Swollman#endif 10611553Srgrimes#undef turnon 106236042Sguido if (sep->se_type == TTCP_TYPE) 106336042Sguido if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH, 106436042Sguido (char *)&on, sizeof (on)) < 0) 106536042Sguido syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m"); 10661553Srgrimes if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, 10671553Srgrimes sizeof (sep->se_ctrladdr)) < 0) { 10681553Srgrimes if (debug) 106929602Scharnier warn("bind failed on %s/%s", 107029602Scharnier sep->se_service, sep->se_proto); 10711553Srgrimes syslog(LOG_ERR, "%s/%s: bind: %m", 10721553Srgrimes sep->se_service, sep->se_proto); 10731553Srgrimes (void) close(sep->se_fd); 10741553Srgrimes sep->se_fd = -1; 10751553Srgrimes if (!timingout) { 10761553Srgrimes timingout = 1; 10771553Srgrimes alarm(RETRYTIME); 10781553Srgrimes } 10791553Srgrimes return; 10801553Srgrimes } 10812657Scsgr if (sep->se_rpc) { 10822657Scsgr int i, len = sizeof(struct sockaddr); 10832657Scsgr 10848857Srgrimes if (getsockname(sep->se_fd, 10852657Scsgr (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){ 10862657Scsgr syslog(LOG_ERR, "%s/%s: getsockname: %m", 10872657Scsgr sep->se_service, sep->se_proto); 10882657Scsgr (void) close(sep->se_fd); 10892657Scsgr sep->se_fd = -1; 10908857Srgrimes return; 10912657Scsgr } 10922657Scsgr if (debug) 10932657Scsgr print_service("REG ", sep); 10942657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { 10952657Scsgr pmap_unset(sep->se_rpc_prog, i); 10962657Scsgr pmap_set(sep->se_rpc_prog, i, 10972657Scsgr (sep->se_socktype == SOCK_DGRAM) 10982657Scsgr ? IPPROTO_UDP : IPPROTO_TCP, 10992657Scsgr ntohs(sep->se_ctrladdr.sin_port)); 11002657Scsgr } 11018857Srgrimes 11022657Scsgr } 11031553Srgrimes if (sep->se_socktype == SOCK_STREAM) 110417197Sdg listen(sep->se_fd, 64); 110519618Sjulian enable(sep); 11061553Srgrimes if (debug) { 110729602Scharnier warnx("registered %s on %d", 11081553Srgrimes sep->se_server, sep->se_fd); 11091553Srgrimes } 11101553Srgrimes} 11111553Srgrimes 11121553Srgrimes/* 11131553Srgrimes * Finish with a service and its socket. 11141553Srgrimes */ 11151553Srgrimesvoid 11161553Srgrimesclose_sep(sep) 11171553Srgrimes struct servtab *sep; 11181553Srgrimes{ 11191553Srgrimes if (sep->se_fd >= 0) { 112019618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 112119618Sjulian disable(sep); 11221553Srgrimes (void) close(sep->se_fd); 11231553Srgrimes sep->se_fd = -1; 11241553Srgrimes } 11251553Srgrimes sep->se_count = 0; 112619618Sjulian sep->se_numchild = 0; /* forget about any existing children */ 11271553Srgrimes} 11281553Srgrimes 11291553Srgrimesstruct servtab * 11301553Srgrimesenter(cp) 11311553Srgrimes struct servtab *cp; 11321553Srgrimes{ 11331553Srgrimes struct servtab *sep; 113442122Sdes long omask; 11351553Srgrimes 11361553Srgrimes sep = (struct servtab *)malloc(sizeof (*sep)); 11371553Srgrimes if (sep == (struct servtab *)0) { 11381553Srgrimes syslog(LOG_ERR, "Out of memory."); 113919617Sjulian exit(EX_OSERR); 11401553Srgrimes } 11411553Srgrimes *sep = *cp; 11421553Srgrimes sep->se_fd = -1; 114342122Sdes omask = sigblock(SIGBLOCK); 11441553Srgrimes sep->se_next = servtab; 11451553Srgrimes servtab = sep; 114642122Sdes sigsetmask(omask); 11471553Srgrimes return (sep); 11481553Srgrimes} 11491553Srgrimes 115019618Sjulianvoid 115119618Sjulianenable(struct servtab *sep) 115219618Sjulian{ 115319618Sjulian if (debug) 115429602Scharnier warnx( 115519618Sjulian "enabling %s, fd %d", sep->se_service, sep->se_fd); 115619618Sjulian#ifdef SANITY_CHECK 115719618Sjulian if (sep->se_fd < 0) { 115819618Sjulian syslog(LOG_ERR, 115919618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 116019618Sjulian exit(EX_SOFTWARE); 116119618Sjulian } 116219618Sjulian if (ISMUX(sep)) { 116319618Sjulian syslog(LOG_ERR, 116419618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 116519618Sjulian exit(EX_SOFTWARE); 116619618Sjulian } 116719618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) { 116819618Sjulian syslog(LOG_ERR, 116919618Sjulian "%s: %s: not off", __FUNCTION__, sep->se_service); 117019618Sjulian exit(EX_SOFTWARE); 117119618Sjulian } 117219618Sjulian#endif 117319618Sjulian FD_SET(sep->se_fd, &allsock); 117419618Sjulian nsock++; 117519618Sjulian if (sep->se_fd > maxsock) 117619618Sjulian maxsock = sep->se_fd; 117719618Sjulian} 117819618Sjulian 117919618Sjulianvoid 118019618Sjuliandisable(struct servtab *sep) 118119618Sjulian{ 118219618Sjulian if (debug) 118329602Scharnier warnx( 118419618Sjulian "disabling %s, fd %d", sep->se_service, sep->se_fd); 118519618Sjulian#ifdef SANITY_CHECK 118619618Sjulian if (sep->se_fd < 0) { 118719618Sjulian syslog(LOG_ERR, 118819618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 118919618Sjulian exit(EX_SOFTWARE); 119019618Sjulian } 119119618Sjulian if (ISMUX(sep)) { 119219618Sjulian syslog(LOG_ERR, 119319618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 119419618Sjulian exit(EX_SOFTWARE); 119519618Sjulian } 119619618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) { 119719618Sjulian syslog(LOG_ERR, 119819618Sjulian "%s: %s: not on", __FUNCTION__, sep->se_service); 119919618Sjulian exit(EX_SOFTWARE); 120019618Sjulian } 120119618Sjulian if (nsock == 0) { 120219618Sjulian syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 120319618Sjulian exit(EX_SOFTWARE); 120419618Sjulian } 120519618Sjulian#endif 120619618Sjulian FD_CLR(sep->se_fd, &allsock); 120719618Sjulian nsock--; 120819618Sjulian if (sep->se_fd == maxsock) 120919618Sjulian maxsock--; 121019618Sjulian} 121119618Sjulian 12121553SrgrimesFILE *fconfig = NULL; 12131553Srgrimesstruct servtab serv; 12141553Srgrimeschar line[LINE_MAX]; 12151553Srgrimes 12161553Srgrimesint 12171553Srgrimessetconfig() 12181553Srgrimes{ 12191553Srgrimes 12201553Srgrimes if (fconfig != NULL) { 12211553Srgrimes fseek(fconfig, 0L, SEEK_SET); 12221553Srgrimes return (1); 12231553Srgrimes } 12241553Srgrimes fconfig = fopen(CONFIG, "r"); 12251553Srgrimes return (fconfig != NULL); 12261553Srgrimes} 12271553Srgrimes 12281553Srgrimesvoid 12291553Srgrimesendconfig() 12301553Srgrimes{ 12311553Srgrimes if (fconfig) { 12321553Srgrimes (void) fclose(fconfig); 12331553Srgrimes fconfig = NULL; 12341553Srgrimes } 12351553Srgrimes} 12361553Srgrimes 12371553Srgrimesstruct servtab * 12381553Srgrimesgetconfigent() 12391553Srgrimes{ 12401553Srgrimes struct servtab *sep = &serv; 12411553Srgrimes int argc; 124219618Sjulian char *cp, *arg, *s; 12432657Scsgr char *versp; 12441553Srgrimes static char TCPMUX_TOKEN[] = "tcpmux/"; 12451553Srgrimes#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1) 12461553Srgrimes 12471553Srgrimesmore: 12481553Srgrimes while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0')) 12491553Srgrimes ; 12501553Srgrimes if (cp == NULL) 12511553Srgrimes return ((struct servtab *)0); 12521553Srgrimes /* 12531553Srgrimes * clear the static buffer, since some fields (se_ctrladdr, 12541553Srgrimes * for example) don't get initialized here. 12551553Srgrimes */ 12561553Srgrimes memset((caddr_t)sep, 0, sizeof *sep); 12571553Srgrimes arg = skip(&cp); 12581553Srgrimes if (cp == NULL) { 12591553Srgrimes /* got an empty line containing just blanks/tabs. */ 12601553Srgrimes goto more; 12611553Srgrimes } 12621553Srgrimes if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { 12631553Srgrimes char *c = arg + MUX_LEN; 12641553Srgrimes if (*c == '+') { 12651553Srgrimes sep->se_type = MUXPLUS_TYPE; 12661553Srgrimes c++; 12671553Srgrimes } else 12681553Srgrimes sep->se_type = MUX_TYPE; 12691553Srgrimes sep->se_service = newstr(c); 12701553Srgrimes } else { 12711553Srgrimes sep->se_service = newstr(arg); 12721553Srgrimes sep->se_type = NORM_TYPE; 12731553Srgrimes } 12741553Srgrimes arg = sskip(&cp); 12751553Srgrimes if (strcmp(arg, "stream") == 0) 12761553Srgrimes sep->se_socktype = SOCK_STREAM; 12771553Srgrimes else if (strcmp(arg, "dgram") == 0) 12781553Srgrimes sep->se_socktype = SOCK_DGRAM; 12791553Srgrimes else if (strcmp(arg, "rdm") == 0) 12801553Srgrimes sep->se_socktype = SOCK_RDM; 12811553Srgrimes else if (strcmp(arg, "seqpacket") == 0) 12821553Srgrimes sep->se_socktype = SOCK_SEQPACKET; 12831553Srgrimes else if (strcmp(arg, "raw") == 0) 12841553Srgrimes sep->se_socktype = SOCK_RAW; 12851553Srgrimes else 12861553Srgrimes sep->se_socktype = -1; 128736042Sguido 128836042Sguido arg = sskip(&cp); 128936042Sguido if (strcmp(arg, "tcp/ttcp") == 0) { 129036042Sguido sep->se_type = TTCP_TYPE; 129136042Sguido sep->se_proto = newstr("tcp"); 129236042Sguido } else { 129336042Sguido sep->se_proto = newstr(arg); 129436042Sguido } 12952657Scsgr if (strncmp(sep->se_proto, "rpc/", 4) == 0) { 129619237Sjoerg memmove(sep->se_proto, sep->se_proto + 4, 129719237Sjoerg strlen(sep->se_proto) + 1 - 4); 12982657Scsgr sep->se_rpc = 1; 12992657Scsgr sep->se_rpc_prog = sep->se_rpc_lowvers = 13002657Scsgr sep->se_rpc_lowvers = 0; 13012657Scsgr sep->se_ctrladdr.sin_family = AF_INET; 13022657Scsgr sep->se_ctrladdr.sin_port = 0; 130317482Sjulian sep->se_ctrladdr.sin_addr = bind_address; 13042657Scsgr if ((versp = rindex(sep->se_service, '/'))) { 13052657Scsgr *versp++ = '\0'; 13062657Scsgr switch (sscanf(versp, "%d-%d", 13072657Scsgr &sep->se_rpc_lowvers, 13082657Scsgr &sep->se_rpc_highvers)) { 13092657Scsgr case 2: 13102657Scsgr break; 13112657Scsgr case 1: 13122657Scsgr sep->se_rpc_highvers = 13132657Scsgr sep->se_rpc_lowvers; 13142657Scsgr break; 13152657Scsgr default: 13168857Srgrimes syslog(LOG_ERR, 13178857Srgrimes "bad RPC version specifier; %s\n", 13182657Scsgr sep->se_service); 13192657Scsgr freeconfig(sep); 13202657Scsgr goto more; 13212657Scsgr } 13222657Scsgr } 13232657Scsgr else { 13242657Scsgr sep->se_rpc_lowvers = 13252657Scsgr sep->se_rpc_highvers = 1; 13262657Scsgr } 13272657Scsgr } 13281553Srgrimes arg = sskip(&cp); 132919618Sjulian if (!strncmp(arg, "wait", 4)) 133019618Sjulian sep->se_accept = 0; 133119618Sjulian else if (!strncmp(arg, "nowait", 6)) 133219618Sjulian sep->se_accept = 1; 133319618Sjulian else { 133419618Sjulian syslog(LOG_ERR, 133519618Sjulian "%s: bad wait/nowait for service %s", 133619618Sjulian CONFIG, sep->se_service); 133719618Sjulian goto more; 133819618Sjulian } 133933794Spst sep->se_maxchild = maxchild; 134033794Spst sep->se_maxcpm = maxcpm; 134119618Sjulian if ((s = strchr(arg, '/')) != NULL) { 134219618Sjulian char *eptr; 134319618Sjulian u_long val; 134419618Sjulian 134519618Sjulian val = strtoul(s + 1, &eptr, 10); 134630847Sdima if (eptr == s + 1 || val > MAX_MAXCHLD) { 134719618Sjulian syslog(LOG_ERR, 134819618Sjulian "%s: bad max-child for service %s", 134919618Sjulian CONFIG, sep->se_service); 135019618Sjulian goto more; 135119618Sjulian } 135219618Sjulian sep->se_maxchild = val; 135330847Sdima if (*eptr == '/') 135430847Sdima sep->se_maxcpm = strtol(eptr + 1, &eptr, 10); 135530847Sdima /* 135630847Sdima * explicitly do not check for \0 for future expansion / 135730847Sdima * backwards compatibility 135830847Sdima */ 135919618Sjulian } 13601553Srgrimes if (ISMUX(sep)) { 13611553Srgrimes /* 136219618Sjulian * Silently enforce "nowait" mode for TCPMUX services 136319618Sjulian * since they don't have an assigned port to listen on. 13641553Srgrimes */ 136519618Sjulian sep->se_accept = 1; 13661553Srgrimes if (strcmp(sep->se_proto, "tcp")) { 13678857Srgrimes syslog(LOG_ERR, 13681553Srgrimes "%s: bad protocol for tcpmux service %s", 13691553Srgrimes CONFIG, sep->se_service); 13701553Srgrimes goto more; 13711553Srgrimes } 13721553Srgrimes if (sep->se_socktype != SOCK_STREAM) { 13738857Srgrimes syslog(LOG_ERR, 13741553Srgrimes "%s: bad socket type for tcpmux service %s", 13751553Srgrimes CONFIG, sep->se_service); 13761553Srgrimes goto more; 13771553Srgrimes } 13781553Srgrimes } 13791553Srgrimes sep->se_user = newstr(sskip(&cp)); 138030792Sache#ifdef LOGIN_CAP 138130792Sache if ((s = strrchr(sep->se_user, '/')) != NULL) { 138230792Sache *s = '\0'; 138330792Sache sep->se_class = newstr(s + 1); 138430792Sache } else 138530792Sache sep->se_class = newstr(RESOURCE_RC); 138630792Sache#endif 138730807Sache if ((s = strrchr(sep->se_user, ':')) != NULL) { 138830807Sache *s = '\0'; 138930807Sache sep->se_group = newstr(s + 1); 139030807Sache } else 139130807Sache sep->se_group = NULL; 13921553Srgrimes sep->se_server = newstr(sskip(&cp)); 13931553Srgrimes if (strcmp(sep->se_server, "internal") == 0) { 13941553Srgrimes struct biltin *bi; 13951553Srgrimes 13961553Srgrimes for (bi = biltins; bi->bi_service; bi++) 13971553Srgrimes if (bi->bi_socktype == sep->se_socktype && 13981553Srgrimes strcmp(bi->bi_service, sep->se_service) == 0) 13991553Srgrimes break; 14001553Srgrimes if (bi->bi_service == 0) { 14011553Srgrimes syslog(LOG_ERR, "internal service %s unknown", 14021553Srgrimes sep->se_service); 14031553Srgrimes goto more; 14041553Srgrimes } 140519618Sjulian sep->se_accept = 1; /* force accept mode for built-ins */ 14061553Srgrimes sep->se_bi = bi; 14071553Srgrimes } else 14081553Srgrimes sep->se_bi = NULL; 140919618Sjulian if (sep->se_maxchild < 0) /* apply default max-children */ 141019618Sjulian if (sep->se_bi) 141119618Sjulian sep->se_maxchild = sep->se_bi->bi_maxchild; 141219618Sjulian else 141319618Sjulian sep->se_maxchild = sep->se_accept ? 0 : 1; 141419618Sjulian if (sep->se_maxchild) { 141519618Sjulian sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); 141619618Sjulian if (sep->se_pids == NULL) { 141719618Sjulian syslog(LOG_ERR, "Out of memory."); 141819618Sjulian exit(EX_OSERR); 141919618Sjulian } 142019618Sjulian } 14211553Srgrimes argc = 0; 14221553Srgrimes for (arg = skip(&cp); cp; arg = skip(&cp)) 142319618Sjulian if (argc < MAXARGV) { 14241553Srgrimes sep->se_argv[argc++] = newstr(arg); 142519618Sjulian } else { 142619618Sjulian syslog(LOG_ERR, 142719618Sjulian "%s: too many arguments for service %s", 142819618Sjulian CONFIG, sep->se_service); 142919618Sjulian goto more; 143019618Sjulian } 14311553Srgrimes while (argc <= MAXARGV) 14321553Srgrimes sep->se_argv[argc++] = NULL; 14331553Srgrimes return (sep); 14341553Srgrimes} 14351553Srgrimes 14361553Srgrimesvoid 14371553Srgrimesfreeconfig(cp) 14381553Srgrimes struct servtab *cp; 14391553Srgrimes{ 14401553Srgrimes int i; 14411553Srgrimes 14421553Srgrimes if (cp->se_service) 14431553Srgrimes free(cp->se_service); 14441553Srgrimes if (cp->se_proto) 14451553Srgrimes free(cp->se_proto); 14461553Srgrimes if (cp->se_user) 14471553Srgrimes free(cp->se_user); 144830807Sache if (cp->se_group) 144930807Sache free(cp->se_group); 145030792Sache#ifdef LOGIN_CAP 145130792Sache if (cp->se_class) 145230792Sache free(cp->se_class); 145330792Sache#endif 14541553Srgrimes if (cp->se_server) 14551553Srgrimes free(cp->se_server); 145619618Sjulian if (cp->se_pids) 145719618Sjulian free(cp->se_pids); 14581553Srgrimes for (i = 0; i < MAXARGV; i++) 14591553Srgrimes if (cp->se_argv[i]) 14601553Srgrimes free(cp->se_argv[i]); 14611553Srgrimes} 14621553Srgrimes 14631553Srgrimes 14641553Srgrimes/* 14651553Srgrimes * Safe skip - if skip returns null, log a syntax error in the 14661553Srgrimes * configuration file and exit. 14671553Srgrimes */ 14681553Srgrimeschar * 14691553Srgrimessskip(cpp) 14701553Srgrimes char **cpp; 14711553Srgrimes{ 14721553Srgrimes char *cp; 14731553Srgrimes 14741553Srgrimes cp = skip(cpp); 14751553Srgrimes if (cp == NULL) { 14761553Srgrimes syslog(LOG_ERR, "%s: syntax error", CONFIG); 147719617Sjulian exit(EX_DATAERR); 14781553Srgrimes } 14791553Srgrimes return (cp); 14801553Srgrimes} 14811553Srgrimes 14821553Srgrimeschar * 14831553Srgrimesskip(cpp) 14841553Srgrimes char **cpp; 14851553Srgrimes{ 14861553Srgrimes char *cp = *cpp; 14871553Srgrimes char *start; 148811933Sadam char quote = '\0'; 14891553Srgrimes 14901553Srgrimesagain: 14911553Srgrimes while (*cp == ' ' || *cp == '\t') 14921553Srgrimes cp++; 14931553Srgrimes if (*cp == '\0') { 14941553Srgrimes int c; 14951553Srgrimes 14961553Srgrimes c = getc(fconfig); 14971553Srgrimes (void) ungetc(c, fconfig); 14981553Srgrimes if (c == ' ' || c == '\t') 149919617Sjulian if ((cp = nextline(fconfig))) 15001553Srgrimes goto again; 15011553Srgrimes *cpp = (char *)0; 15021553Srgrimes return ((char *)0); 15031553Srgrimes } 150411933Sadam if (*cp == '"' || *cp == '\'') 150511933Sadam quote = *cp++; 15061553Srgrimes start = cp; 150711933Sadam if (quote) 150811933Sadam while (*cp && *cp != quote) 150911933Sadam cp++; 151011933Sadam else 151111933Sadam while (*cp && *cp != ' ' && *cp != '\t') 151211933Sadam cp++; 15131553Srgrimes if (*cp != '\0') 15141553Srgrimes *cp++ = '\0'; 15151553Srgrimes *cpp = cp; 15161553Srgrimes return (start); 15171553Srgrimes} 15181553Srgrimes 15191553Srgrimeschar * 15201553Srgrimesnextline(fd) 15211553Srgrimes FILE *fd; 15221553Srgrimes{ 15231553Srgrimes char *cp; 15241553Srgrimes 15251553Srgrimes if (fgets(line, sizeof (line), fd) == NULL) 15261553Srgrimes return ((char *)0); 15271553Srgrimes cp = strchr(line, '\n'); 15281553Srgrimes if (cp) 15291553Srgrimes *cp = '\0'; 15301553Srgrimes return (line); 15311553Srgrimes} 15321553Srgrimes 15331553Srgrimeschar * 15341553Srgrimesnewstr(cp) 15351553Srgrimes char *cp; 15361553Srgrimes{ 153719617Sjulian if ((cp = strdup(cp ? cp : ""))) 15381553Srgrimes return (cp); 15391553Srgrimes syslog(LOG_ERR, "strdup: %m"); 154019617Sjulian exit(EX_OSERR); 15411553Srgrimes} 15421553Srgrimes 154313142Speter#ifdef OLD_SETPROCTITLE 15441553Srgrimesvoid 154513142Speterinetd_setproctitle(a, s) 15461553Srgrimes char *a; 15471553Srgrimes int s; 15481553Srgrimes{ 15491553Srgrimes int size; 15501553Srgrimes char *cp; 15511553Srgrimes struct sockaddr_in sin; 15521553Srgrimes char buf[80]; 15531553Srgrimes 15541553Srgrimes cp = Argv[0]; 15551553Srgrimes size = sizeof(sin); 15561553Srgrimes if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) 15578857Srgrimes (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr)); 15581553Srgrimes else 15598857Srgrimes (void) sprintf(buf, "-%s", a); 15601553Srgrimes strncpy(cp, buf, LastArg - cp); 15611553Srgrimes cp += strlen(cp); 15621553Srgrimes while (cp < LastArg) 15631553Srgrimes *cp++ = ' '; 15641553Srgrimes} 156513142Speter#else 156613142Spetervoid 156713142Speterinetd_setproctitle(a, s) 156813142Speter char *a; 156913142Speter int s; 157013142Speter{ 157113142Speter int size; 157213142Speter struct sockaddr_in sin; 157313142Speter char buf[80]; 15741553Srgrimes 157513142Speter size = sizeof(sin); 157613142Speter if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) 157713142Speter (void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr)); 157813142Speter else 157913142Speter (void) sprintf(buf, "%s", a); 158013142Speter setproctitle("%s", buf); 158113142Speter} 158213142Speter#endif 158313142Speter 158413142Speter 15851553Srgrimes/* 15861553Srgrimes * Internet services provided internally by inetd: 15871553Srgrimes */ 15881553Srgrimes#define BUFSIZE 8192 15891553Srgrimes 159040910Sphk#define IDENT_RESPONSE ":ERROR:HIDDEN-USER\r\n" 159140910Sphk 15921553Srgrimes/* ARGSUSED */ 15931553Srgrimesvoid 159440910Sphkident_stream(s, sep) /* Ident service */ 159540910Sphk int s; 159640910Sphk struct servtab *sep; 159740910Sphk{ 159840910Sphk char buffer[BUFSIZE]; 159940910Sphk int i, j; 160040910Sphk 160140910Sphk inetd_setproctitle(sep->se_service, s); 160240910Sphk j = 0; 160340910Sphk while ((i = read(s, buffer + j, sizeof(buffer) - j)) > 0) { 160440910Sphk j += i; 160540910Sphk buffer[j] = '\0'; 160640910Sphk if (strchr(buffer, '\n')) 160740910Sphk break; 160840910Sphk if (strchr(buffer, '\r')) 160940910Sphk break; 161040910Sphk } 161140910Sphk while (j > 0 && (buffer[j-1] == '\n' || buffer[j-1] == '\r')) 161240910Sphk j--; 161340910Sphk write(s, buffer, j); 161440910Sphk write(s, IDENT_RESPONSE, strlen(IDENT_RESPONSE)); 161540910Sphk exit(0); 161640910Sphk} 161740910Sphk/* ARGSUSED */ 161840910Sphkvoid 16191553Srgrimesecho_stream(s, sep) /* Echo service -- echo data back */ 16201553Srgrimes int s; 16211553Srgrimes struct servtab *sep; 16221553Srgrimes{ 16231553Srgrimes char buffer[BUFSIZE]; 16241553Srgrimes int i; 16251553Srgrimes 162613142Speter inetd_setproctitle(sep->se_service, s); 16271553Srgrimes while ((i = read(s, buffer, sizeof(buffer))) > 0 && 16281553Srgrimes write(s, buffer, i) > 0) 16291553Srgrimes ; 16301553Srgrimes exit(0); 16311553Srgrimes} 16321553Srgrimes 16335182Swollmanint check_loop(sin, sep) 16345182Swollman struct sockaddr_in *sin; 16355182Swollman struct servtab *sep; 16365182Swollman{ 16375182Swollman struct servtab *se2; 16385182Swollman 16395182Swollman for (se2 = servtab; se2; se2 = se2->se_next) { 16405182Swollman if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM) 16415182Swollman continue; 16425182Swollman 16435182Swollman if (sin->sin_port == se2->se_ctrladdr.sin_port) { 16445182Swollman syslog(LOG_WARNING, 16455182Swollman "%s/%s:%s/%s loop request REFUSED from %s", 16468857Srgrimes sep->se_service, sep->se_proto, 16475182Swollman se2->se_service, se2->se_proto, 16485182Swollman inet_ntoa(sin->sin_addr)); 16495182Swollman return 1; 16505182Swollman } 16515182Swollman } 16525182Swollman return 0; 16535182Swollman} 16545182Swollman 16551553Srgrimes/* ARGSUSED */ 16561553Srgrimesvoid 16571553Srgrimesecho_dg(s, sep) /* Echo service -- echo data back */ 16581553Srgrimes int s; 16591553Srgrimes struct servtab *sep; 16601553Srgrimes{ 16611553Srgrimes char buffer[BUFSIZE]; 16621553Srgrimes int i, size; 16635182Swollman struct sockaddr_in sin; 16641553Srgrimes 16655182Swollman size = sizeof(sin); 16668857Srgrimes if ((i = recvfrom(s, buffer, sizeof(buffer), 0, 16675182Swollman (struct sockaddr *)&sin, &size)) < 0) 16681553Srgrimes return; 16695182Swollman 16705182Swollman if (check_loop(&sin, sep)) 16715182Swollman return; 16725182Swollman 16735182Swollman (void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin, 16745182Swollman sizeof(sin)); 16751553Srgrimes} 16761553Srgrimes 16771553Srgrimes/* ARGSUSED */ 16781553Srgrimesvoid 16791553Srgrimesdiscard_stream(s, sep) /* Discard service -- ignore data */ 16801553Srgrimes int s; 16811553Srgrimes struct servtab *sep; 16821553Srgrimes{ 16831553Srgrimes int ret; 16841553Srgrimes char buffer[BUFSIZE]; 16851553Srgrimes 168613142Speter inetd_setproctitle(sep->se_service, s); 16871553Srgrimes while (1) { 16881553Srgrimes while ((ret = read(s, buffer, sizeof(buffer))) > 0) 16891553Srgrimes ; 16901553Srgrimes if (ret == 0 || errno != EINTR) 16911553Srgrimes break; 16921553Srgrimes } 16931553Srgrimes exit(0); 16941553Srgrimes} 16951553Srgrimes 16961553Srgrimes/* ARGSUSED */ 16971553Srgrimesvoid 16981553Srgrimesdiscard_dg(s, sep) /* Discard service -- ignore data */ 16991553Srgrimes int s; 17001553Srgrimes struct servtab *sep; 17011553Srgrimes{ 17021553Srgrimes char buffer[BUFSIZE]; 17031553Srgrimes 17041553Srgrimes (void) read(s, buffer, sizeof(buffer)); 17051553Srgrimes} 17061553Srgrimes 17071553Srgrimes#include <ctype.h> 17081553Srgrimes#define LINESIZ 72 17091553Srgrimeschar ring[128]; 17101553Srgrimeschar *endring; 17111553Srgrimes 17121553Srgrimesvoid 17131553Srgrimesinitring() 17141553Srgrimes{ 17151553Srgrimes int i; 17161553Srgrimes 17171553Srgrimes endring = ring; 17181553Srgrimes 17191553Srgrimes for (i = 0; i <= 128; ++i) 17201553Srgrimes if (isprint(i)) 17211553Srgrimes *endring++ = i; 17221553Srgrimes} 17231553Srgrimes 17241553Srgrimes/* ARGSUSED */ 17251553Srgrimesvoid 17261553Srgrimeschargen_stream(s, sep) /* Character generator */ 17271553Srgrimes int s; 17281553Srgrimes struct servtab *sep; 17291553Srgrimes{ 17301553Srgrimes int len; 17311553Srgrimes char *rs, text[LINESIZ+2]; 17321553Srgrimes 173313142Speter inetd_setproctitle(sep->se_service, s); 17341553Srgrimes 17351553Srgrimes if (!endring) { 17361553Srgrimes initring(); 17371553Srgrimes rs = ring; 17381553Srgrimes } 17391553Srgrimes 17401553Srgrimes text[LINESIZ] = '\r'; 17411553Srgrimes text[LINESIZ + 1] = '\n'; 17421553Srgrimes for (rs = ring;;) { 17431553Srgrimes if ((len = endring - rs) >= LINESIZ) 17441553Srgrimes memmove(text, rs, LINESIZ); 17451553Srgrimes else { 17461553Srgrimes memmove(text, rs, len); 17471553Srgrimes memmove(text + len, ring, LINESIZ - len); 17481553Srgrimes } 17491553Srgrimes if (++rs == endring) 17501553Srgrimes rs = ring; 17511553Srgrimes if (write(s, text, sizeof(text)) != sizeof(text)) 17521553Srgrimes break; 17531553Srgrimes } 17541553Srgrimes exit(0); 17551553Srgrimes} 17561553Srgrimes 17571553Srgrimes/* ARGSUSED */ 17581553Srgrimesvoid 17591553Srgrimeschargen_dg(s, sep) /* Character generator */ 17601553Srgrimes int s; 17611553Srgrimes struct servtab *sep; 17621553Srgrimes{ 17635182Swollman struct sockaddr_in sin; 17641553Srgrimes static char *rs; 17651553Srgrimes int len, size; 17661553Srgrimes char text[LINESIZ+2]; 17671553Srgrimes 17681553Srgrimes if (endring == 0) { 17691553Srgrimes initring(); 17701553Srgrimes rs = ring; 17711553Srgrimes } 17721553Srgrimes 17735182Swollman size = sizeof(sin); 17748857Srgrimes if (recvfrom(s, text, sizeof(text), 0, 17755182Swollman (struct sockaddr *)&sin, &size) < 0) 17761553Srgrimes return; 17771553Srgrimes 17785182Swollman if (check_loop(&sin, sep)) 17795182Swollman return; 17805182Swollman 17811553Srgrimes if ((len = endring - rs) >= LINESIZ) 17821553Srgrimes memmove(text, rs, LINESIZ); 17831553Srgrimes else { 17841553Srgrimes memmove(text, rs, len); 17851553Srgrimes memmove(text + len, ring, LINESIZ - len); 17861553Srgrimes } 17871553Srgrimes if (++rs == endring) 17881553Srgrimes rs = ring; 17891553Srgrimes text[LINESIZ] = '\r'; 17901553Srgrimes text[LINESIZ + 1] = '\n'; 17918857Srgrimes (void) sendto(s, text, sizeof(text), 0, 17925182Swollman (struct sockaddr *)&sin, sizeof(sin)); 17931553Srgrimes} 17941553Srgrimes 17951553Srgrimes/* 17961553Srgrimes * Return a machine readable date and time, in the form of the 17971553Srgrimes * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 17981553Srgrimes * returns the number of seconds since midnight, Jan 1, 1970, 17991553Srgrimes * we must add 2208988800 seconds to this figure to make up for 18001553Srgrimes * some seventy years Bell Labs was asleep. 18011553Srgrimes */ 18021553Srgrimes 180342311Sdannyunsigned long 18041553Srgrimesmachtime() 18051553Srgrimes{ 18061553Srgrimes struct timeval tv; 18071553Srgrimes 180837856Sache if (gettimeofday(&tv, (struct timezone *)NULL) < 0) { 18091553Srgrimes if (debug) 181029602Scharnier warnx("unable to get time of day"); 18111553Srgrimes return (0L); 18121553Srgrimes } 18131553Srgrimes#define OFFSET ((u_long)25567 * 24*60*60) 18141553Srgrimes return (htonl((long)(tv.tv_sec + OFFSET))); 18151553Srgrimes#undef OFFSET 18161553Srgrimes} 18171553Srgrimes 18181553Srgrimes/* ARGSUSED */ 18191553Srgrimesvoid 18201553Srgrimesmachtime_stream(s, sep) 18211553Srgrimes int s; 18221553Srgrimes struct servtab *sep; 18231553Srgrimes{ 182442311Sdanny unsigned long result; 18251553Srgrimes 18261553Srgrimes result = machtime(); 18271553Srgrimes (void) write(s, (char *) &result, sizeof(result)); 18281553Srgrimes} 18291553Srgrimes 18301553Srgrimes/* ARGSUSED */ 18311553Srgrimesvoid 18321553Srgrimesmachtime_dg(s, sep) 18331553Srgrimes int s; 18341553Srgrimes struct servtab *sep; 18351553Srgrimes{ 183642311Sdanny unsigned long result; 18375182Swollman struct sockaddr_in sin; 18381553Srgrimes int size; 18391553Srgrimes 18405182Swollman size = sizeof(sin); 18418857Srgrimes if (recvfrom(s, (char *)&result, sizeof(result), 0, 18425182Swollman (struct sockaddr *)&sin, &size) < 0) 18431553Srgrimes return; 18445182Swollman 18455182Swollman if (check_loop(&sin, sep)) 18465182Swollman return; 18475182Swollman 18481553Srgrimes result = machtime(); 18498857Srgrimes (void) sendto(s, (char *) &result, sizeof(result), 0, 18505182Swollman (struct sockaddr *)&sin, sizeof(sin)); 18511553Srgrimes} 18521553Srgrimes 18531553Srgrimes/* ARGSUSED */ 18541553Srgrimesvoid 18551553Srgrimesdaytime_stream(s, sep) /* Return human-readable time of day */ 18561553Srgrimes int s; 18571553Srgrimes struct servtab *sep; 18581553Srgrimes{ 18591553Srgrimes char buffer[256]; 18601553Srgrimes time_t clock; 18611553Srgrimes 18621553Srgrimes clock = time((time_t *) 0); 18631553Srgrimes 18641553Srgrimes (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 18651553Srgrimes (void) write(s, buffer, strlen(buffer)); 18661553Srgrimes} 18671553Srgrimes 18681553Srgrimes/* ARGSUSED */ 18691553Srgrimesvoid 18701553Srgrimesdaytime_dg(s, sep) /* Return human-readable time of day */ 18711553Srgrimes int s; 18721553Srgrimes struct servtab *sep; 18731553Srgrimes{ 18741553Srgrimes char buffer[256]; 18751553Srgrimes time_t clock; 18765182Swollman struct sockaddr_in sin; 18771553Srgrimes int size; 18781553Srgrimes 18791553Srgrimes clock = time((time_t *) 0); 18801553Srgrimes 18815182Swollman size = sizeof(sin); 18828857Srgrimes if (recvfrom(s, buffer, sizeof(buffer), 0, 18835182Swollman (struct sockaddr *)&sin, &size) < 0) 18841553Srgrimes return; 18855182Swollman 18865182Swollman if (check_loop(&sin, sep)) 18875182Swollman return; 18885182Swollman 18891553Srgrimes (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 18908857Srgrimes (void) sendto(s, buffer, strlen(buffer), 0, 18915182Swollman (struct sockaddr *)&sin, sizeof(sin)); 18921553Srgrimes} 18931553Srgrimes 18941553Srgrimes/* 18951553Srgrimes * print_service: 18961553Srgrimes * Dump relevant information to stderr 18971553Srgrimes */ 18981553Srgrimesvoid 18991553Srgrimesprint_service(action, sep) 19001553Srgrimes char *action; 19011553Srgrimes struct servtab *sep; 19021553Srgrimes{ 190319617Sjulian fprintf(stderr, 190430792Sache#ifdef LOGIN_CAP 190538380Sjb "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%p server=%s\n", 190630792Sache#else 190738380Sjb "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%p server=%s\n", 190830792Sache#endif 190919617Sjulian action, sep->se_service, sep->se_proto, 191030807Sache sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group, 191130792Sache#ifdef LOGIN_CAP 191230792Sache sep->se_class, 191330792Sache#endif 191438417Sjb (void *) sep->se_bi, sep->se_server); 19151553Srgrimes} 19161553Srgrimes 19171553Srgrimes/* 19181553Srgrimes * Based on TCPMUX.C by Mark K. Lottor November 1988 19191553Srgrimes * sri-nic::ps:<mkl>tcpmux.c 19201553Srgrimes */ 19211553Srgrimes 19221553Srgrimes 19231553Srgrimesstatic int /* # of characters upto \r,\n or \0 */ 19241553Srgrimesgetline(fd, buf, len) 19251553Srgrimes int fd; 19261553Srgrimes char *buf; 19271553Srgrimes int len; 19281553Srgrimes{ 19291553Srgrimes int count = 0, n; 193035848Sguido struct sigaction sa; 19311553Srgrimes 193235948Sbde sa.sa_flags = 0; 193335948Sbde sigemptyset(&sa.sa_mask); 193435848Sguido sa.sa_handler = SIG_DFL; 193535848Sguido sigaction(SIGALRM, &sa, (struct sigaction *)0); 19361553Srgrimes do { 193735829Sguido alarm(10); 19381553Srgrimes n = read(fd, buf, len-count); 193935829Sguido alarm(0); 19401553Srgrimes if (n == 0) 194142122Sdes return (count); 194242122Sdes if (n < 0) 194342122Sdes return (-1); 19441553Srgrimes while (--n >= 0) { 194542122Sdes if (*buf == '\r' || *buf == '\n' || *buf == '\0') 194642122Sdes return (count); 19471553Srgrimes count++; 19481553Srgrimes buf++; 19491553Srgrimes } 195042122Sdes } while (count < len); 19511553Srgrimes return (count); 19521553Srgrimes} 19531553Srgrimes 19541553Srgrimes#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 19551553Srgrimes 19561553Srgrimes#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 19571553Srgrimes 19581553Srgrimesstruct servtab * 19591553Srgrimestcpmux(s) 19601553Srgrimes int s; 19611553Srgrimes{ 19621553Srgrimes struct servtab *sep; 19631553Srgrimes char service[MAX_SERV_LEN+1]; 19641553Srgrimes int len; 19651553Srgrimes 19661553Srgrimes /* Get requested service name */ 19671553Srgrimes if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 19681553Srgrimes strwrite(s, "-Error reading service name\r\n"); 19691553Srgrimes return (NULL); 19701553Srgrimes } 19711553Srgrimes service[len] = '\0'; 19721553Srgrimes 19731553Srgrimes if (debug) 197429602Scharnier warnx("tcpmux: someone wants %s", service); 19751553Srgrimes 19761553Srgrimes /* 19771553Srgrimes * Help is a required command, and lists available services, 19781553Srgrimes * one per line. 19791553Srgrimes */ 19801553Srgrimes if (!strcasecmp(service, "help")) { 19811553Srgrimes for (sep = servtab; sep; sep = sep->se_next) { 19821553Srgrimes if (!ISMUX(sep)) 19831553Srgrimes continue; 19841553Srgrimes (void)write(s,sep->se_service,strlen(sep->se_service)); 19851553Srgrimes strwrite(s, "\r\n"); 19861553Srgrimes } 19871553Srgrimes return (NULL); 19881553Srgrimes } 19891553Srgrimes 19901553Srgrimes /* Try matching a service in inetd.conf with the request */ 19911553Srgrimes for (sep = servtab; sep; sep = sep->se_next) { 19921553Srgrimes if (!ISMUX(sep)) 19931553Srgrimes continue; 19941553Srgrimes if (!strcasecmp(service, sep->se_service)) { 19951553Srgrimes if (ISMUXPLUS(sep)) { 19961553Srgrimes strwrite(s, "+Go\r\n"); 19971553Srgrimes } 19981553Srgrimes return (sep); 19991553Srgrimes } 20001553Srgrimes } 20011553Srgrimes strwrite(s, "-Service not available\r\n"); 20021553Srgrimes return (NULL); 20031553Srgrimes} 200430847Sdima 200530847Sdima#define CPMHSIZE 256 200630847Sdima#define CPMHMASK (CPMHSIZE-1) 200730847Sdima#define CHTGRAN 10 200830847Sdima#define CHTSIZE 6 200930847Sdima 201030847Sdimatypedef struct CTime { 201130847Sdima unsigned long ct_Ticks; 201230847Sdima int ct_Count; 201330847Sdima} CTime; 201430847Sdima 201530847Sdimatypedef struct CHash { 201630847Sdima struct in_addr ch_Addr; 201730847Sdima time_t ch_LTime; 201830847Sdima char *ch_Service; 201930847Sdima CTime ch_Times[CHTSIZE]; 202030847Sdima} CHash; 202130847Sdima 202230847SdimaCHash CHashAry[CPMHSIZE]; 202330847Sdima 202430847Sdimaint 202530847Sdimacpmip(sep, ctrl) 202630847Sdima struct servtab *sep; 202730847Sdima int ctrl; 202830847Sdima{ 202930847Sdima struct sockaddr_in rsin; 203030847Sdima int rsinLen = sizeof(rsin); 203130847Sdima int r = 0; 203230847Sdima 203330847Sdima /* 203430847Sdima * If getpeername() fails, just let it through (if logging is 203530847Sdima * enabled the condition is caught elsewhere) 203630847Sdima */ 203730847Sdima 203830847Sdima if (sep->se_maxcpm > 0 && 203930847Sdima getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 0 ) { 204030847Sdima time_t t = time(NULL); 204130847Sdima int hv = 0xABC3D20F; 204230847Sdima int i; 204330847Sdima int cnt = 0; 204430847Sdima CHash *chBest = NULL; 204530847Sdima unsigned int ticks = t / CHTGRAN; 204630847Sdima 204730847Sdima { 204830847Sdima char *p; 204930847Sdima int i; 205030847Sdima 205130847Sdima for (i = 0, p = (char *)&rsin.sin_addr; 205230847Sdima i < sizeof(rsin.sin_addr); 205330847Sdima ++i, ++p) { 205430847Sdima hv = (hv << 5) ^ (hv >> 23) ^ *p; 205530847Sdima } 205630847Sdima hv = (hv ^ (hv >> 16)); 205730847Sdima } 205830847Sdima for (i = 0; i < 5; ++i) { 205930847Sdima CHash *ch = &CHashAry[(hv + i) & CPMHMASK]; 206030847Sdima 206130847Sdima if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr && 206230847Sdima ch->ch_Service && strcmp(sep->se_service, 206330847Sdima ch->ch_Service) == 0) { 206430847Sdima chBest = ch; 206530847Sdima break; 206630847Sdima } 206730847Sdima if (chBest == NULL || ch->ch_LTime == 0 || 206830847Sdima ch->ch_LTime < chBest->ch_LTime) { 206930847Sdima chBest = ch; 207030847Sdima } 207130847Sdima } 207230847Sdima if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr || 207330847Sdima chBest->ch_Service == NULL || 207430847Sdima strcmp(sep->se_service, chBest->ch_Service) != 0) { 207530847Sdima chBest->ch_Addr = rsin.sin_addr; 207630847Sdima if (chBest->ch_Service) 207730847Sdima free(chBest->ch_Service); 207830847Sdima chBest->ch_Service = strdup(sep->se_service); 207930847Sdima bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 208030847Sdima } 208130847Sdima chBest->ch_LTime = t; 208230847Sdima { 208330847Sdima CTime *ct = &chBest->ch_Times[ticks % CHTSIZE]; 208430847Sdima if (ct->ct_Ticks != ticks) { 208530847Sdima ct->ct_Ticks = ticks; 208630847Sdima ct->ct_Count = 0; 208730847Sdima } 208830847Sdima ++ct->ct_Count; 208930847Sdima } 209030847Sdima for (i = 0; i < CHTSIZE; ++i) { 209130847Sdima CTime *ct = &chBest->ch_Times[i]; 209230847Sdima if (ct->ct_Ticks <= ticks && 209330847Sdima ct->ct_Ticks >= ticks - CHTSIZE) { 209430847Sdima cnt += ct->ct_Count; 209530847Sdima } 209630847Sdima } 209730847Sdima if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) { 209830847Sdima r = -1; 209930847Sdima syslog(LOG_ERR, 210033794Spst "%s from %s exceeded counts/min (limit %d/min)", 210133794Spst sep->se_service, inet_ntoa(rsin.sin_addr), 210233794Spst sep->se_maxcpm); 210330847Sdima } 210430847Sdima } 210530847Sdima return(r); 210630847Sdima} 2107