inetd.c revision 47972
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[] = 4547972Ssheldonh "$Id: inetd.c,v 1.49 1999/05/11 12:50:14 des 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 14947972Ssheldonhint allow_severity; 15047972Ssheldonhint 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 */ 21045588Smarkm char *se_server_name; /* server program without path */ 2111553Srgrimes#define MAXARGV 20 2121553Srgrimes char *se_argv[MAXARGV+1]; /* program arguments */ 2131553Srgrimes int se_fd; /* open descriptor */ 2141553Srgrimes struct sockaddr_in se_ctrladdr;/* bound address */ 21519618Sjulian u_char se_type; /* type: normal, mux, or mux+ */ 21619618Sjulian u_char se_checked; /* looked at during merge */ 21719618Sjulian u_char se_accept; /* i.e., wait/nowait mode */ 21819618Sjulian u_char se_rpc; /* ==1 if RPC service */ 2192657Scsgr int se_rpc_prog; /* RPC program number */ 2202657Scsgr u_int se_rpc_lowvers; /* RPC low version */ 2212657Scsgr u_int se_rpc_highvers; /* RPC high version */ 2221553Srgrimes int se_count; /* number started since se_time */ 2231553Srgrimes struct timeval se_time; /* start of se_count */ 2241553Srgrimes struct servtab *se_next; 2251553Srgrimes} *servtab; 2261553Srgrimes 2271553Srgrimes#define NORM_TYPE 0 2281553Srgrimes#define MUX_TYPE 1 2291553Srgrimes#define MUXPLUS_TYPE 2 23036042Sguido#define TTCP_TYPE 3 2311553Srgrimes#define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \ 2321553Srgrimes ((sep)->se_type == MUXPLUS_TYPE)) 2331553Srgrimes#define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE) 23436042Sguido#define ISTTCP(sep) ((sep)->se_type == TTCP_TYPE) 2351553Srgrimes 2361553Srgrimes 2371553Srgrimesvoid chargen_dg __P((int, struct servtab *)); 2381553Srgrimesvoid chargen_stream __P((int, struct servtab *)); 2391553Srgrimesvoid close_sep __P((struct servtab *)); 24042122Sdesvoid flag_signal __P((char)); 24142122Sdesvoid flag_config __P((int)); 24242122Sdesvoid config __P((void)); 2431553Srgrimesvoid daytime_dg __P((int, struct servtab *)); 2441553Srgrimesvoid daytime_stream __P((int, struct servtab *)); 2451553Srgrimesvoid discard_dg __P((int, struct servtab *)); 2461553Srgrimesvoid discard_stream __P((int, struct servtab *)); 2471553Srgrimesvoid echo_dg __P((int, struct servtab *)); 2481553Srgrimesvoid echo_stream __P((int, struct servtab *)); 2491553Srgrimesvoid endconfig __P((void)); 2501553Srgrimesstruct servtab *enter __P((struct servtab *)); 2511553Srgrimesvoid freeconfig __P((struct servtab *)); 2521553Srgrimesstruct servtab *getconfigent __P((void)); 25340910Sphkvoid ident_stream __P((int, struct servtab *)); 2541553Srgrimesvoid machtime_dg __P((int, struct servtab *)); 2551553Srgrimesvoid machtime_stream __P((int, struct servtab *)); 2561553Srgrimeschar *newstr __P((char *)); 2571553Srgrimeschar *nextline __P((FILE *)); 2581553Srgrimesvoid print_service __P((char *, struct servtab *)); 25919618Sjulianvoid addchild __P((struct servtab *, int)); 26042122Sdesvoid flag_reapchild __P((int)); 26142122Sdesvoid reapchild __P((void)); 26219618Sjulianvoid enable __P((struct servtab *)); 26319618Sjulianvoid disable __P((struct servtab *)); 26442122Sdesvoid flag_retry __P((int)); 26542122Sdesvoid retry __P((void)); 2661553Srgrimesint setconfig __P((void)); 2671553Srgrimesvoid setup __P((struct servtab *)); 2681553Srgrimeschar *sskip __P((char **)); 2691553Srgrimeschar *skip __P((char **)); 2701553Srgrimesstruct servtab *tcpmux __P((int)); 27130847Sdimaint cpmip __P((struct servtab *, int)); 2721553Srgrimes 2732657Scsgrvoid unregisterrpc __P((register struct servtab *sep)); 2742657Scsgr 2751553Srgrimesstruct biltin { 2761553Srgrimes char *bi_service; /* internally provided service name */ 2771553Srgrimes int bi_socktype; /* type of socket supported */ 2781553Srgrimes short bi_fork; /* 1 if should fork before call */ 27930847Sdima int bi_maxchild; /* max number of children (default) */ 2801553Srgrimes void (*bi_fn)(); /* function which performs it */ 2811553Srgrimes} biltins[] = { 2821553Srgrimes /* Echo received data */ 2831553Srgrimes { "echo", SOCK_STREAM, 1, 0, echo_stream }, 2841553Srgrimes { "echo", SOCK_DGRAM, 0, 0, echo_dg }, 2851553Srgrimes 2861553Srgrimes /* Internet /dev/null */ 2871553Srgrimes { "discard", SOCK_STREAM, 1, 0, discard_stream }, 2881553Srgrimes { "discard", SOCK_DGRAM, 0, 0, discard_dg }, 2891553Srgrimes 29045089Smarkm /* Return 32 bit time since 1970 */ 2911553Srgrimes { "time", SOCK_STREAM, 0, 0, machtime_stream }, 2921553Srgrimes { "time", SOCK_DGRAM, 0, 0, machtime_dg }, 2931553Srgrimes 2941553Srgrimes /* Return human-readable time */ 2951553Srgrimes { "daytime", SOCK_STREAM, 0, 0, daytime_stream }, 2961553Srgrimes { "daytime", SOCK_DGRAM, 0, 0, daytime_dg }, 2971553Srgrimes 2981553Srgrimes /* Familiar character generator */ 2991553Srgrimes { "chargen", SOCK_STREAM, 1, 0, chargen_stream }, 3001553Srgrimes { "chargen", SOCK_DGRAM, 0, 0, chargen_dg }, 3011553Srgrimes 3021553Srgrimes { "tcpmux", SOCK_STREAM, 1, 0, (void (*)())tcpmux }, 3031553Srgrimes 30440910Sphk { "ident", SOCK_STREAM, 1, 0, ident_stream }, 30540910Sphk 3061553Srgrimes { NULL } 3071553Srgrimes}; 3081553Srgrimes 3091553Srgrimes#define NUMINT (sizeof(intab) / sizeof(struct inent)) 3101553Srgrimeschar *CONFIG = _PATH_INETDCONF; 31117482Sjulianchar *pid_file = _PATH_INETDPID; 31213142Speter 31313142Speter#ifdef OLD_SETPROCTITLE 3141553Srgrimeschar **Argv; 3151553Srgrimeschar *LastArg; 31613142Speter#endif 3171553Srgrimes 3181553Srgrimesint 31933794Spstgetvalue(arg, value, whine) 32033794Spst char *arg, *whine; 32133794Spst int *value; 32233794Spst{ 32333794Spst int tmp; 32433794Spst char *p; 32533794Spst 32633794Spst tmp = strtol(arg, &p, 0); 32733794Spst if (tmp < 1 || *p) { 32833794Spst syslog(LOG_ERR, whine, arg); 32933794Spst return 1; /* failure */ 33033794Spst } 33133794Spst *value = tmp; 33233794Spst return 0; /* success */ 33333794Spst} 33433794Spst 33533794Spstint 3361553Srgrimesmain(argc, argv, envp) 3371553Srgrimes int argc; 3381553Srgrimes char *argv[], *envp[]; 3391553Srgrimes{ 3401553Srgrimes struct servtab *sep; 3411553Srgrimes struct passwd *pwd; 34230807Sache struct group *grp; 34335848Sguido struct sigaction sa, sapipe; 3441553Srgrimes int tmpint, ch, dofork; 3451553Srgrimes pid_t pid; 3461553Srgrimes char buf[50]; 34721640Speter#ifdef LOGIN_CAP 34821640Speter login_cap_t *lc = NULL; 34921640Speter#endif 35045089Smarkm#ifdef LIBWRAP 35145089Smarkm struct request_info req; 35245089Smarkm int denied; 35345089Smarkm char *service = NULL; 35447972Ssheldonh#else 35547972Ssheldonh struct sockaddr_in peer; 35647972Ssheldonh int i; 35745089Smarkm#endif 3581553Srgrimes 35913142Speter 36013142Speter#ifdef OLD_SETPROCTITLE 3611553Srgrimes Argv = argv; 3621553Srgrimes if (envp == 0 || *envp == 0) 3631553Srgrimes envp = argv; 3641553Srgrimes while (*envp) 3651553Srgrimes envp++; 3661553Srgrimes LastArg = envp[-1] + strlen(envp[-1]); 36713142Speter#endif 3681553Srgrimes 3691553Srgrimes openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); 3701553Srgrimes 37117482Sjulian bind_address.s_addr = htonl(INADDR_ANY); 37233794Spst while ((ch = getopt(argc, argv, "dlR:a:c:C:p:")) != -1) 3731553Srgrimes switch(ch) { 3741553Srgrimes case 'd': 3751553Srgrimes debug = 1; 3761553Srgrimes options |= SO_DEBUG; 3771553Srgrimes break; 3782659Scsgr case 'l': 3792659Scsgr log = 1; 3802659Scsgr break; 38133794Spst case 'R': 38233794Spst getvalue(optarg, &toomany, 38333794Spst "-R %s: bad value for service invocation rate"); 3841553Srgrimes break; 38533794Spst case 'c': 38633794Spst getvalue(optarg, &maxchild, 38733794Spst "-c %s: bad value for maximum children"); 38833794Spst break; 38933794Spst case 'C': 39033794Spst getvalue(optarg, &maxcpm, 39133794Spst "-C %s: bad value for maximum children/minute"); 39233794Spst break; 39317482Sjulian case 'a': 39417482Sjulian if (!inet_aton(optarg, &bind_address)) { 39517482Sjulian syslog(LOG_ERR, 39617482Sjulian "-a %s: invalid IP address", optarg); 39719617Sjulian exit(EX_USAGE); 39817482Sjulian } 39917482Sjulian break; 40017482Sjulian case 'p': 40117482Sjulian pid_file = optarg; 40217482Sjulian break; 4031553Srgrimes case '?': 4041553Srgrimes default: 4051553Srgrimes syslog(LOG_ERR, 40617482Sjulian "usage: inetd [-dl] [-a address] [-R rate]" 40733794Spst " [-c maximum] [-C rate]" 40817482Sjulian " [-p pidfile] [conf-file]"); 40919617Sjulian exit(EX_USAGE); 4101553Srgrimes } 4111553Srgrimes argc -= optind; 4121553Srgrimes argv += optind; 4131553Srgrimes 4141553Srgrimes if (argc > 0) 4151553Srgrimes CONFIG = argv[0]; 4161553Srgrimes if (debug == 0) { 41711447Swollman FILE *fp; 41812024Speter if (daemon(0, 0) < 0) { 41912024Speter syslog(LOG_WARNING, "daemon(0,0) failed: %m"); 42012024Speter } 42112024Speter /* 42212024Speter * In case somebody has started inetd manually, we need to 42312024Speter * clear the logname, so that old servers run as root do not 42412024Speter * get the user's logname.. 42512024Speter */ 42612024Speter if (setlogin("") < 0) { 42712024Speter syslog(LOG_WARNING, "cannot clear logname: %m"); 42812024Speter /* no big deal if it fails.. */ 42912024Speter } 43011447Swollman pid = getpid(); 43117482Sjulian fp = fopen(pid_file, "w"); 43211447Swollman if (fp) { 43311447Swollman fprintf(fp, "%ld\n", (long)pid); 43411447Swollman fclose(fp); 43511447Swollman } else { 43617482Sjulian syslog(LOG_WARNING, "%s: %m", pid_file); 43711447Swollman } 4381553Srgrimes } 43935948Sbde sa.sa_flags = 0; 44035948Sbde sigemptyset(&sa.sa_mask); 44135948Sbde sigaddset(&sa.sa_mask, SIGALRM); 44235948Sbde sigaddset(&sa.sa_mask, SIGCHLD); 44335948Sbde sigaddset(&sa.sa_mask, SIGHUP); 44442122Sdes sa.sa_handler = flag_retry; 44535848Sguido sigaction(SIGALRM, &sa, (struct sigaction *)0); 44642122Sdes config(); 44742122Sdes sa.sa_handler = flag_config; 44835848Sguido sigaction(SIGHUP, &sa, (struct sigaction *)0); 44942122Sdes sa.sa_handler = flag_reapchild; 45035848Sguido sigaction(SIGCHLD, &sa, (struct sigaction *)0); 45135848Sguido sa.sa_handler = SIG_IGN; 45235948Sbde sigaction(SIGPIPE, &sa, &sapipe); 4531553Srgrimes 4541553Srgrimes { 4551553Srgrimes /* space for daemons to overwrite environment for ps */ 4561553Srgrimes#define DUMMYSIZE 100 4571553Srgrimes char dummy[DUMMYSIZE]; 4581553Srgrimes 45919298Salex (void)memset(dummy, 'x', DUMMYSIZE - 1); 4601553Srgrimes dummy[DUMMYSIZE - 1] = '\0'; 4611553Srgrimes (void)setenv("inetd_dummy", dummy, 1); 4621553Srgrimes } 4631553Srgrimes 46442250Sdes if (pipe(signalpipe) != 0) { 46542250Sdes syslog(LOG_ERR, "pipe: %%m"); 46642250Sdes exit(EX_OSERR); 46742122Sdes } 46842122Sdes FD_SET(signalpipe[0], &allsock); 46947015Sdes nsock++; 47047015Sdes if (signalpipe[0] > maxsock) 47147015Sdes maxsock = signalpipe[0]; 47241685Sdillon 4731553Srgrimes for (;;) { 4741553Srgrimes int n, ctrl; 4751553Srgrimes fd_set readable; 4761553Srgrimes 4771553Srgrimes if (nsock == 0) { 47847015Sdes syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 47947015Sdes exit(EX_SOFTWARE); 4801553Srgrimes } 4811553Srgrimes readable = allsock; 48242122Sdes if ((n = select(maxsock + 1, &readable, (fd_set *)0, 48342122Sdes (fd_set *)0, (struct timeval *)0)) <= 0) { 48442122Sdes if (n < 0 && errno != EINTR) { 4851553Srgrimes syslog(LOG_WARNING, "select: %m"); 48628907Simp sleep(1); 48728907Simp } 4881553Srgrimes continue; 4891553Srgrimes } 49042122Sdes /* handle any queued signal flags */ 49142250Sdes if (FD_ISSET(signalpipe[0], &readable)) { 49242122Sdes int n; 49342250Sdes if (ioctl(signalpipe[0], FIONREAD, &n) != 0) { 49442122Sdes syslog(LOG_ERR, "ioctl: %m"); 49542122Sdes exit(EX_OSERR); 49642122Sdes } 49742250Sdes while (--n >= 0) { 49842250Sdes char c; 49942250Sdes if (read(signalpipe[0], &c, 1) != 1) { 50042250Sdes syslog(LOG_ERR, "read: %m"); 50142250Sdes exit(EX_OSERR); 50242250Sdes } 50342250Sdes if (debug) 50442250Sdes warnx("Handling signal flag %c", c); 50542250Sdes switch(c) { 50642250Sdes case 'A': /* sigalrm */ 50742250Sdes retry(); 50842250Sdes break; 50942250Sdes case 'C': /* sigchld */ 51042250Sdes reapchild(); 51142250Sdes break; 51242250Sdes case 'H': /* sighup */ 51342250Sdes config(); 51442250Sdes break; 51542250Sdes } 51642250Sdes } 51742122Sdes } 5181553Srgrimes for (sep = servtab; n && sep; sep = sep->se_next) 5191553Srgrimes if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { 5201553Srgrimes n--; 5211553Srgrimes if (debug) 52229602Scharnier warnx("someone wants %s", sep->se_service); 52319618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) { 5241553Srgrimes ctrl = accept(sep->se_fd, (struct sockaddr *)0, 5251553Srgrimes (int *)0); 5261553Srgrimes if (debug) 52729602Scharnier warnx("accept, ctrl %d", ctrl); 5281553Srgrimes if (ctrl < 0) { 5291553Srgrimes if (errno != EINTR) 5301553Srgrimes syslog(LOG_WARNING, 5311553Srgrimes "accept (for %s): %m", 53237844Sphk sep->se_service); 53337816Sphk if (sep->se_accept && 53437816Sphk sep->se_socktype == SOCK_STREAM) 53537816Sphk close(ctrl); 5361553Srgrimes continue; 5371553Srgrimes } 53830847Sdima if (cpmip(sep, ctrl) < 0) { 53930847Sdima close(ctrl); 54030847Sdima continue; 54130847Sdima } 54247972Ssheldonh#ifndef LIBWRAP 54319617Sjulian if (log) { 5442659Scsgr i = sizeof peer; 54530847Sdima if (getpeername(ctrl, (struct sockaddr *) 5462659Scsgr &peer, &i)) { 5472659Scsgr syslog(LOG_WARNING, 5482659Scsgr "getpeername(for %s): %m", 5492659Scsgr sep->se_service); 55030847Sdima close(ctrl); 5512659Scsgr continue; 5522659Scsgr } 5532659Scsgr syslog(LOG_INFO,"%s from %s", 5542659Scsgr sep->se_service, 5552659Scsgr inet_ntoa(peer.sin_addr)); 5562659Scsgr } 55747972Ssheldonh#endif 5581553Srgrimes } else 5591553Srgrimes ctrl = sep->se_fd; 56042122Sdes (void) sigblock(SIGBLOCK); 5611553Srgrimes pid = 0; 56245089Smarkm#ifdef LIBWRAP_INTERNAL 56347972Ssheldonh /* 56447972Ssheldonh * When builtins are wrapped, avoid a minor optimization 56547972Ssheldonh * that breaks hosts_options(5) twist. 56647972Ssheldonh */ 56745089Smarkm dofork = 1; 56845089Smarkm#else 5691553Srgrimes dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork); 57045089Smarkm#endif 5711553Srgrimes if (dofork) { 5721553Srgrimes if (sep->se_count++ == 0) 57337856Sache (void)gettimeofday(&sep->se_time, (struct timezone *)NULL); 5741553Srgrimes else if (sep->se_count >= toomany) { 5751553Srgrimes struct timeval now; 5761553Srgrimes 57737856Sache (void)gettimeofday(&now, (struct timezone *)NULL); 5781553Srgrimes if (now.tv_sec - sep->se_time.tv_sec > 5791553Srgrimes CNT_INTVL) { 5801553Srgrimes sep->se_time = now; 5811553Srgrimes sep->se_count = 1; 5821553Srgrimes } else { 5831553Srgrimes syslog(LOG_ERR, 5841553Srgrimes "%s/%s server failing (looping), service terminated", 5851553Srgrimes sep->se_service, sep->se_proto); 5861553Srgrimes close_sep(sep); 58742122Sdes sigsetmask(0L); 5881553Srgrimes if (!timingout) { 5891553Srgrimes timingout = 1; 5901553Srgrimes alarm(RETRYTIME); 5911553Srgrimes } 5921553Srgrimes continue; 5931553Srgrimes } 5941553Srgrimes } 5951553Srgrimes pid = fork(); 5961553Srgrimes } 5971553Srgrimes if (pid < 0) { 5981553Srgrimes syslog(LOG_ERR, "fork: %m"); 59919618Sjulian if (sep->se_accept && 6001553Srgrimes sep->se_socktype == SOCK_STREAM) 6011553Srgrimes close(ctrl); 60242122Sdes sigsetmask(0L); 6031553Srgrimes sleep(1); 6041553Srgrimes continue; 6051553Srgrimes } 60619618Sjulian if (pid) 60719618Sjulian addchild(sep, pid); 60842122Sdes sigsetmask(0L); 6091553Srgrimes if (pid == 0) { 6101553Srgrimes if (dofork) { 6111553Srgrimes if (debug) 61229602Scharnier warnx("+ closing from %d", maxsock); 6131553Srgrimes for (tmpint = maxsock; tmpint > 2; tmpint--) 6141553Srgrimes if (tmpint != ctrl) 61519617Sjulian (void) close(tmpint); 6161553Srgrimes } 61735829Sguido /* 61835829Sguido * Call tcpmux to find the real service to exec. 61935829Sguido */ 62035829Sguido if (sep->se_bi && 62135829Sguido sep->se_bi->bi_fn == (void (*)()) tcpmux) { 62235829Sguido sep = tcpmux(ctrl); 62335829Sguido if (sep == NULL) { 62435829Sguido close(ctrl); 62535829Sguido _exit(0); 62635829Sguido } 62735829Sguido } 62845089Smarkm#ifdef LIBWRAP 62945089Smarkm#ifndef LIBWRAP_INTERNAL 63045089Smarkm if (sep->se_bi == 0) 63145089Smarkm#endif 63245089Smarkm if (sep->se_accept 63345089Smarkm && sep->se_socktype == SOCK_STREAM) { 63447972Ssheldonh service = sep->se_server_name ? 63547972Ssheldonh sep->se_server_name : sep->se_service; 63647972Ssheldonh request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, NULL); 63745089Smarkm fromhost(&req); 63847972Ssheldonh deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY; 63947972Ssheldonh allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY; 64045089Smarkm denied = !hosts_access(&req); 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); 7481553Srgrimes } 74947972Ssheldonh if (dofork) 75047972Ssheldonh _exit(0); 7511553Srgrimes } 75219618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) 7531553Srgrimes close(ctrl); 7541553Srgrimes } 7551553Srgrimes } 7561553Srgrimes} 7571553Srgrimes 75819618Sjulian/* 75942122Sdes * Add a signal flag to the signal flag queue for later handling 76042122Sdes */ 76142122Sdes 76242122Sdesvoid flag_signal(c) 76342122Sdes char c; 76442122Sdes{ 76542250Sdes if (write(signalpipe[1], &c, 1) != 1) { 76642250Sdes syslog(LOG_ERR, "write: %m"); 76742250Sdes exit(EX_OSERR); 76842250Sdes } 76942122Sdes} 77042122Sdes 77142122Sdes/* 77219618Sjulian * Record a new child pid for this service. If we've reached the 77319618Sjulian * limit on children, then stop accepting incoming requests. 77419618Sjulian */ 77519618Sjulian 7761553Srgrimesvoid 77719618Sjulianaddchild(struct servtab *sep, pid_t pid) 77819618Sjulian{ 77919618Sjulian#ifdef SANITY_CHECK 78019618Sjulian if (sep->se_numchild >= sep->se_maxchild) { 78119618Sjulian syslog(LOG_ERR, "%s: %d >= %d", 78219618Sjulian __FUNCTION__, sep->se_numchild, sep->se_maxchild); 78319618Sjulian exit(EX_SOFTWARE); 78419618Sjulian } 78519618Sjulian#endif 78619618Sjulian if (sep->se_maxchild == 0) 78719618Sjulian return; 78819618Sjulian sep->se_pids[sep->se_numchild++] = pid; 78919618Sjulian if (sep->se_numchild == sep->se_maxchild) 79019618Sjulian disable(sep); 79119618Sjulian} 79219618Sjulian 79319618Sjulian/* 79419618Sjulian * Some child process has exited. See if it's on somebody's list. 79519618Sjulian */ 79619618Sjulian 79719618Sjulianvoid 79842122Sdesflag_reapchild(signo) 7991553Srgrimes int signo; 8001553Srgrimes{ 80142250Sdes flag_signal('C'); 80242122Sdes} 80342122Sdes 80442122Sdesvoid 80542122Sdesreapchild() 80642122Sdes{ 80719618Sjulian int k, status; 8081553Srgrimes pid_t pid; 8091553Srgrimes struct servtab *sep; 8101553Srgrimes 8111553Srgrimes for (;;) { 8121553Srgrimes pid = wait3(&status, WNOHANG, (struct rusage *)0); 8131553Srgrimes if (pid <= 0) 8141553Srgrimes break; 8151553Srgrimes if (debug) 81629602Scharnier warnx("%d reaped, status %#x", pid, status); 81719618Sjulian for (sep = servtab; sep; sep = sep->se_next) { 81819618Sjulian for (k = 0; k < sep->se_numchild; k++) 81919618Sjulian if (sep->se_pids[k] == pid) 82019618Sjulian break; 82119618Sjulian if (k == sep->se_numchild) 82219618Sjulian continue; 82319618Sjulian if (sep->se_numchild == sep->se_maxchild) 82419618Sjulian enable(sep); 82519618Sjulian sep->se_pids[k] = sep->se_pids[--sep->se_numchild]; 82619618Sjulian if (status) 82719618Sjulian syslog(LOG_WARNING, 82819618Sjulian "%s[%d]: exit status 0x%x", 82919618Sjulian sep->se_server, pid, status); 83019618Sjulian break; 83119618Sjulian } 8321553Srgrimes } 8331553Srgrimes} 8341553Srgrimes 8351553Srgrimesvoid 83642122Sdesflag_config(signo) 8371553Srgrimes int signo; 8381553Srgrimes{ 83942250Sdes flag_signal('H'); 84042122Sdes} 84142122Sdes 84242122Sdesvoid config() 84342122Sdes{ 84419618Sjulian struct servtab *sep, *new, **sepp; 84542122Sdes long omask; 8461553Srgrimes 8471553Srgrimes if (!setconfig()) { 8481553Srgrimes syslog(LOG_ERR, "%s: %m", CONFIG); 8491553Srgrimes return; 8501553Srgrimes } 8511553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 8521553Srgrimes sep->se_checked = 0; 85319618Sjulian while ((new = getconfigent())) { 85430807Sache if (getpwnam(new->se_user) == NULL) { 8551553Srgrimes syslog(LOG_ERR, 8561553Srgrimes "%s/%s: No such user '%s', service ignored", 85719618Sjulian new->se_service, new->se_proto, new->se_user); 8581553Srgrimes continue; 8591553Srgrimes } 86030807Sache if (new->se_group && getgrnam(new->se_group) == NULL) { 86130807Sache syslog(LOG_ERR, 86230807Sache "%s/%s: No such group '%s', service ignored", 86330807Sache new->se_service, new->se_proto, new->se_group); 86430807Sache continue; 86530807Sache } 86630792Sache#ifdef LOGIN_CAP 86730792Sache if (login_getclass(new->se_class) == NULL) { 86830792Sache /* error syslogged by getclass */ 86930792Sache syslog(LOG_ERR, 87037850Sache "%s/%s: %s: login class error, service ignored", 87137850Sache new->se_service, new->se_proto, new->se_class); 87230792Sache continue; 87330792Sache } 87430792Sache#endif 8751553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 87619618Sjulian if (strcmp(sep->se_service, new->se_service) == 0 && 87719618Sjulian strcmp(sep->se_proto, new->se_proto) == 0) 8781553Srgrimes break; 8791553Srgrimes if (sep != 0) { 8801553Srgrimes int i; 8811553Srgrimes 88219618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; } 88342122Sdes omask = sigblock(SIGBLOCK); 88419618Sjulian /* copy over outstanding child pids */ 88519618Sjulian if (sep->se_maxchild && new->se_maxchild) { 88619618Sjulian new->se_numchild = sep->se_numchild; 88719618Sjulian if (new->se_numchild > new->se_maxchild) 88819618Sjulian new->se_numchild = new->se_maxchild; 88919618Sjulian memcpy(new->se_pids, sep->se_pids, 89019618Sjulian new->se_numchild * sizeof(*new->se_pids)); 89119618Sjulian } 89219618Sjulian SWAP(sep->se_pids, new->se_pids); 89319618Sjulian sep->se_maxchild = new->se_maxchild; 89419618Sjulian sep->se_numchild = new->se_numchild; 89530847Sdima sep->se_maxcpm = new->se_maxcpm; 89619618Sjulian /* might need to turn on or off service now */ 89719618Sjulian if (sep->se_fd >= 0) { 89819618Sjulian if (sep->se_maxchild 89919618Sjulian && sep->se_numchild == sep->se_maxchild) { 90019618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 90119618Sjulian disable(sep); 90219618Sjulian } else { 90319618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) 90419618Sjulian enable(sep); 90519618Sjulian } 90619618Sjulian } 90719618Sjulian sep->se_accept = new->se_accept; 90830807Sache SWAP(sep->se_user, new->se_user); 90930807Sache SWAP(sep->se_group, new->se_group); 91030792Sache#ifdef LOGIN_CAP 91130807Sache SWAP(sep->se_class, new->se_class); 91230792Sache#endif 91330807Sache SWAP(sep->se_server, new->se_server); 91447972Ssheldonh SWAP(sep->se_server_name, new->se_server_name); 9151553Srgrimes for (i = 0; i < MAXARGV; i++) 91619618Sjulian SWAP(sep->se_argv[i], new->se_argv[i]); 91742122Sdes sigsetmask(omask); 91819618Sjulian freeconfig(new); 9191553Srgrimes if (debug) 9201553Srgrimes print_service("REDO", sep); 9211553Srgrimes } else { 92219618Sjulian sep = enter(new); 9231553Srgrimes if (debug) 9241553Srgrimes print_service("ADD ", sep); 9251553Srgrimes } 9261553Srgrimes sep->se_checked = 1; 9271553Srgrimes if (ISMUX(sep)) { 9281553Srgrimes sep->se_fd = -1; 9291553Srgrimes continue; 9301553Srgrimes } 9312657Scsgr if (!sep->se_rpc) { 9322657Scsgr sp = getservbyname(sep->se_service, sep->se_proto); 9332657Scsgr if (sp == 0) { 9342657Scsgr syslog(LOG_ERR, "%s/%s: unknown service", 9352657Scsgr sep->se_service, sep->se_proto); 9362657Scsgr sep->se_checked = 0; 9372657Scsgr continue; 9382657Scsgr } 9392657Scsgr if (sp->s_port != sep->se_ctrladdr.sin_port) { 9402657Scsgr sep->se_ctrladdr.sin_family = AF_INET; 94122306Sjulian sep->se_ctrladdr.sin_addr = bind_address; 9422657Scsgr sep->se_ctrladdr.sin_port = sp->s_port; 9432657Scsgr if (sep->se_fd >= 0) 9442657Scsgr close_sep(sep); 9452657Scsgr } 9462657Scsgr } else { 9472657Scsgr rpc = getrpcbyname(sep->se_service); 9482657Scsgr if (rpc == 0) { 9492657Scsgr syslog(LOG_ERR, "%s/%s unknown RPC service.", 9502657Scsgr sep->se_service, sep->se_proto); 9512657Scsgr if (sep->se_fd != -1) 9522657Scsgr (void) close(sep->se_fd); 9532657Scsgr sep->se_fd = -1; 9542657Scsgr continue; 9552657Scsgr } 9562657Scsgr if (rpc->r_number != sep->se_rpc_prog) { 9572657Scsgr if (sep->se_rpc_prog) 9582657Scsgr unregisterrpc(sep); 9592657Scsgr sep->se_rpc_prog = rpc->r_number; 9602657Scsgr if (sep->se_fd != -1) 9612657Scsgr (void) close(sep->se_fd); 9622657Scsgr sep->se_fd = -1; 9632657Scsgr } 9641553Srgrimes } 9651553Srgrimes if (sep->se_fd == -1) 9661553Srgrimes setup(sep); 9671553Srgrimes } 9681553Srgrimes endconfig(); 9691553Srgrimes /* 9701553Srgrimes * Purge anything not looked at above. 9711553Srgrimes */ 97242122Sdes omask = sigblock(SIGBLOCK); 9731553Srgrimes sepp = &servtab; 97419617Sjulian while ((sep = *sepp)) { 9751553Srgrimes if (sep->se_checked) { 9761553Srgrimes sepp = &sep->se_next; 9771553Srgrimes continue; 9781553Srgrimes } 9791553Srgrimes *sepp = sep->se_next; 9801553Srgrimes if (sep->se_fd >= 0) 9811553Srgrimes close_sep(sep); 9821553Srgrimes if (debug) 9831553Srgrimes print_service("FREE", sep); 9842657Scsgr if (sep->se_rpc && sep->se_rpc_prog > 0) 9852657Scsgr unregisterrpc(sep); 9861553Srgrimes freeconfig(sep); 9871553Srgrimes free((char *)sep); 9881553Srgrimes } 98942122Sdes (void) sigsetmask(omask); 9901553Srgrimes} 9911553Srgrimes 9921553Srgrimesvoid 9932657Scsgrunregisterrpc(sep) 9942657Scsgr struct servtab *sep; 9952657Scsgr{ 9962657Scsgr int i; 9972657Scsgr struct servtab *sepp; 99842122Sdes long omask; 9992657Scsgr 100042122Sdes omask = sigblock(SIGBLOCK); 10012657Scsgr for (sepp = servtab; sepp; sepp = sepp->se_next) { 10022657Scsgr if (sepp == sep) 10032657Scsgr continue; 10042657Scsgr if (sep->se_checked == 0 || 10052657Scsgr !sepp->se_rpc || 10062657Scsgr sep->se_rpc_prog != sepp->se_rpc_prog) 10072657Scsgr continue; 10082657Scsgr return; 10092657Scsgr } 10102657Scsgr if (debug) 10112657Scsgr print_service("UNREG", sep); 10122657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) 10132657Scsgr pmap_unset(sep->se_rpc_prog, i); 10142657Scsgr if (sep->se_fd != -1) 10152657Scsgr (void) close(sep->se_fd); 10162657Scsgr sep->se_fd = -1; 101742122Sdes (void) sigsetmask(omask); 10182657Scsgr} 10192657Scsgr 10202657Scsgrvoid 102142122Sdesflag_retry(signo) 10221553Srgrimes int signo; 10231553Srgrimes{ 102442250Sdes flag_signal('A'); 102542122Sdes} 102642122Sdes 102742122Sdesvoid 102842122Sdesretry() 102942122Sdes{ 10301553Srgrimes struct servtab *sep; 10311553Srgrimes 10321553Srgrimes timingout = 0; 10331553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 103419617Sjulian if (sep->se_fd == -1 && !ISMUX(sep)) 10351553Srgrimes setup(sep); 10361553Srgrimes} 10371553Srgrimes 10381553Srgrimesvoid 10391553Srgrimessetup(sep) 10401553Srgrimes struct servtab *sep; 10411553Srgrimes{ 10421553Srgrimes int on = 1; 10431553Srgrimes 10441553Srgrimes if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) { 10451553Srgrimes if (debug) 104629602Scharnier warn("socket failed on %s/%s", 104729602Scharnier sep->se_service, sep->se_proto); 10481553Srgrimes syslog(LOG_ERR, "%s/%s: socket: %m", 10491553Srgrimes sep->se_service, sep->se_proto); 10501553Srgrimes return; 10511553Srgrimes } 10521553Srgrimes#define turnon(fd, opt) \ 10531553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) 10541553Srgrimes if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && 10551553Srgrimes turnon(sep->se_fd, SO_DEBUG) < 0) 10561553Srgrimes syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 10571553Srgrimes if (turnon(sep->se_fd, SO_REUSEADDR) < 0) 10581553Srgrimes syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); 105925253Swollman#ifdef SO_PRIVSTATE 106013956Swollman if (turnon(sep->se_fd, SO_PRIVSTATE) < 0) 106113956Swollman syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m"); 106225253Swollman#endif 10631553Srgrimes#undef turnon 106436042Sguido if (sep->se_type == TTCP_TYPE) 106536042Sguido if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH, 106636042Sguido (char *)&on, sizeof (on)) < 0) 106736042Sguido syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m"); 10681553Srgrimes if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, 10691553Srgrimes sizeof (sep->se_ctrladdr)) < 0) { 10701553Srgrimes if (debug) 107129602Scharnier warn("bind failed on %s/%s", 107229602Scharnier sep->se_service, sep->se_proto); 10731553Srgrimes syslog(LOG_ERR, "%s/%s: bind: %m", 10741553Srgrimes sep->se_service, sep->se_proto); 10751553Srgrimes (void) close(sep->se_fd); 10761553Srgrimes sep->se_fd = -1; 10771553Srgrimes if (!timingout) { 10781553Srgrimes timingout = 1; 10791553Srgrimes alarm(RETRYTIME); 10801553Srgrimes } 10811553Srgrimes return; 10821553Srgrimes } 10832657Scsgr if (sep->se_rpc) { 10842657Scsgr int i, len = sizeof(struct sockaddr); 10852657Scsgr 10868857Srgrimes if (getsockname(sep->se_fd, 10872657Scsgr (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){ 10882657Scsgr syslog(LOG_ERR, "%s/%s: getsockname: %m", 10892657Scsgr sep->se_service, sep->se_proto); 10902657Scsgr (void) close(sep->se_fd); 10912657Scsgr sep->se_fd = -1; 10928857Srgrimes return; 10932657Scsgr } 10942657Scsgr if (debug) 10952657Scsgr print_service("REG ", sep); 10962657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { 10972657Scsgr pmap_unset(sep->se_rpc_prog, i); 10982657Scsgr pmap_set(sep->se_rpc_prog, i, 10992657Scsgr (sep->se_socktype == SOCK_DGRAM) 11002657Scsgr ? IPPROTO_UDP : IPPROTO_TCP, 11012657Scsgr ntohs(sep->se_ctrladdr.sin_port)); 11022657Scsgr } 11038857Srgrimes 11042657Scsgr } 11051553Srgrimes if (sep->se_socktype == SOCK_STREAM) 110617197Sdg listen(sep->se_fd, 64); 110719618Sjulian enable(sep); 11081553Srgrimes if (debug) { 110929602Scharnier warnx("registered %s on %d", 11101553Srgrimes sep->se_server, sep->se_fd); 11111553Srgrimes } 11121553Srgrimes} 11131553Srgrimes 11141553Srgrimes/* 11151553Srgrimes * Finish with a service and its socket. 11161553Srgrimes */ 11171553Srgrimesvoid 11181553Srgrimesclose_sep(sep) 11191553Srgrimes struct servtab *sep; 11201553Srgrimes{ 11211553Srgrimes if (sep->se_fd >= 0) { 112219618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 112319618Sjulian disable(sep); 11241553Srgrimes (void) close(sep->se_fd); 11251553Srgrimes sep->se_fd = -1; 11261553Srgrimes } 11271553Srgrimes sep->se_count = 0; 112819618Sjulian sep->se_numchild = 0; /* forget about any existing children */ 11291553Srgrimes} 11301553Srgrimes 11311553Srgrimesstruct servtab * 11321553Srgrimesenter(cp) 11331553Srgrimes struct servtab *cp; 11341553Srgrimes{ 11351553Srgrimes struct servtab *sep; 113642122Sdes long omask; 11371553Srgrimes 11381553Srgrimes sep = (struct servtab *)malloc(sizeof (*sep)); 11391553Srgrimes if (sep == (struct servtab *)0) { 11401553Srgrimes syslog(LOG_ERR, "Out of memory."); 114119617Sjulian exit(EX_OSERR); 11421553Srgrimes } 11431553Srgrimes *sep = *cp; 11441553Srgrimes sep->se_fd = -1; 114542122Sdes omask = sigblock(SIGBLOCK); 11461553Srgrimes sep->se_next = servtab; 11471553Srgrimes servtab = sep; 114842122Sdes sigsetmask(omask); 11491553Srgrimes return (sep); 11501553Srgrimes} 11511553Srgrimes 115219618Sjulianvoid 115319618Sjulianenable(struct servtab *sep) 115419618Sjulian{ 115519618Sjulian if (debug) 115629602Scharnier warnx( 115719618Sjulian "enabling %s, fd %d", sep->se_service, sep->se_fd); 115819618Sjulian#ifdef SANITY_CHECK 115919618Sjulian if (sep->se_fd < 0) { 116019618Sjulian syslog(LOG_ERR, 116119618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 116219618Sjulian exit(EX_SOFTWARE); 116319618Sjulian } 116419618Sjulian if (ISMUX(sep)) { 116519618Sjulian syslog(LOG_ERR, 116619618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 116719618Sjulian exit(EX_SOFTWARE); 116819618Sjulian } 116919618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) { 117019618Sjulian syslog(LOG_ERR, 117119618Sjulian "%s: %s: not off", __FUNCTION__, sep->se_service); 117219618Sjulian exit(EX_SOFTWARE); 117319618Sjulian } 117419618Sjulian#endif 117519618Sjulian FD_SET(sep->se_fd, &allsock); 117619618Sjulian nsock++; 117719618Sjulian if (sep->se_fd > maxsock) 117819618Sjulian maxsock = sep->se_fd; 117919618Sjulian} 118019618Sjulian 118119618Sjulianvoid 118219618Sjuliandisable(struct servtab *sep) 118319618Sjulian{ 118419618Sjulian if (debug) 118529602Scharnier warnx( 118619618Sjulian "disabling %s, fd %d", sep->se_service, sep->se_fd); 118719618Sjulian#ifdef SANITY_CHECK 118819618Sjulian if (sep->se_fd < 0) { 118919618Sjulian syslog(LOG_ERR, 119019618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 119119618Sjulian exit(EX_SOFTWARE); 119219618Sjulian } 119319618Sjulian if (ISMUX(sep)) { 119419618Sjulian syslog(LOG_ERR, 119519618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 119619618Sjulian exit(EX_SOFTWARE); 119719618Sjulian } 119819618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) { 119919618Sjulian syslog(LOG_ERR, 120019618Sjulian "%s: %s: not on", __FUNCTION__, sep->se_service); 120119618Sjulian exit(EX_SOFTWARE); 120219618Sjulian } 120319618Sjulian if (nsock == 0) { 120419618Sjulian syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 120519618Sjulian exit(EX_SOFTWARE); 120619618Sjulian } 120719618Sjulian#endif 120819618Sjulian FD_CLR(sep->se_fd, &allsock); 120919618Sjulian nsock--; 121019618Sjulian if (sep->se_fd == maxsock) 121119618Sjulian maxsock--; 121219618Sjulian} 121319618Sjulian 12141553SrgrimesFILE *fconfig = NULL; 12151553Srgrimesstruct servtab serv; 12161553Srgrimeschar line[LINE_MAX]; 12171553Srgrimes 12181553Srgrimesint 12191553Srgrimessetconfig() 12201553Srgrimes{ 12211553Srgrimes 12221553Srgrimes if (fconfig != NULL) { 12231553Srgrimes fseek(fconfig, 0L, SEEK_SET); 12241553Srgrimes return (1); 12251553Srgrimes } 12261553Srgrimes fconfig = fopen(CONFIG, "r"); 12271553Srgrimes return (fconfig != NULL); 12281553Srgrimes} 12291553Srgrimes 12301553Srgrimesvoid 12311553Srgrimesendconfig() 12321553Srgrimes{ 12331553Srgrimes if (fconfig) { 12341553Srgrimes (void) fclose(fconfig); 12351553Srgrimes fconfig = NULL; 12361553Srgrimes } 12371553Srgrimes} 12381553Srgrimes 12391553Srgrimesstruct servtab * 12401553Srgrimesgetconfigent() 12411553Srgrimes{ 12421553Srgrimes struct servtab *sep = &serv; 12431553Srgrimes int argc; 124419618Sjulian char *cp, *arg, *s; 12452657Scsgr char *versp; 12461553Srgrimes static char TCPMUX_TOKEN[] = "tcpmux/"; 12471553Srgrimes#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1) 12481553Srgrimes 12491553Srgrimesmore: 12501553Srgrimes while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0')) 12511553Srgrimes ; 12521553Srgrimes if (cp == NULL) 12531553Srgrimes return ((struct servtab *)0); 12541553Srgrimes /* 12551553Srgrimes * clear the static buffer, since some fields (se_ctrladdr, 12561553Srgrimes * for example) don't get initialized here. 12571553Srgrimes */ 12581553Srgrimes memset((caddr_t)sep, 0, sizeof *sep); 12591553Srgrimes arg = skip(&cp); 12601553Srgrimes if (cp == NULL) { 12611553Srgrimes /* got an empty line containing just blanks/tabs. */ 12621553Srgrimes goto more; 12631553Srgrimes } 12641553Srgrimes if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { 12651553Srgrimes char *c = arg + MUX_LEN; 12661553Srgrimes if (*c == '+') { 12671553Srgrimes sep->se_type = MUXPLUS_TYPE; 12681553Srgrimes c++; 12691553Srgrimes } else 12701553Srgrimes sep->se_type = MUX_TYPE; 12711553Srgrimes sep->se_service = newstr(c); 12721553Srgrimes } else { 12731553Srgrimes sep->se_service = newstr(arg); 12741553Srgrimes sep->se_type = NORM_TYPE; 12751553Srgrimes } 12761553Srgrimes arg = sskip(&cp); 12771553Srgrimes if (strcmp(arg, "stream") == 0) 12781553Srgrimes sep->se_socktype = SOCK_STREAM; 12791553Srgrimes else if (strcmp(arg, "dgram") == 0) 12801553Srgrimes sep->se_socktype = SOCK_DGRAM; 12811553Srgrimes else if (strcmp(arg, "rdm") == 0) 12821553Srgrimes sep->se_socktype = SOCK_RDM; 12831553Srgrimes else if (strcmp(arg, "seqpacket") == 0) 12841553Srgrimes sep->se_socktype = SOCK_SEQPACKET; 12851553Srgrimes else if (strcmp(arg, "raw") == 0) 12861553Srgrimes sep->se_socktype = SOCK_RAW; 12871553Srgrimes else 12881553Srgrimes sep->se_socktype = -1; 128936042Sguido 129036042Sguido arg = sskip(&cp); 129136042Sguido if (strcmp(arg, "tcp/ttcp") == 0) { 129236042Sguido sep->se_type = TTCP_TYPE; 129336042Sguido sep->se_proto = newstr("tcp"); 129436042Sguido } else { 129536042Sguido sep->se_proto = newstr(arg); 129636042Sguido } 12972657Scsgr if (strncmp(sep->se_proto, "rpc/", 4) == 0) { 129819237Sjoerg memmove(sep->se_proto, sep->se_proto + 4, 129919237Sjoerg strlen(sep->se_proto) + 1 - 4); 13002657Scsgr sep->se_rpc = 1; 13012657Scsgr sep->se_rpc_prog = sep->se_rpc_lowvers = 13022657Scsgr sep->se_rpc_lowvers = 0; 13032657Scsgr sep->se_ctrladdr.sin_family = AF_INET; 13042657Scsgr sep->se_ctrladdr.sin_port = 0; 130517482Sjulian sep->se_ctrladdr.sin_addr = bind_address; 13062657Scsgr if ((versp = rindex(sep->se_service, '/'))) { 13072657Scsgr *versp++ = '\0'; 13082657Scsgr switch (sscanf(versp, "%d-%d", 13092657Scsgr &sep->se_rpc_lowvers, 13102657Scsgr &sep->se_rpc_highvers)) { 13112657Scsgr case 2: 13122657Scsgr break; 13132657Scsgr case 1: 13142657Scsgr sep->se_rpc_highvers = 13152657Scsgr sep->se_rpc_lowvers; 13162657Scsgr break; 13172657Scsgr default: 13188857Srgrimes syslog(LOG_ERR, 13198857Srgrimes "bad RPC version specifier; %s\n", 13202657Scsgr sep->se_service); 13212657Scsgr freeconfig(sep); 13222657Scsgr goto more; 13232657Scsgr } 13242657Scsgr } 13252657Scsgr else { 13262657Scsgr sep->se_rpc_lowvers = 13272657Scsgr sep->se_rpc_highvers = 1; 13282657Scsgr } 13292657Scsgr } 13301553Srgrimes arg = sskip(&cp); 133119618Sjulian if (!strncmp(arg, "wait", 4)) 133219618Sjulian sep->se_accept = 0; 133319618Sjulian else if (!strncmp(arg, "nowait", 6)) 133419618Sjulian sep->se_accept = 1; 133519618Sjulian else { 133619618Sjulian syslog(LOG_ERR, 133719618Sjulian "%s: bad wait/nowait for service %s", 133819618Sjulian CONFIG, sep->se_service); 133919618Sjulian goto more; 134019618Sjulian } 134133794Spst sep->se_maxchild = maxchild; 134233794Spst sep->se_maxcpm = maxcpm; 134319618Sjulian if ((s = strchr(arg, '/')) != NULL) { 134419618Sjulian char *eptr; 134519618Sjulian u_long val; 134619618Sjulian 134719618Sjulian val = strtoul(s + 1, &eptr, 10); 134830847Sdima if (eptr == s + 1 || val > MAX_MAXCHLD) { 134919618Sjulian syslog(LOG_ERR, 135019618Sjulian "%s: bad max-child for service %s", 135119618Sjulian CONFIG, sep->se_service); 135219618Sjulian goto more; 135319618Sjulian } 135419618Sjulian sep->se_maxchild = val; 135530847Sdima if (*eptr == '/') 135630847Sdima sep->se_maxcpm = strtol(eptr + 1, &eptr, 10); 135730847Sdima /* 135830847Sdima * explicitly do not check for \0 for future expansion / 135930847Sdima * backwards compatibility 136030847Sdima */ 136119618Sjulian } 13621553Srgrimes if (ISMUX(sep)) { 13631553Srgrimes /* 136419618Sjulian * Silently enforce "nowait" mode for TCPMUX services 136519618Sjulian * since they don't have an assigned port to listen on. 13661553Srgrimes */ 136719618Sjulian sep->se_accept = 1; 13681553Srgrimes if (strcmp(sep->se_proto, "tcp")) { 13698857Srgrimes syslog(LOG_ERR, 13701553Srgrimes "%s: bad protocol for tcpmux service %s", 13711553Srgrimes CONFIG, sep->se_service); 13721553Srgrimes goto more; 13731553Srgrimes } 13741553Srgrimes if (sep->se_socktype != SOCK_STREAM) { 13758857Srgrimes syslog(LOG_ERR, 13761553Srgrimes "%s: bad socket type for tcpmux service %s", 13771553Srgrimes CONFIG, sep->se_service); 13781553Srgrimes goto more; 13791553Srgrimes } 13801553Srgrimes } 13811553Srgrimes sep->se_user = newstr(sskip(&cp)); 138230792Sache#ifdef LOGIN_CAP 138330792Sache if ((s = strrchr(sep->se_user, '/')) != NULL) { 138430792Sache *s = '\0'; 138530792Sache sep->se_class = newstr(s + 1); 138630792Sache } else 138730792Sache sep->se_class = newstr(RESOURCE_RC); 138830792Sache#endif 138930807Sache if ((s = strrchr(sep->se_user, ':')) != NULL) { 139030807Sache *s = '\0'; 139130807Sache sep->se_group = newstr(s + 1); 139230807Sache } else 139330807Sache sep->se_group = NULL; 13941553Srgrimes sep->se_server = newstr(sskip(&cp)); 139545588Smarkm if ((sep->se_server_name = rindex(sep->se_server, '/'))) 139645588Smarkm sep->se_server_name++; 13971553Srgrimes if (strcmp(sep->se_server, "internal") == 0) { 13981553Srgrimes struct biltin *bi; 13991553Srgrimes 14001553Srgrimes for (bi = biltins; bi->bi_service; bi++) 14011553Srgrimes if (bi->bi_socktype == sep->se_socktype && 14021553Srgrimes strcmp(bi->bi_service, sep->se_service) == 0) 14031553Srgrimes break; 14041553Srgrimes if (bi->bi_service == 0) { 14051553Srgrimes syslog(LOG_ERR, "internal service %s unknown", 14061553Srgrimes sep->se_service); 14071553Srgrimes goto more; 14081553Srgrimes } 140919618Sjulian sep->se_accept = 1; /* force accept mode for built-ins */ 14101553Srgrimes sep->se_bi = bi; 14111553Srgrimes } else 14121553Srgrimes sep->se_bi = NULL; 141345588Smarkm if (sep->se_maxchild < 0) { /* apply default max-children */ 141419618Sjulian if (sep->se_bi) 141519618Sjulian sep->se_maxchild = sep->se_bi->bi_maxchild; 141619618Sjulian else 141719618Sjulian sep->se_maxchild = sep->se_accept ? 0 : 1; 141845588Smarkm } 141919618Sjulian if (sep->se_maxchild) { 142019618Sjulian sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); 142119618Sjulian if (sep->se_pids == NULL) { 142219618Sjulian syslog(LOG_ERR, "Out of memory."); 142319618Sjulian exit(EX_OSERR); 142419618Sjulian } 142519618Sjulian } 14261553Srgrimes argc = 0; 14271553Srgrimes for (arg = skip(&cp); cp; arg = skip(&cp)) 142819618Sjulian if (argc < MAXARGV) { 14291553Srgrimes sep->se_argv[argc++] = newstr(arg); 143019618Sjulian } else { 143119618Sjulian syslog(LOG_ERR, 143219618Sjulian "%s: too many arguments for service %s", 143319618Sjulian CONFIG, sep->se_service); 143419618Sjulian goto more; 143519618Sjulian } 14361553Srgrimes while (argc <= MAXARGV) 14371553Srgrimes sep->se_argv[argc++] = NULL; 14381553Srgrimes return (sep); 14391553Srgrimes} 14401553Srgrimes 14411553Srgrimesvoid 14421553Srgrimesfreeconfig(cp) 14431553Srgrimes struct servtab *cp; 14441553Srgrimes{ 14451553Srgrimes int i; 14461553Srgrimes 14471553Srgrimes if (cp->se_service) 14481553Srgrimes free(cp->se_service); 14491553Srgrimes if (cp->se_proto) 14501553Srgrimes free(cp->se_proto); 14511553Srgrimes if (cp->se_user) 14521553Srgrimes free(cp->se_user); 145330807Sache if (cp->se_group) 145430807Sache free(cp->se_group); 145530792Sache#ifdef LOGIN_CAP 145630792Sache if (cp->se_class) 145730792Sache free(cp->se_class); 145830792Sache#endif 14591553Srgrimes if (cp->se_server) 14601553Srgrimes free(cp->se_server); 146119618Sjulian if (cp->se_pids) 146219618Sjulian free(cp->se_pids); 14631553Srgrimes for (i = 0; i < MAXARGV; i++) 14641553Srgrimes if (cp->se_argv[i]) 14651553Srgrimes free(cp->se_argv[i]); 14661553Srgrimes} 14671553Srgrimes 14681553Srgrimes 14691553Srgrimes/* 14701553Srgrimes * Safe skip - if skip returns null, log a syntax error in the 14711553Srgrimes * configuration file and exit. 14721553Srgrimes */ 14731553Srgrimeschar * 14741553Srgrimessskip(cpp) 14751553Srgrimes char **cpp; 14761553Srgrimes{ 14771553Srgrimes char *cp; 14781553Srgrimes 14791553Srgrimes cp = skip(cpp); 14801553Srgrimes if (cp == NULL) { 14811553Srgrimes syslog(LOG_ERR, "%s: syntax error", CONFIG); 148219617Sjulian exit(EX_DATAERR); 14831553Srgrimes } 14841553Srgrimes return (cp); 14851553Srgrimes} 14861553Srgrimes 14871553Srgrimeschar * 14881553Srgrimesskip(cpp) 14891553Srgrimes char **cpp; 14901553Srgrimes{ 14911553Srgrimes char *cp = *cpp; 14921553Srgrimes char *start; 149311933Sadam char quote = '\0'; 14941553Srgrimes 14951553Srgrimesagain: 14961553Srgrimes while (*cp == ' ' || *cp == '\t') 14971553Srgrimes cp++; 14981553Srgrimes if (*cp == '\0') { 14991553Srgrimes int c; 15001553Srgrimes 15011553Srgrimes c = getc(fconfig); 15021553Srgrimes (void) ungetc(c, fconfig); 15031553Srgrimes if (c == ' ' || c == '\t') 150419617Sjulian if ((cp = nextline(fconfig))) 15051553Srgrimes goto again; 15061553Srgrimes *cpp = (char *)0; 15071553Srgrimes return ((char *)0); 15081553Srgrimes } 150911933Sadam if (*cp == '"' || *cp == '\'') 151011933Sadam quote = *cp++; 15111553Srgrimes start = cp; 151211933Sadam if (quote) 151311933Sadam while (*cp && *cp != quote) 151411933Sadam cp++; 151511933Sadam else 151611933Sadam while (*cp && *cp != ' ' && *cp != '\t') 151711933Sadam cp++; 15181553Srgrimes if (*cp != '\0') 15191553Srgrimes *cp++ = '\0'; 15201553Srgrimes *cpp = cp; 15211553Srgrimes return (start); 15221553Srgrimes} 15231553Srgrimes 15241553Srgrimeschar * 15251553Srgrimesnextline(fd) 15261553Srgrimes FILE *fd; 15271553Srgrimes{ 15281553Srgrimes char *cp; 15291553Srgrimes 15301553Srgrimes if (fgets(line, sizeof (line), fd) == NULL) 15311553Srgrimes return ((char *)0); 15321553Srgrimes cp = strchr(line, '\n'); 15331553Srgrimes if (cp) 15341553Srgrimes *cp = '\0'; 15351553Srgrimes return (line); 15361553Srgrimes} 15371553Srgrimes 15381553Srgrimeschar * 15391553Srgrimesnewstr(cp) 15401553Srgrimes char *cp; 15411553Srgrimes{ 154219617Sjulian if ((cp = strdup(cp ? cp : ""))) 15431553Srgrimes return (cp); 15441553Srgrimes syslog(LOG_ERR, "strdup: %m"); 154519617Sjulian exit(EX_OSERR); 15461553Srgrimes} 15471553Srgrimes 154813142Speter#ifdef OLD_SETPROCTITLE 15491553Srgrimesvoid 155013142Speterinetd_setproctitle(a, s) 15511553Srgrimes char *a; 15521553Srgrimes int s; 15531553Srgrimes{ 15541553Srgrimes int size; 15551553Srgrimes char *cp; 15561553Srgrimes struct sockaddr_in sin; 15571553Srgrimes char buf[80]; 15581553Srgrimes 15591553Srgrimes cp = Argv[0]; 15601553Srgrimes size = sizeof(sin); 15611553Srgrimes if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) 15628857Srgrimes (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr)); 15631553Srgrimes else 15648857Srgrimes (void) sprintf(buf, "-%s", a); 15651553Srgrimes strncpy(cp, buf, LastArg - cp); 15661553Srgrimes cp += strlen(cp); 15671553Srgrimes while (cp < LastArg) 15681553Srgrimes *cp++ = ' '; 15691553Srgrimes} 157013142Speter#else 157113142Spetervoid 157213142Speterinetd_setproctitle(a, s) 157313142Speter char *a; 157413142Speter int s; 157513142Speter{ 157613142Speter int size; 157713142Speter struct sockaddr_in sin; 157813142Speter char buf[80]; 15791553Srgrimes 158013142Speter size = sizeof(sin); 158113142Speter if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) 158213142Speter (void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr)); 158313142Speter else 158413142Speter (void) sprintf(buf, "%s", a); 158513142Speter setproctitle("%s", buf); 158613142Speter} 158713142Speter#endif 158813142Speter 158913142Speter 15901553Srgrimes/* 15911553Srgrimes * Internet services provided internally by inetd: 15921553Srgrimes */ 15931553Srgrimes#define BUFSIZE 8192 15941553Srgrimes 159540910Sphk#define IDENT_RESPONSE ":ERROR:HIDDEN-USER\r\n" 159640910Sphk 15971553Srgrimes/* ARGSUSED */ 15981553Srgrimesvoid 159940910Sphkident_stream(s, sep) /* Ident service */ 160040910Sphk int s; 160140910Sphk struct servtab *sep; 160240910Sphk{ 160340910Sphk char buffer[BUFSIZE]; 160440910Sphk int i, j; 160540910Sphk 160640910Sphk inetd_setproctitle(sep->se_service, s); 160740910Sphk j = 0; 160840910Sphk while ((i = read(s, buffer + j, sizeof(buffer) - j)) > 0) { 160940910Sphk j += i; 161040910Sphk buffer[j] = '\0'; 161140910Sphk if (strchr(buffer, '\n')) 161240910Sphk break; 161340910Sphk if (strchr(buffer, '\r')) 161440910Sphk break; 161540910Sphk } 161640910Sphk while (j > 0 && (buffer[j-1] == '\n' || buffer[j-1] == '\r')) 161740910Sphk j--; 161840910Sphk write(s, buffer, j); 161940910Sphk write(s, IDENT_RESPONSE, strlen(IDENT_RESPONSE)); 162040910Sphk exit(0); 162140910Sphk} 162240910Sphk/* ARGSUSED */ 162340910Sphkvoid 16241553Srgrimesecho_stream(s, sep) /* Echo service -- echo data back */ 16251553Srgrimes int s; 16261553Srgrimes struct servtab *sep; 16271553Srgrimes{ 16281553Srgrimes char buffer[BUFSIZE]; 16291553Srgrimes int i; 16301553Srgrimes 163113142Speter inetd_setproctitle(sep->se_service, s); 16321553Srgrimes while ((i = read(s, buffer, sizeof(buffer))) > 0 && 16331553Srgrimes write(s, buffer, i) > 0) 16341553Srgrimes ; 16351553Srgrimes exit(0); 16361553Srgrimes} 16371553Srgrimes 16385182Swollmanint check_loop(sin, sep) 16395182Swollman struct sockaddr_in *sin; 16405182Swollman struct servtab *sep; 16415182Swollman{ 16425182Swollman struct servtab *se2; 16435182Swollman 16445182Swollman for (se2 = servtab; se2; se2 = se2->se_next) { 16455182Swollman if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM) 16465182Swollman continue; 16475182Swollman 16485182Swollman if (sin->sin_port == se2->se_ctrladdr.sin_port) { 16495182Swollman syslog(LOG_WARNING, 16505182Swollman "%s/%s:%s/%s loop request REFUSED from %s", 16518857Srgrimes sep->se_service, sep->se_proto, 16525182Swollman se2->se_service, se2->se_proto, 16535182Swollman inet_ntoa(sin->sin_addr)); 16545182Swollman return 1; 16555182Swollman } 16565182Swollman } 16575182Swollman return 0; 16585182Swollman} 16595182Swollman 16601553Srgrimes/* ARGSUSED */ 16611553Srgrimesvoid 16621553Srgrimesecho_dg(s, sep) /* Echo service -- echo data back */ 16631553Srgrimes int s; 16641553Srgrimes struct servtab *sep; 16651553Srgrimes{ 16661553Srgrimes char buffer[BUFSIZE]; 16671553Srgrimes int i, size; 16685182Swollman struct sockaddr_in sin; 16691553Srgrimes 16705182Swollman size = sizeof(sin); 16718857Srgrimes if ((i = recvfrom(s, buffer, sizeof(buffer), 0, 16725182Swollman (struct sockaddr *)&sin, &size)) < 0) 16731553Srgrimes return; 16745182Swollman 16755182Swollman if (check_loop(&sin, sep)) 16765182Swollman return; 16775182Swollman 16785182Swollman (void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin, 16795182Swollman sizeof(sin)); 16801553Srgrimes} 16811553Srgrimes 16821553Srgrimes/* ARGSUSED */ 16831553Srgrimesvoid 16841553Srgrimesdiscard_stream(s, sep) /* Discard service -- ignore data */ 16851553Srgrimes int s; 16861553Srgrimes struct servtab *sep; 16871553Srgrimes{ 16881553Srgrimes int ret; 16891553Srgrimes char buffer[BUFSIZE]; 16901553Srgrimes 169113142Speter inetd_setproctitle(sep->se_service, s); 16921553Srgrimes while (1) { 16931553Srgrimes while ((ret = read(s, buffer, sizeof(buffer))) > 0) 16941553Srgrimes ; 16951553Srgrimes if (ret == 0 || errno != EINTR) 16961553Srgrimes break; 16971553Srgrimes } 16981553Srgrimes exit(0); 16991553Srgrimes} 17001553Srgrimes 17011553Srgrimes/* ARGSUSED */ 17021553Srgrimesvoid 17031553Srgrimesdiscard_dg(s, sep) /* Discard service -- ignore data */ 17041553Srgrimes int s; 17051553Srgrimes struct servtab *sep; 17061553Srgrimes{ 17071553Srgrimes char buffer[BUFSIZE]; 17081553Srgrimes 17091553Srgrimes (void) read(s, buffer, sizeof(buffer)); 17101553Srgrimes} 17111553Srgrimes 17121553Srgrimes#include <ctype.h> 17131553Srgrimes#define LINESIZ 72 17141553Srgrimeschar ring[128]; 17151553Srgrimeschar *endring; 17161553Srgrimes 17171553Srgrimesvoid 17181553Srgrimesinitring() 17191553Srgrimes{ 17201553Srgrimes int i; 17211553Srgrimes 17221553Srgrimes endring = ring; 17231553Srgrimes 17241553Srgrimes for (i = 0; i <= 128; ++i) 17251553Srgrimes if (isprint(i)) 17261553Srgrimes *endring++ = i; 17271553Srgrimes} 17281553Srgrimes 17291553Srgrimes/* ARGSUSED */ 17301553Srgrimesvoid 17311553Srgrimeschargen_stream(s, sep) /* Character generator */ 17321553Srgrimes int s; 17331553Srgrimes struct servtab *sep; 17341553Srgrimes{ 17351553Srgrimes int len; 17361553Srgrimes char *rs, text[LINESIZ+2]; 17371553Srgrimes 173813142Speter inetd_setproctitle(sep->se_service, s); 17391553Srgrimes 17401553Srgrimes if (!endring) { 17411553Srgrimes initring(); 17421553Srgrimes rs = ring; 17431553Srgrimes } 17441553Srgrimes 17451553Srgrimes text[LINESIZ] = '\r'; 17461553Srgrimes text[LINESIZ + 1] = '\n'; 17471553Srgrimes for (rs = ring;;) { 17481553Srgrimes if ((len = endring - rs) >= LINESIZ) 17491553Srgrimes memmove(text, rs, LINESIZ); 17501553Srgrimes else { 17511553Srgrimes memmove(text, rs, len); 17521553Srgrimes memmove(text + len, ring, LINESIZ - len); 17531553Srgrimes } 17541553Srgrimes if (++rs == endring) 17551553Srgrimes rs = ring; 17561553Srgrimes if (write(s, text, sizeof(text)) != sizeof(text)) 17571553Srgrimes break; 17581553Srgrimes } 17591553Srgrimes exit(0); 17601553Srgrimes} 17611553Srgrimes 17621553Srgrimes/* ARGSUSED */ 17631553Srgrimesvoid 17641553Srgrimeschargen_dg(s, sep) /* Character generator */ 17651553Srgrimes int s; 17661553Srgrimes struct servtab *sep; 17671553Srgrimes{ 17685182Swollman struct sockaddr_in sin; 17691553Srgrimes static char *rs; 17701553Srgrimes int len, size; 17711553Srgrimes char text[LINESIZ+2]; 17721553Srgrimes 17731553Srgrimes if (endring == 0) { 17741553Srgrimes initring(); 17751553Srgrimes rs = ring; 17761553Srgrimes } 17771553Srgrimes 17785182Swollman size = sizeof(sin); 17798857Srgrimes if (recvfrom(s, text, sizeof(text), 0, 17805182Swollman (struct sockaddr *)&sin, &size) < 0) 17811553Srgrimes return; 17821553Srgrimes 17835182Swollman if (check_loop(&sin, sep)) 17845182Swollman return; 17855182Swollman 17861553Srgrimes if ((len = endring - rs) >= LINESIZ) 17871553Srgrimes memmove(text, rs, LINESIZ); 17881553Srgrimes else { 17891553Srgrimes memmove(text, rs, len); 17901553Srgrimes memmove(text + len, ring, LINESIZ - len); 17911553Srgrimes } 17921553Srgrimes if (++rs == endring) 17931553Srgrimes rs = ring; 17941553Srgrimes text[LINESIZ] = '\r'; 17951553Srgrimes text[LINESIZ + 1] = '\n'; 17968857Srgrimes (void) sendto(s, text, sizeof(text), 0, 17975182Swollman (struct sockaddr *)&sin, sizeof(sin)); 17981553Srgrimes} 17991553Srgrimes 18001553Srgrimes/* 18011553Srgrimes * Return a machine readable date and time, in the form of the 18021553Srgrimes * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 18031553Srgrimes * returns the number of seconds since midnight, Jan 1, 1970, 18041553Srgrimes * we must add 2208988800 seconds to this figure to make up for 18051553Srgrimes * some seventy years Bell Labs was asleep. 18061553Srgrimes */ 18071553Srgrimes 180842311Sdannyunsigned long 18091553Srgrimesmachtime() 18101553Srgrimes{ 18111553Srgrimes struct timeval tv; 18121553Srgrimes 181337856Sache if (gettimeofday(&tv, (struct timezone *)NULL) < 0) { 18141553Srgrimes if (debug) 181529602Scharnier warnx("unable to get time of day"); 18161553Srgrimes return (0L); 18171553Srgrimes } 18181553Srgrimes#define OFFSET ((u_long)25567 * 24*60*60) 18191553Srgrimes return (htonl((long)(tv.tv_sec + OFFSET))); 18201553Srgrimes#undef OFFSET 18211553Srgrimes} 18221553Srgrimes 18231553Srgrimes/* ARGSUSED */ 18241553Srgrimesvoid 18251553Srgrimesmachtime_stream(s, sep) 18261553Srgrimes int s; 18271553Srgrimes struct servtab *sep; 18281553Srgrimes{ 182942311Sdanny unsigned long result; 18301553Srgrimes 18311553Srgrimes result = machtime(); 18321553Srgrimes (void) write(s, (char *) &result, sizeof(result)); 18331553Srgrimes} 18341553Srgrimes 18351553Srgrimes/* ARGSUSED */ 18361553Srgrimesvoid 18371553Srgrimesmachtime_dg(s, sep) 18381553Srgrimes int s; 18391553Srgrimes struct servtab *sep; 18401553Srgrimes{ 184142311Sdanny unsigned long result; 18425182Swollman struct sockaddr_in sin; 18431553Srgrimes int size; 18441553Srgrimes 18455182Swollman size = sizeof(sin); 18468857Srgrimes if (recvfrom(s, (char *)&result, sizeof(result), 0, 18475182Swollman (struct sockaddr *)&sin, &size) < 0) 18481553Srgrimes return; 18495182Swollman 18505182Swollman if (check_loop(&sin, sep)) 18515182Swollman return; 18525182Swollman 18531553Srgrimes result = machtime(); 18548857Srgrimes (void) sendto(s, (char *) &result, sizeof(result), 0, 18555182Swollman (struct sockaddr *)&sin, sizeof(sin)); 18561553Srgrimes} 18571553Srgrimes 18581553Srgrimes/* ARGSUSED */ 18591553Srgrimesvoid 18601553Srgrimesdaytime_stream(s, sep) /* Return human-readable time of day */ 18611553Srgrimes int s; 18621553Srgrimes struct servtab *sep; 18631553Srgrimes{ 18641553Srgrimes char buffer[256]; 18651553Srgrimes time_t clock; 18661553Srgrimes 18671553Srgrimes clock = time((time_t *) 0); 18681553Srgrimes 18691553Srgrimes (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 18701553Srgrimes (void) write(s, buffer, strlen(buffer)); 18711553Srgrimes} 18721553Srgrimes 18731553Srgrimes/* ARGSUSED */ 18741553Srgrimesvoid 18751553Srgrimesdaytime_dg(s, sep) /* Return human-readable time of day */ 18761553Srgrimes int s; 18771553Srgrimes struct servtab *sep; 18781553Srgrimes{ 18791553Srgrimes char buffer[256]; 18801553Srgrimes time_t clock; 18815182Swollman struct sockaddr_in sin; 18821553Srgrimes int size; 18831553Srgrimes 18841553Srgrimes clock = time((time_t *) 0); 18851553Srgrimes 18865182Swollman size = sizeof(sin); 18878857Srgrimes if (recvfrom(s, buffer, sizeof(buffer), 0, 18885182Swollman (struct sockaddr *)&sin, &size) < 0) 18891553Srgrimes return; 18905182Swollman 18915182Swollman if (check_loop(&sin, sep)) 18925182Swollman return; 18935182Swollman 18941553Srgrimes (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 18958857Srgrimes (void) sendto(s, buffer, strlen(buffer), 0, 18965182Swollman (struct sockaddr *)&sin, sizeof(sin)); 18971553Srgrimes} 18981553Srgrimes 18991553Srgrimes/* 19001553Srgrimes * print_service: 19011553Srgrimes * Dump relevant information to stderr 19021553Srgrimes */ 19031553Srgrimesvoid 19041553Srgrimesprint_service(action, sep) 19051553Srgrimes char *action; 19061553Srgrimes struct servtab *sep; 19071553Srgrimes{ 190819617Sjulian fprintf(stderr, 190930792Sache#ifdef LOGIN_CAP 191038380Sjb "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%p server=%s\n", 191130792Sache#else 191238380Sjb "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%p server=%s\n", 191330792Sache#endif 191419617Sjulian action, sep->se_service, sep->se_proto, 191530807Sache sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group, 191630792Sache#ifdef LOGIN_CAP 191730792Sache sep->se_class, 191830792Sache#endif 191938417Sjb (void *) sep->se_bi, sep->se_server); 19201553Srgrimes} 19211553Srgrimes 19221553Srgrimes/* 19231553Srgrimes * Based on TCPMUX.C by Mark K. Lottor November 1988 19241553Srgrimes * sri-nic::ps:<mkl>tcpmux.c 19251553Srgrimes */ 19261553Srgrimes 19271553Srgrimes 19281553Srgrimesstatic int /* # of characters upto \r,\n or \0 */ 19291553Srgrimesgetline(fd, buf, len) 19301553Srgrimes int fd; 19311553Srgrimes char *buf; 19321553Srgrimes int len; 19331553Srgrimes{ 19341553Srgrimes int count = 0, n; 193535848Sguido struct sigaction sa; 19361553Srgrimes 193735948Sbde sa.sa_flags = 0; 193835948Sbde sigemptyset(&sa.sa_mask); 193935848Sguido sa.sa_handler = SIG_DFL; 194035848Sguido sigaction(SIGALRM, &sa, (struct sigaction *)0); 19411553Srgrimes do { 194235829Sguido alarm(10); 19431553Srgrimes n = read(fd, buf, len-count); 194435829Sguido alarm(0); 19451553Srgrimes if (n == 0) 194642122Sdes return (count); 194742122Sdes if (n < 0) 194842122Sdes return (-1); 19491553Srgrimes while (--n >= 0) { 195042122Sdes if (*buf == '\r' || *buf == '\n' || *buf == '\0') 195142122Sdes return (count); 19521553Srgrimes count++; 19531553Srgrimes buf++; 19541553Srgrimes } 195542122Sdes } while (count < len); 19561553Srgrimes return (count); 19571553Srgrimes} 19581553Srgrimes 19591553Srgrimes#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 19601553Srgrimes 19611553Srgrimes#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 19621553Srgrimes 19631553Srgrimesstruct servtab * 19641553Srgrimestcpmux(s) 19651553Srgrimes int s; 19661553Srgrimes{ 19671553Srgrimes struct servtab *sep; 19681553Srgrimes char service[MAX_SERV_LEN+1]; 19691553Srgrimes int len; 19701553Srgrimes 19711553Srgrimes /* Get requested service name */ 19721553Srgrimes if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 19731553Srgrimes strwrite(s, "-Error reading service name\r\n"); 19741553Srgrimes return (NULL); 19751553Srgrimes } 19761553Srgrimes service[len] = '\0'; 19771553Srgrimes 19781553Srgrimes if (debug) 197929602Scharnier warnx("tcpmux: someone wants %s", service); 19801553Srgrimes 19811553Srgrimes /* 19821553Srgrimes * Help is a required command, and lists available services, 19831553Srgrimes * one per line. 19841553Srgrimes */ 19851553Srgrimes if (!strcasecmp(service, "help")) { 19861553Srgrimes for (sep = servtab; sep; sep = sep->se_next) { 19871553Srgrimes if (!ISMUX(sep)) 19881553Srgrimes continue; 19891553Srgrimes (void)write(s,sep->se_service,strlen(sep->se_service)); 19901553Srgrimes strwrite(s, "\r\n"); 19911553Srgrimes } 19921553Srgrimes return (NULL); 19931553Srgrimes } 19941553Srgrimes 19951553Srgrimes /* Try matching a service in inetd.conf with the request */ 19961553Srgrimes for (sep = servtab; sep; sep = sep->se_next) { 19971553Srgrimes if (!ISMUX(sep)) 19981553Srgrimes continue; 19991553Srgrimes if (!strcasecmp(service, sep->se_service)) { 20001553Srgrimes if (ISMUXPLUS(sep)) { 20011553Srgrimes strwrite(s, "+Go\r\n"); 20021553Srgrimes } 20031553Srgrimes return (sep); 20041553Srgrimes } 20051553Srgrimes } 20061553Srgrimes strwrite(s, "-Service not available\r\n"); 20071553Srgrimes return (NULL); 20081553Srgrimes} 200930847Sdima 201030847Sdima#define CPMHSIZE 256 201130847Sdima#define CPMHMASK (CPMHSIZE-1) 201230847Sdima#define CHTGRAN 10 201330847Sdima#define CHTSIZE 6 201430847Sdima 201530847Sdimatypedef struct CTime { 201630847Sdima unsigned long ct_Ticks; 201730847Sdima int ct_Count; 201830847Sdima} CTime; 201930847Sdima 202030847Sdimatypedef struct CHash { 202130847Sdima struct in_addr ch_Addr; 202230847Sdima time_t ch_LTime; 202330847Sdima char *ch_Service; 202430847Sdima CTime ch_Times[CHTSIZE]; 202530847Sdima} CHash; 202630847Sdima 202730847SdimaCHash CHashAry[CPMHSIZE]; 202830847Sdima 202930847Sdimaint 203030847Sdimacpmip(sep, ctrl) 203130847Sdima struct servtab *sep; 203230847Sdima int ctrl; 203330847Sdima{ 203430847Sdima struct sockaddr_in rsin; 203530847Sdima int rsinLen = sizeof(rsin); 203630847Sdima int r = 0; 203730847Sdima 203830847Sdima /* 203930847Sdima * If getpeername() fails, just let it through (if logging is 204030847Sdima * enabled the condition is caught elsewhere) 204130847Sdima */ 204230847Sdima 204330847Sdima if (sep->se_maxcpm > 0 && 204430847Sdima getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 0 ) { 204530847Sdima time_t t = time(NULL); 204630847Sdima int hv = 0xABC3D20F; 204730847Sdima int i; 204830847Sdima int cnt = 0; 204930847Sdima CHash *chBest = NULL; 205030847Sdima unsigned int ticks = t / CHTGRAN; 205130847Sdima 205230847Sdima { 205330847Sdima char *p; 205430847Sdima int i; 205530847Sdima 205630847Sdima for (i = 0, p = (char *)&rsin.sin_addr; 205730847Sdima i < sizeof(rsin.sin_addr); 205830847Sdima ++i, ++p) { 205930847Sdima hv = (hv << 5) ^ (hv >> 23) ^ *p; 206030847Sdima } 206130847Sdima hv = (hv ^ (hv >> 16)); 206230847Sdima } 206330847Sdima for (i = 0; i < 5; ++i) { 206430847Sdima CHash *ch = &CHashAry[(hv + i) & CPMHMASK]; 206530847Sdima 206630847Sdima if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr && 206730847Sdima ch->ch_Service && strcmp(sep->se_service, 206830847Sdima ch->ch_Service) == 0) { 206930847Sdima chBest = ch; 207030847Sdima break; 207130847Sdima } 207230847Sdima if (chBest == NULL || ch->ch_LTime == 0 || 207330847Sdima ch->ch_LTime < chBest->ch_LTime) { 207430847Sdima chBest = ch; 207530847Sdima } 207630847Sdima } 207730847Sdima if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr || 207830847Sdima chBest->ch_Service == NULL || 207930847Sdima strcmp(sep->se_service, chBest->ch_Service) != 0) { 208030847Sdima chBest->ch_Addr = rsin.sin_addr; 208130847Sdima if (chBest->ch_Service) 208230847Sdima free(chBest->ch_Service); 208330847Sdima chBest->ch_Service = strdup(sep->se_service); 208430847Sdima bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 208530847Sdima } 208630847Sdima chBest->ch_LTime = t; 208730847Sdima { 208830847Sdima CTime *ct = &chBest->ch_Times[ticks % CHTSIZE]; 208930847Sdima if (ct->ct_Ticks != ticks) { 209030847Sdima ct->ct_Ticks = ticks; 209130847Sdima ct->ct_Count = 0; 209230847Sdima } 209330847Sdima ++ct->ct_Count; 209430847Sdima } 209530847Sdima for (i = 0; i < CHTSIZE; ++i) { 209630847Sdima CTime *ct = &chBest->ch_Times[i]; 209730847Sdima if (ct->ct_Ticks <= ticks && 209830847Sdima ct->ct_Ticks >= ticks - CHTSIZE) { 209930847Sdima cnt += ct->ct_Count; 210030847Sdima } 210130847Sdima } 210230847Sdima if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) { 210330847Sdima r = -1; 210430847Sdima syslog(LOG_ERR, 210533794Spst "%s from %s exceeded counts/min (limit %d/min)", 210633794Spst sep->se_service, inet_ntoa(rsin.sin_addr), 210733794Spst sep->se_maxcpm); 210830847Sdima } 210930847Sdima } 211030847Sdima return(r); 211130847Sdima} 2112