inetd.c revision 33794
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[] = 4533794Spst "$Id: inetd.c,v 1.29 1997/10/29 21:49:04 dima 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> 1151553Srgrimes#include <arpa/inet.h> 1162657Scsgr#include <rpc/rpc.h> 11719617Sjulian#include <rpc/pmap_clnt.h> 1181553Srgrimes 1191553Srgrimes#include <errno.h> 12029602Scharnier#include <err.h> 1211553Srgrimes#include <fcntl.h> 12230807Sache#include <grp.h> 1231553Srgrimes#include <netdb.h> 1241553Srgrimes#include <pwd.h> 1251553Srgrimes#include <signal.h> 1261553Srgrimes#include <stdio.h> 1271553Srgrimes#include <stdlib.h> 1281553Srgrimes#include <string.h> 1291553Srgrimes#include <syslog.h> 1301553Srgrimes#include <unistd.h> 13113142Speter#include <libutil.h> 13219617Sjulian#include <sysexits.h> 1331553Srgrimes 13421640Speter#ifdef LOGIN_CAP 13521640Speter#include <login_cap.h> 13630792Sache 13730792Sache/* see init.c */ 13830792Sache#define RESOURCE_RC "daemon" 13930792Sache 14021640Speter#endif 14121640Speter 1421553Srgrimes#include "pathnames.h" 1431553Srgrimes 14433794Spst#ifndef MAXCHILD 14533794Spst#define MAXCHILD -1 /* maximum number of this service 14633794Spst < 0 = no limit */ 14733794Spst#endif 14833794Spst 14933794Spst#ifndef MAXCPM 15033794Spst#define MAXCPM -1 /* rate limit invocations from a 15133794Spst single remote address, 15233794Spst < 0 = no limit */ 15333794Spst#endif 15433794Spst 1552659Scsgr#define TOOMANY 256 /* don't start more than TOOMANY */ 1561553Srgrimes#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ 1571553Srgrimes#define RETRYTIME (60*10) /* retry after bind or server fail */ 15819618Sjulian#define MAX_MAXCHLD 32767 /* max allowable max children */ 1591553Srgrimes 1601553Srgrimes#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) 1611553Srgrimes 1621553Srgrimesint debug = 0; 1632659Scsgrint log = 0; 1641553Srgrimesint nsock, maxsock; 1651553Srgrimesfd_set allsock; 1661553Srgrimesint options; 1671553Srgrimesint timingout; 1681553Srgrimesint toomany = TOOMANY; 16933794Spstint maxchild = MAXCPM; 17033794Spstint maxcpm = MAXCHILD; 1711553Srgrimesstruct servent *sp; 1722657Scsgrstruct rpcent *rpc; 17317482Sjulianstruct in_addr bind_address; 1741553Srgrimes 1751553Srgrimesstruct servtab { 1761553Srgrimes char *se_service; /* name of service */ 1771553Srgrimes int se_socktype; /* type of socket to use */ 1781553Srgrimes char *se_proto; /* protocol used */ 17930847Sdima int se_maxchild; /* max number of children */ 18030847Sdima int se_maxcpm; /* max connects per IP per minute */ 18130847Sdima int se_numchild; /* current number of children */ 18219618Sjulian pid_t *se_pids; /* array of child pids */ 1831553Srgrimes char *se_user; /* user name to run as */ 18430807Sache char *se_group; /* group name to run as */ 18530792Sache#ifdef LOGIN_CAP 18630792Sache char *se_class; /* login class name to run with */ 18730792Sache#endif 1881553Srgrimes struct biltin *se_bi; /* if built-in, description */ 1891553Srgrimes char *se_server; /* server program */ 1901553Srgrimes#define MAXARGV 20 1911553Srgrimes char *se_argv[MAXARGV+1]; /* program arguments */ 1921553Srgrimes int se_fd; /* open descriptor */ 1931553Srgrimes struct sockaddr_in se_ctrladdr;/* bound address */ 19419618Sjulian u_char se_type; /* type: normal, mux, or mux+ */ 19519618Sjulian u_char se_checked; /* looked at during merge */ 19619618Sjulian u_char se_accept; /* i.e., wait/nowait mode */ 19719618Sjulian u_char se_rpc; /* ==1 if RPC service */ 1982657Scsgr int se_rpc_prog; /* RPC program number */ 1992657Scsgr u_int se_rpc_lowvers; /* RPC low version */ 2002657Scsgr u_int se_rpc_highvers; /* RPC high version */ 2011553Srgrimes int se_count; /* number started since se_time */ 2021553Srgrimes struct timeval se_time; /* start of se_count */ 2031553Srgrimes struct servtab *se_next; 2041553Srgrimes} *servtab; 2051553Srgrimes 2061553Srgrimes#define NORM_TYPE 0 2071553Srgrimes#define MUX_TYPE 1 2081553Srgrimes#define MUXPLUS_TYPE 2 2091553Srgrimes#define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \ 2101553Srgrimes ((sep)->se_type == MUXPLUS_TYPE)) 2111553Srgrimes#define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE) 2121553Srgrimes 2131553Srgrimes 2141553Srgrimesvoid chargen_dg __P((int, struct servtab *)); 2151553Srgrimesvoid chargen_stream __P((int, struct servtab *)); 2161553Srgrimesvoid close_sep __P((struct servtab *)); 2171553Srgrimesvoid config __P((int)); 2181553Srgrimesvoid daytime_dg __P((int, struct servtab *)); 2191553Srgrimesvoid daytime_stream __P((int, struct servtab *)); 2201553Srgrimesvoid discard_dg __P((int, struct servtab *)); 2211553Srgrimesvoid discard_stream __P((int, struct servtab *)); 2221553Srgrimesvoid echo_dg __P((int, struct servtab *)); 2231553Srgrimesvoid echo_stream __P((int, struct servtab *)); 2241553Srgrimesvoid endconfig __P((void)); 2251553Srgrimesstruct servtab *enter __P((struct servtab *)); 2261553Srgrimesvoid freeconfig __P((struct servtab *)); 2271553Srgrimesstruct servtab *getconfigent __P((void)); 2281553Srgrimesvoid machtime_dg __P((int, struct servtab *)); 2291553Srgrimesvoid machtime_stream __P((int, struct servtab *)); 2301553Srgrimeschar *newstr __P((char *)); 2311553Srgrimeschar *nextline __P((FILE *)); 2321553Srgrimesvoid print_service __P((char *, struct servtab *)); 23319618Sjulianvoid addchild __P((struct servtab *, int)); 2341553Srgrimesvoid reapchild __P((int)); 23519618Sjulianvoid enable __P((struct servtab *)); 23619618Sjulianvoid disable __P((struct servtab *)); 2371553Srgrimesvoid retry __P((int)); 2381553Srgrimesint setconfig __P((void)); 2391553Srgrimesvoid setup __P((struct servtab *)); 2401553Srgrimeschar *sskip __P((char **)); 2411553Srgrimeschar *skip __P((char **)); 2421553Srgrimesstruct servtab *tcpmux __P((int)); 24330847Sdimaint cpmip __P((struct servtab *, int)); 2441553Srgrimes 2452657Scsgrvoid unregisterrpc __P((register struct servtab *sep)); 2462657Scsgr 2471553Srgrimesstruct biltin { 2481553Srgrimes char *bi_service; /* internally provided service name */ 2491553Srgrimes int bi_socktype; /* type of socket supported */ 2501553Srgrimes short bi_fork; /* 1 if should fork before call */ 25130847Sdima int bi_maxchild; /* max number of children (default) */ 2521553Srgrimes void (*bi_fn)(); /* function which performs it */ 2531553Srgrimes} biltins[] = { 2541553Srgrimes /* Echo received data */ 2551553Srgrimes { "echo", SOCK_STREAM, 1, 0, echo_stream }, 2561553Srgrimes { "echo", SOCK_DGRAM, 0, 0, echo_dg }, 2571553Srgrimes 2581553Srgrimes /* Internet /dev/null */ 2591553Srgrimes { "discard", SOCK_STREAM, 1, 0, discard_stream }, 2601553Srgrimes { "discard", SOCK_DGRAM, 0, 0, discard_dg }, 2611553Srgrimes 2621553Srgrimes /* Return 32 bit time since 1970 */ 2631553Srgrimes { "time", SOCK_STREAM, 0, 0, machtime_stream }, 2641553Srgrimes { "time", SOCK_DGRAM, 0, 0, machtime_dg }, 2651553Srgrimes 2661553Srgrimes /* Return human-readable time */ 2671553Srgrimes { "daytime", SOCK_STREAM, 0, 0, daytime_stream }, 2681553Srgrimes { "daytime", SOCK_DGRAM, 0, 0, daytime_dg }, 2691553Srgrimes 2701553Srgrimes /* Familiar character generator */ 2711553Srgrimes { "chargen", SOCK_STREAM, 1, 0, chargen_stream }, 2721553Srgrimes { "chargen", SOCK_DGRAM, 0, 0, chargen_dg }, 2731553Srgrimes 2741553Srgrimes { "tcpmux", SOCK_STREAM, 1, 0, (void (*)())tcpmux }, 2751553Srgrimes 2761553Srgrimes { NULL } 2771553Srgrimes}; 2781553Srgrimes 2791553Srgrimes#define NUMINT (sizeof(intab) / sizeof(struct inent)) 2801553Srgrimeschar *CONFIG = _PATH_INETDCONF; 28117482Sjulianchar *pid_file = _PATH_INETDPID; 28213142Speter 28313142Speter#ifdef OLD_SETPROCTITLE 2841553Srgrimeschar **Argv; 2851553Srgrimeschar *LastArg; 28613142Speter#endif 2871553Srgrimes 2881553Srgrimesint 28933794Spstgetvalue(arg, value, whine) 29033794Spst char *arg, *whine; 29133794Spst int *value; 29233794Spst{ 29333794Spst int tmp; 29433794Spst char *p; 29533794Spst 29633794Spst tmp = strtol(arg, &p, 0); 29733794Spst if (tmp < 1 || *p) { 29833794Spst syslog(LOG_ERR, whine, arg); 29933794Spst return 1; /* failure */ 30033794Spst } 30133794Spst *value = tmp; 30233794Spst return 0; /* success */ 30333794Spst} 30433794Spst 30533794Spstint 3061553Srgrimesmain(argc, argv, envp) 3071553Srgrimes int argc; 3081553Srgrimes char *argv[], *envp[]; 3091553Srgrimes{ 3101553Srgrimes struct servtab *sep; 3111553Srgrimes struct passwd *pwd; 31230807Sache struct group *grp; 3131553Srgrimes struct sigvec sv; 3141553Srgrimes int tmpint, ch, dofork; 3151553Srgrimes pid_t pid; 3161553Srgrimes char buf[50]; 3172659Scsgr struct sockaddr_in peer; 3182659Scsgr int i; 31921640Speter#ifdef LOGIN_CAP 32021640Speter login_cap_t *lc = NULL; 32121640Speter#endif 3221553Srgrimes 32313142Speter 32413142Speter#ifdef OLD_SETPROCTITLE 3251553Srgrimes Argv = argv; 3261553Srgrimes if (envp == 0 || *envp == 0) 3271553Srgrimes envp = argv; 3281553Srgrimes while (*envp) 3291553Srgrimes envp++; 3301553Srgrimes LastArg = envp[-1] + strlen(envp[-1]); 33113142Speter#endif 3321553Srgrimes 3331553Srgrimes openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); 3341553Srgrimes 33517482Sjulian bind_address.s_addr = htonl(INADDR_ANY); 33633794Spst while ((ch = getopt(argc, argv, "dlR:a:c:C:p:")) != -1) 3371553Srgrimes switch(ch) { 3381553Srgrimes case 'd': 3391553Srgrimes debug = 1; 3401553Srgrimes options |= SO_DEBUG; 3411553Srgrimes break; 3422659Scsgr case 'l': 3432659Scsgr log = 1; 3442659Scsgr break; 34533794Spst case 'R': 34633794Spst getvalue(optarg, &toomany, 34733794Spst "-R %s: bad value for service invocation rate"); 3481553Srgrimes break; 34933794Spst case 'c': 35033794Spst getvalue(optarg, &maxchild, 35133794Spst "-c %s: bad value for maximum children"); 35233794Spst break; 35333794Spst case 'C': 35433794Spst getvalue(optarg, &maxcpm, 35533794Spst "-C %s: bad value for maximum children/minute"); 35633794Spst break; 35717482Sjulian case 'a': 35817482Sjulian if (!inet_aton(optarg, &bind_address)) { 35917482Sjulian syslog(LOG_ERR, 36017482Sjulian "-a %s: invalid IP address", optarg); 36119617Sjulian exit(EX_USAGE); 36217482Sjulian } 36317482Sjulian break; 36417482Sjulian case 'p': 36517482Sjulian pid_file = optarg; 36617482Sjulian break; 3671553Srgrimes case '?': 3681553Srgrimes default: 3691553Srgrimes syslog(LOG_ERR, 37017482Sjulian "usage: inetd [-dl] [-a address] [-R rate]" 37133794Spst " [-c maximum] [-C rate]" 37217482Sjulian " [-p pidfile] [conf-file]"); 37319617Sjulian exit(EX_USAGE); 3741553Srgrimes } 3751553Srgrimes argc -= optind; 3761553Srgrimes argv += optind; 3771553Srgrimes 3781553Srgrimes if (argc > 0) 3791553Srgrimes CONFIG = argv[0]; 3801553Srgrimes if (debug == 0) { 38111447Swollman FILE *fp; 38212024Speter if (daemon(0, 0) < 0) { 38312024Speter syslog(LOG_WARNING, "daemon(0,0) failed: %m"); 38412024Speter } 38512024Speter /* 38612024Speter * In case somebody has started inetd manually, we need to 38712024Speter * clear the logname, so that old servers run as root do not 38812024Speter * get the user's logname.. 38912024Speter */ 39012024Speter if (setlogin("") < 0) { 39112024Speter syslog(LOG_WARNING, "cannot clear logname: %m"); 39212024Speter /* no big deal if it fails.. */ 39312024Speter } 39411447Swollman pid = getpid(); 39517482Sjulian fp = fopen(pid_file, "w"); 39611447Swollman if (fp) { 39711447Swollman fprintf(fp, "%ld\n", (long)pid); 39811447Swollman fclose(fp); 39911447Swollman } else { 40017482Sjulian syslog(LOG_WARNING, "%s: %m", pid_file); 40111447Swollman } 4021553Srgrimes } 4031553Srgrimes memset(&sv, 0, sizeof(sv)); 4041553Srgrimes sv.sv_mask = SIGBLOCK; 4051553Srgrimes sv.sv_handler = retry; 4061553Srgrimes sigvec(SIGALRM, &sv, (struct sigvec *)0); 4071553Srgrimes config(SIGHUP); 4081553Srgrimes sv.sv_handler = config; 4091553Srgrimes sigvec(SIGHUP, &sv, (struct sigvec *)0); 4101553Srgrimes sv.sv_handler = reapchild; 4111553Srgrimes sigvec(SIGCHLD, &sv, (struct sigvec *)0); 4121553Srgrimes 4131553Srgrimes { 4141553Srgrimes /* space for daemons to overwrite environment for ps */ 4151553Srgrimes#define DUMMYSIZE 100 4161553Srgrimes char dummy[DUMMYSIZE]; 4171553Srgrimes 41819298Salex (void)memset(dummy, 'x', DUMMYSIZE - 1); 4191553Srgrimes dummy[DUMMYSIZE - 1] = '\0'; 4201553Srgrimes (void)setenv("inetd_dummy", dummy, 1); 4211553Srgrimes } 4221553Srgrimes 4231553Srgrimes for (;;) { 4241553Srgrimes int n, ctrl; 4251553Srgrimes fd_set readable; 4261553Srgrimes 4271553Srgrimes if (nsock == 0) { 4281553Srgrimes (void) sigblock(SIGBLOCK); 4291553Srgrimes while (nsock == 0) 4301553Srgrimes sigpause(0L); 4311553Srgrimes (void) sigsetmask(0L); 4321553Srgrimes } 4331553Srgrimes readable = allsock; 4341553Srgrimes if ((n = select(maxsock + 1, &readable, (fd_set *)0, 4351553Srgrimes (fd_set *)0, (struct timeval *)0)) <= 0) { 43628907Simp if (n < 0 && errno != EINTR) { 4371553Srgrimes syslog(LOG_WARNING, "select: %m"); 43828907Simp sleep(1); 43928907Simp } 4401553Srgrimes continue; 4411553Srgrimes } 4421553Srgrimes for (sep = servtab; n && sep; sep = sep->se_next) 4431553Srgrimes if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { 4441553Srgrimes n--; 4451553Srgrimes if (debug) 44629602Scharnier warnx("someone wants %s", sep->se_service); 44719618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) { 4481553Srgrimes ctrl = accept(sep->se_fd, (struct sockaddr *)0, 4491553Srgrimes (int *)0); 4501553Srgrimes if (debug) 45129602Scharnier warnx("accept, ctrl %d", ctrl); 4521553Srgrimes if (ctrl < 0) { 4531553Srgrimes if (errno != EINTR) 4541553Srgrimes syslog(LOG_WARNING, 4551553Srgrimes "accept (for %s): %m", 4561553Srgrimes sep->se_service); 4571553Srgrimes continue; 4581553Srgrimes } 45930847Sdima if (cpmip(sep, ctrl) < 0) { 46030847Sdima close(ctrl); 46130847Sdima continue; 46230847Sdima } 46319617Sjulian if (log) { 4642659Scsgr i = sizeof peer; 46530847Sdima if (getpeername(ctrl, (struct sockaddr *) 4662659Scsgr &peer, &i)) { 4672659Scsgr syslog(LOG_WARNING, 4682659Scsgr "getpeername(for %s): %m", 4692659Scsgr sep->se_service); 47030847Sdima close(ctrl); 4712659Scsgr continue; 4722659Scsgr } 4732659Scsgr syslog(LOG_INFO,"%s from %s", 4742659Scsgr sep->se_service, 4752659Scsgr inet_ntoa(peer.sin_addr)); 4762659Scsgr } 4771553Srgrimes /* 4781553Srgrimes * Call tcpmux to find the real service to exec. 4791553Srgrimes */ 4801553Srgrimes if (sep->se_bi && 4811553Srgrimes sep->se_bi->bi_fn == (void (*)()) tcpmux) { 4829202Srgrimes struct servtab *tsep; 4839202Srgrimes 4849202Srgrimes tsep = tcpmux(ctrl); 4859202Srgrimes if (tsep == NULL) { 4861553Srgrimes close(ctrl); 4871553Srgrimes continue; 4881553Srgrimes } 4899202Srgrimes sep = tsep; 4901553Srgrimes } 4911553Srgrimes } else 4921553Srgrimes ctrl = sep->se_fd; 4931553Srgrimes (void) sigblock(SIGBLOCK); 4941553Srgrimes pid = 0; 4951553Srgrimes dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork); 4961553Srgrimes if (dofork) { 4971553Srgrimes if (sep->se_count++ == 0) 4981553Srgrimes (void)gettimeofday(&sep->se_time, 4991553Srgrimes (struct timezone *)0); 5001553Srgrimes else if (sep->se_count >= toomany) { 5011553Srgrimes struct timeval now; 5021553Srgrimes 5031553Srgrimes (void)gettimeofday(&now, (struct timezone *)0); 5041553Srgrimes if (now.tv_sec - sep->se_time.tv_sec > 5051553Srgrimes CNT_INTVL) { 5061553Srgrimes sep->se_time = now; 5071553Srgrimes sep->se_count = 1; 5081553Srgrimes } else { 5091553Srgrimes syslog(LOG_ERR, 5101553Srgrimes "%s/%s server failing (looping), service terminated", 5111553Srgrimes sep->se_service, sep->se_proto); 5121553Srgrimes close_sep(sep); 5131553Srgrimes sigsetmask(0L); 5141553Srgrimes if (!timingout) { 5151553Srgrimes timingout = 1; 5161553Srgrimes alarm(RETRYTIME); 5171553Srgrimes } 5181553Srgrimes continue; 5191553Srgrimes } 5201553Srgrimes } 5211553Srgrimes pid = fork(); 5221553Srgrimes } 5231553Srgrimes if (pid < 0) { 5241553Srgrimes syslog(LOG_ERR, "fork: %m"); 52519618Sjulian if (sep->se_accept && 5261553Srgrimes sep->se_socktype == SOCK_STREAM) 5271553Srgrimes close(ctrl); 5281553Srgrimes sigsetmask(0L); 5291553Srgrimes sleep(1); 5301553Srgrimes continue; 5311553Srgrimes } 53219618Sjulian if (pid) 53319618Sjulian addchild(sep, pid); 5341553Srgrimes sigsetmask(0L); 5351553Srgrimes if (pid == 0) { 5361553Srgrimes if (dofork) { 5371553Srgrimes if (debug) 53829602Scharnier warnx("+ closing from %d", maxsock); 5391553Srgrimes for (tmpint = maxsock; tmpint > 2; tmpint--) 5401553Srgrimes if (tmpint != ctrl) 54119617Sjulian (void) close(tmpint); 5421553Srgrimes } 54319617Sjulian if (sep->se_bi) { 5441553Srgrimes (*sep->se_bi->bi_fn)(ctrl, sep); 54519617Sjulian /* NOTREACHED */ 54619617Sjulian } else { 5471553Srgrimes if (debug) 54829602Scharnier warnx("%d execl %s", 54929602Scharnier getpid(), sep->se_server); 5501553Srgrimes dup2(ctrl, 0); 5511553Srgrimes close(ctrl); 5521553Srgrimes dup2(0, 1); 5531553Srgrimes dup2(0, 2); 5541553Srgrimes if ((pwd = getpwnam(sep->se_user)) == NULL) { 5551553Srgrimes syslog(LOG_ERR, 5561553Srgrimes "%s/%s: %s: No such user", 5571553Srgrimes sep->se_service, sep->se_proto, 5581553Srgrimes sep->se_user); 5591553Srgrimes if (sep->se_socktype != SOCK_STREAM) 5601553Srgrimes recv(0, buf, sizeof (buf), 0); 56119617Sjulian _exit(EX_NOUSER); 5621553Srgrimes } 56330807Sache grp = NULL; 56430807Sache if ( sep->se_group != NULL 56530807Sache && (grp = getgrnam(sep->se_group)) == NULL 56630807Sache ) { 56730807Sache syslog(LOG_ERR, 56830807Sache "%s/%s: %s: No such group", 56930807Sache sep->se_service, sep->se_proto, 57030807Sache sep->se_group); 57130807Sache if (sep->se_socktype != SOCK_STREAM) 57230807Sache recv(0, buf, sizeof (buf), 0); 57330807Sache _exit(EX_NOUSER); 57430807Sache } 57530807Sache if (grp != NULL) 57630807Sache pwd->pw_gid = grp->gr_gid; 57721640Speter#ifdef LOGIN_CAP 57830792Sache if ((lc = login_getclass(sep->se_class)) == NULL) { 57930792Sache /* error syslogged by getclass */ 58030792Sache syslog(LOG_ERR, 58130792Sache "%s/%s: %s: login class error", 58230792Sache sep->se_service, sep->se_proto); 58330792Sache if (sep->se_socktype != SOCK_STREAM) 58430792Sache recv(0, buf, sizeof (buf), 0); 58530792Sache _exit(EX_NOUSER); 58630792Sache } 58721640Speter#endif 58812024Speter if (setsid() < 0) { 58912024Speter syslog(LOG_ERR, 59012024Speter "%s: can't setsid(): %m", 59112024Speter sep->se_service); 59219617Sjulian /* _exit(EX_OSERR); not fatal yet */ 59312024Speter } 59421640Speter#ifdef LOGIN_CAP 59521640Speter if (setusercontext(lc, pwd, pwd->pw_uid, 59621640Speter LOGIN_SETALL) != 0) { 59721640Speter syslog(LOG_ERR, 59821640Speter "%s: can't setusercontext(..%s..): %m", 59921640Speter sep->se_service, sep->se_user); 60021640Speter _exit(EX_OSERR); 60121640Speter } 60221640Speter#else 6031553Srgrimes if (pwd->pw_uid) { 60412024Speter if (setlogin(sep->se_user) < 0) { 60512024Speter syslog(LOG_ERR, 60612024Speter "%s: can't setlogin(%s): %m", 60712024Speter sep->se_service, sep->se_user); 60819617Sjulian /* _exit(EX_OSERR); not yet */ 60912024Speter } 6101553Srgrimes if (setgid(pwd->pw_gid) < 0) { 6111553Srgrimes syslog(LOG_ERR, 6128857Srgrimes "%s: can't set gid %d: %m", 6131553Srgrimes sep->se_service, pwd->pw_gid); 61419617Sjulian _exit(EX_OSERR); 6151553Srgrimes } 6161553Srgrimes (void) initgroups(pwd->pw_name, 6171553Srgrimes pwd->pw_gid); 6181553Srgrimes if (setuid(pwd->pw_uid) < 0) { 6191553Srgrimes syslog(LOG_ERR, 6208857Srgrimes "%s: can't set uid %d: %m", 6211553Srgrimes sep->se_service, pwd->pw_uid); 62219617Sjulian _exit(EX_OSERR); 6231553Srgrimes } 6241553Srgrimes } 62521640Speter#endif 6261553Srgrimes execv(sep->se_server, sep->se_argv); 6271553Srgrimes if (sep->se_socktype != SOCK_STREAM) 6281553Srgrimes recv(0, buf, sizeof (buf), 0); 6291553Srgrimes syslog(LOG_ERR, 6301553Srgrimes "cannot execute %s: %m", sep->se_server); 63119617Sjulian _exit(EX_OSERR); 6321553Srgrimes } 6331553Srgrimes } 63419618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) 6351553Srgrimes close(ctrl); 6361553Srgrimes } 6371553Srgrimes } 6381553Srgrimes} 6391553Srgrimes 64019618Sjulian/* 64119618Sjulian * Record a new child pid for this service. If we've reached the 64219618Sjulian * limit on children, then stop accepting incoming requests. 64319618Sjulian */ 64419618Sjulian 6451553Srgrimesvoid 64619618Sjulianaddchild(struct servtab *sep, pid_t pid) 64719618Sjulian{ 64819618Sjulian#ifdef SANITY_CHECK 64919618Sjulian if (sep->se_numchild >= sep->se_maxchild) { 65019618Sjulian syslog(LOG_ERR, "%s: %d >= %d", 65119618Sjulian __FUNCTION__, sep->se_numchild, sep->se_maxchild); 65219618Sjulian exit(EX_SOFTWARE); 65319618Sjulian } 65419618Sjulian#endif 65519618Sjulian if (sep->se_maxchild == 0) 65619618Sjulian return; 65719618Sjulian sep->se_pids[sep->se_numchild++] = pid; 65819618Sjulian if (sep->se_numchild == sep->se_maxchild) 65919618Sjulian disable(sep); 66019618Sjulian} 66119618Sjulian 66219618Sjulian/* 66319618Sjulian * Some child process has exited. See if it's on somebody's list. 66419618Sjulian */ 66519618Sjulian 66619618Sjulianvoid 6671553Srgrimesreapchild(signo) 6681553Srgrimes int signo; 6691553Srgrimes{ 67019618Sjulian int k, status; 6711553Srgrimes pid_t pid; 6721553Srgrimes struct servtab *sep; 6731553Srgrimes 6741553Srgrimes for (;;) { 6751553Srgrimes pid = wait3(&status, WNOHANG, (struct rusage *)0); 6761553Srgrimes if (pid <= 0) 6771553Srgrimes break; 6781553Srgrimes if (debug) 67929602Scharnier warnx("%d reaped, status %#x", pid, status); 68019618Sjulian for (sep = servtab; sep; sep = sep->se_next) { 68119618Sjulian for (k = 0; k < sep->se_numchild; k++) 68219618Sjulian if (sep->se_pids[k] == pid) 68319618Sjulian break; 68419618Sjulian if (k == sep->se_numchild) 68519618Sjulian continue; 68619618Sjulian if (sep->se_numchild == sep->se_maxchild) 68719618Sjulian enable(sep); 68819618Sjulian sep->se_pids[k] = sep->se_pids[--sep->se_numchild]; 68919618Sjulian if (status) 69019618Sjulian syslog(LOG_WARNING, 69119618Sjulian "%s[%d]: exit status 0x%x", 69219618Sjulian sep->se_server, pid, status); 69319618Sjulian break; 69419618Sjulian } 6951553Srgrimes } 6961553Srgrimes} 6971553Srgrimes 6981553Srgrimesvoid 6991553Srgrimesconfig(signo) 7001553Srgrimes int signo; 7011553Srgrimes{ 70219618Sjulian struct servtab *sep, *new, **sepp; 7031553Srgrimes long omask; 7041553Srgrimes 7051553Srgrimes if (!setconfig()) { 7061553Srgrimes syslog(LOG_ERR, "%s: %m", CONFIG); 7071553Srgrimes return; 7081553Srgrimes } 7091553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 7101553Srgrimes sep->se_checked = 0; 71119618Sjulian while ((new = getconfigent())) { 71230807Sache if (getpwnam(new->se_user) == NULL) { 7131553Srgrimes syslog(LOG_ERR, 7141553Srgrimes "%s/%s: No such user '%s', service ignored", 71519618Sjulian new->se_service, new->se_proto, new->se_user); 7161553Srgrimes continue; 7171553Srgrimes } 71830807Sache if (new->se_group && getgrnam(new->se_group) == NULL) { 71930807Sache syslog(LOG_ERR, 72030807Sache "%s/%s: No such group '%s', service ignored", 72130807Sache new->se_service, new->se_proto, new->se_group); 72230807Sache continue; 72330807Sache } 72430792Sache#ifdef LOGIN_CAP 72530792Sache if (login_getclass(new->se_class) == NULL) { 72630792Sache /* error syslogged by getclass */ 72730792Sache syslog(LOG_ERR, 72830792Sache "%s/%s: login class error, service ignored", 72930792Sache new->se_service, new->se_proto); 73030792Sache continue; 73130792Sache } 73230792Sache#endif 7331553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 73419618Sjulian if (strcmp(sep->se_service, new->se_service) == 0 && 73519618Sjulian strcmp(sep->se_proto, new->se_proto) == 0) 7361553Srgrimes break; 7371553Srgrimes if (sep != 0) { 7381553Srgrimes int i; 7391553Srgrimes 74019618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; } 7411553Srgrimes omask = sigblock(SIGBLOCK); 74219618Sjulian /* copy over outstanding child pids */ 74319618Sjulian if (sep->se_maxchild && new->se_maxchild) { 74419618Sjulian new->se_numchild = sep->se_numchild; 74519618Sjulian if (new->se_numchild > new->se_maxchild) 74619618Sjulian new->se_numchild = new->se_maxchild; 74719618Sjulian memcpy(new->se_pids, sep->se_pids, 74819618Sjulian new->se_numchild * sizeof(*new->se_pids)); 74919618Sjulian } 75019618Sjulian SWAP(sep->se_pids, new->se_pids); 75119618Sjulian sep->se_maxchild = new->se_maxchild; 75219618Sjulian sep->se_numchild = new->se_numchild; 75330847Sdima sep->se_maxcpm = new->se_maxcpm; 75419618Sjulian /* might need to turn on or off service now */ 75519618Sjulian if (sep->se_fd >= 0) { 75619618Sjulian if (sep->se_maxchild 75719618Sjulian && sep->se_numchild == sep->se_maxchild) { 75819618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 75919618Sjulian disable(sep); 76019618Sjulian } else { 76119618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) 76219618Sjulian enable(sep); 76319618Sjulian } 76419618Sjulian } 76519618Sjulian sep->se_accept = new->se_accept; 76630807Sache SWAP(sep->se_user, new->se_user); 76730807Sache SWAP(sep->se_group, new->se_group); 76830792Sache#ifdef LOGIN_CAP 76930807Sache SWAP(sep->se_class, new->se_class); 77030792Sache#endif 77130807Sache SWAP(sep->se_server, new->se_server); 7721553Srgrimes for (i = 0; i < MAXARGV; i++) 77319618Sjulian SWAP(sep->se_argv[i], new->se_argv[i]); 7741553Srgrimes sigsetmask(omask); 77519618Sjulian freeconfig(new); 7761553Srgrimes if (debug) 7771553Srgrimes print_service("REDO", sep); 7781553Srgrimes } else { 77919618Sjulian sep = enter(new); 7801553Srgrimes if (debug) 7811553Srgrimes print_service("ADD ", sep); 7821553Srgrimes } 7831553Srgrimes sep->se_checked = 1; 7841553Srgrimes if (ISMUX(sep)) { 7851553Srgrimes sep->se_fd = -1; 7861553Srgrimes continue; 7871553Srgrimes } 7882657Scsgr if (!sep->se_rpc) { 7892657Scsgr sp = getservbyname(sep->se_service, sep->se_proto); 7902657Scsgr if (sp == 0) { 7912657Scsgr syslog(LOG_ERR, "%s/%s: unknown service", 7922657Scsgr sep->se_service, sep->se_proto); 7932657Scsgr sep->se_checked = 0; 7942657Scsgr continue; 7952657Scsgr } 7962657Scsgr if (sp->s_port != sep->se_ctrladdr.sin_port) { 7972657Scsgr sep->se_ctrladdr.sin_family = AF_INET; 79822306Sjulian sep->se_ctrladdr.sin_addr = bind_address; 7992657Scsgr sep->se_ctrladdr.sin_port = sp->s_port; 8002657Scsgr if (sep->se_fd >= 0) 8012657Scsgr close_sep(sep); 8022657Scsgr } 8032657Scsgr } else { 8042657Scsgr rpc = getrpcbyname(sep->se_service); 8052657Scsgr if (rpc == 0) { 8062657Scsgr syslog(LOG_ERR, "%s/%s unknown RPC service.", 8072657Scsgr sep->se_service, sep->se_proto); 8082657Scsgr if (sep->se_fd != -1) 8092657Scsgr (void) close(sep->se_fd); 8102657Scsgr sep->se_fd = -1; 8112657Scsgr continue; 8122657Scsgr } 8132657Scsgr if (rpc->r_number != sep->se_rpc_prog) { 8142657Scsgr if (sep->se_rpc_prog) 8152657Scsgr unregisterrpc(sep); 8162657Scsgr sep->se_rpc_prog = rpc->r_number; 8172657Scsgr if (sep->se_fd != -1) 8182657Scsgr (void) close(sep->se_fd); 8192657Scsgr sep->se_fd = -1; 8202657Scsgr } 8211553Srgrimes } 8221553Srgrimes if (sep->se_fd == -1) 8231553Srgrimes setup(sep); 8241553Srgrimes } 8251553Srgrimes endconfig(); 8261553Srgrimes /* 8271553Srgrimes * Purge anything not looked at above. 8281553Srgrimes */ 8291553Srgrimes omask = sigblock(SIGBLOCK); 8301553Srgrimes sepp = &servtab; 83119617Sjulian while ((sep = *sepp)) { 8321553Srgrimes if (sep->se_checked) { 8331553Srgrimes sepp = &sep->se_next; 8341553Srgrimes continue; 8351553Srgrimes } 8361553Srgrimes *sepp = sep->se_next; 8371553Srgrimes if (sep->se_fd >= 0) 8381553Srgrimes close_sep(sep); 8391553Srgrimes if (debug) 8401553Srgrimes print_service("FREE", sep); 8412657Scsgr if (sep->se_rpc && sep->se_rpc_prog > 0) 8422657Scsgr unregisterrpc(sep); 8431553Srgrimes freeconfig(sep); 8441553Srgrimes free((char *)sep); 8451553Srgrimes } 8461553Srgrimes (void) sigsetmask(omask); 8471553Srgrimes} 8481553Srgrimes 8491553Srgrimesvoid 8502657Scsgrunregisterrpc(sep) 8512657Scsgr struct servtab *sep; 8522657Scsgr{ 8532657Scsgr int i; 8542657Scsgr struct servtab *sepp; 8552657Scsgr long omask; 8562657Scsgr 8572657Scsgr omask = sigblock(SIGBLOCK); 8582657Scsgr for (sepp = servtab; sepp; sepp = sepp->se_next) { 8592657Scsgr if (sepp == sep) 8602657Scsgr continue; 8612657Scsgr if (sep->se_checked == 0 || 8622657Scsgr !sepp->se_rpc || 8632657Scsgr sep->se_rpc_prog != sepp->se_rpc_prog) 8642657Scsgr continue; 8652657Scsgr return; 8662657Scsgr } 8672657Scsgr if (debug) 8682657Scsgr print_service("UNREG", sep); 8692657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) 8702657Scsgr pmap_unset(sep->se_rpc_prog, i); 8712657Scsgr if (sep->se_fd != -1) 8722657Scsgr (void) close(sep->se_fd); 8732657Scsgr sep->se_fd = -1; 8742657Scsgr (void) sigsetmask(omask); 8752657Scsgr} 8762657Scsgr 8772657Scsgrvoid 8781553Srgrimesretry(signo) 8791553Srgrimes int signo; 8801553Srgrimes{ 8811553Srgrimes struct servtab *sep; 8821553Srgrimes 8831553Srgrimes timingout = 0; 8841553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 88519617Sjulian if (sep->se_fd == -1 && !ISMUX(sep)) 8861553Srgrimes setup(sep); 8871553Srgrimes} 8881553Srgrimes 8891553Srgrimesvoid 8901553Srgrimessetup(sep) 8911553Srgrimes struct servtab *sep; 8921553Srgrimes{ 8931553Srgrimes int on = 1; 8941553Srgrimes 8951553Srgrimes if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) { 8961553Srgrimes if (debug) 89729602Scharnier warn("socket failed on %s/%s", 89829602Scharnier sep->se_service, sep->se_proto); 8991553Srgrimes syslog(LOG_ERR, "%s/%s: socket: %m", 9001553Srgrimes sep->se_service, sep->se_proto); 9011553Srgrimes return; 9021553Srgrimes } 9031553Srgrimes#define turnon(fd, opt) \ 9041553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) 9051553Srgrimes if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && 9061553Srgrimes turnon(sep->se_fd, SO_DEBUG) < 0) 9071553Srgrimes syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 9081553Srgrimes if (turnon(sep->se_fd, SO_REUSEADDR) < 0) 9091553Srgrimes syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); 91025253Swollman#ifdef SO_PRIVSTATE 91113956Swollman if (turnon(sep->se_fd, SO_PRIVSTATE) < 0) 91213956Swollman syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m"); 91325253Swollman#endif 9141553Srgrimes#undef turnon 9151553Srgrimes if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, 9161553Srgrimes sizeof (sep->se_ctrladdr)) < 0) { 9171553Srgrimes if (debug) 91829602Scharnier warn("bind failed on %s/%s", 91929602Scharnier sep->se_service, sep->se_proto); 9201553Srgrimes syslog(LOG_ERR, "%s/%s: bind: %m", 9211553Srgrimes sep->se_service, sep->se_proto); 9221553Srgrimes (void) close(sep->se_fd); 9231553Srgrimes sep->se_fd = -1; 9241553Srgrimes if (!timingout) { 9251553Srgrimes timingout = 1; 9261553Srgrimes alarm(RETRYTIME); 9271553Srgrimes } 9281553Srgrimes return; 9291553Srgrimes } 9302657Scsgr if (sep->se_rpc) { 9312657Scsgr int i, len = sizeof(struct sockaddr); 9322657Scsgr 9338857Srgrimes if (getsockname(sep->se_fd, 9342657Scsgr (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){ 9352657Scsgr syslog(LOG_ERR, "%s/%s: getsockname: %m", 9362657Scsgr sep->se_service, sep->se_proto); 9372657Scsgr (void) close(sep->se_fd); 9382657Scsgr sep->se_fd = -1; 9398857Srgrimes return; 9402657Scsgr } 9412657Scsgr if (debug) 9422657Scsgr print_service("REG ", sep); 9432657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { 9442657Scsgr pmap_unset(sep->se_rpc_prog, i); 9452657Scsgr pmap_set(sep->se_rpc_prog, i, 9462657Scsgr (sep->se_socktype == SOCK_DGRAM) 9472657Scsgr ? IPPROTO_UDP : IPPROTO_TCP, 9482657Scsgr ntohs(sep->se_ctrladdr.sin_port)); 9492657Scsgr } 9508857Srgrimes 9512657Scsgr } 9521553Srgrimes if (sep->se_socktype == SOCK_STREAM) 95317197Sdg listen(sep->se_fd, 64); 95419618Sjulian enable(sep); 9551553Srgrimes if (debug) { 95629602Scharnier warnx("registered %s on %d", 9571553Srgrimes sep->se_server, sep->se_fd); 9581553Srgrimes } 9591553Srgrimes} 9601553Srgrimes 9611553Srgrimes/* 9621553Srgrimes * Finish with a service and its socket. 9631553Srgrimes */ 9641553Srgrimesvoid 9651553Srgrimesclose_sep(sep) 9661553Srgrimes struct servtab *sep; 9671553Srgrimes{ 9681553Srgrimes if (sep->se_fd >= 0) { 96919618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 97019618Sjulian disable(sep); 9711553Srgrimes (void) close(sep->se_fd); 9721553Srgrimes sep->se_fd = -1; 9731553Srgrimes } 9741553Srgrimes sep->se_count = 0; 97519618Sjulian sep->se_numchild = 0; /* forget about any existing children */ 9761553Srgrimes} 9771553Srgrimes 9781553Srgrimesstruct servtab * 9791553Srgrimesenter(cp) 9801553Srgrimes struct servtab *cp; 9811553Srgrimes{ 9821553Srgrimes struct servtab *sep; 9831553Srgrimes long omask; 9841553Srgrimes 9851553Srgrimes sep = (struct servtab *)malloc(sizeof (*sep)); 9861553Srgrimes if (sep == (struct servtab *)0) { 9871553Srgrimes syslog(LOG_ERR, "Out of memory."); 98819617Sjulian exit(EX_OSERR); 9891553Srgrimes } 9901553Srgrimes *sep = *cp; 9911553Srgrimes sep->se_fd = -1; 9921553Srgrimes omask = sigblock(SIGBLOCK); 9931553Srgrimes sep->se_next = servtab; 9941553Srgrimes servtab = sep; 9951553Srgrimes sigsetmask(omask); 9961553Srgrimes return (sep); 9971553Srgrimes} 9981553Srgrimes 99919618Sjulianvoid 100019618Sjulianenable(struct servtab *sep) 100119618Sjulian{ 100219618Sjulian if (debug) 100329602Scharnier warnx( 100419618Sjulian "enabling %s, fd %d", sep->se_service, sep->se_fd); 100519618Sjulian#ifdef SANITY_CHECK 100619618Sjulian if (sep->se_fd < 0) { 100719618Sjulian syslog(LOG_ERR, 100819618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 100919618Sjulian exit(EX_SOFTWARE); 101019618Sjulian } 101119618Sjulian if (ISMUX(sep)) { 101219618Sjulian syslog(LOG_ERR, 101319618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 101419618Sjulian exit(EX_SOFTWARE); 101519618Sjulian } 101619618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) { 101719618Sjulian syslog(LOG_ERR, 101819618Sjulian "%s: %s: not off", __FUNCTION__, sep->se_service); 101919618Sjulian exit(EX_SOFTWARE); 102019618Sjulian } 102119618Sjulian#endif 102219618Sjulian FD_SET(sep->se_fd, &allsock); 102319618Sjulian nsock++; 102419618Sjulian if (sep->se_fd > maxsock) 102519618Sjulian maxsock = sep->se_fd; 102619618Sjulian} 102719618Sjulian 102819618Sjulianvoid 102919618Sjuliandisable(struct servtab *sep) 103019618Sjulian{ 103119618Sjulian if (debug) 103229602Scharnier warnx( 103319618Sjulian "disabling %s, fd %d", sep->se_service, sep->se_fd); 103419618Sjulian#ifdef SANITY_CHECK 103519618Sjulian if (sep->se_fd < 0) { 103619618Sjulian syslog(LOG_ERR, 103719618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 103819618Sjulian exit(EX_SOFTWARE); 103919618Sjulian } 104019618Sjulian if (ISMUX(sep)) { 104119618Sjulian syslog(LOG_ERR, 104219618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 104319618Sjulian exit(EX_SOFTWARE); 104419618Sjulian } 104519618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) { 104619618Sjulian syslog(LOG_ERR, 104719618Sjulian "%s: %s: not on", __FUNCTION__, sep->se_service); 104819618Sjulian exit(EX_SOFTWARE); 104919618Sjulian } 105019618Sjulian if (nsock == 0) { 105119618Sjulian syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 105219618Sjulian exit(EX_SOFTWARE); 105319618Sjulian } 105419618Sjulian#endif 105519618Sjulian FD_CLR(sep->se_fd, &allsock); 105619618Sjulian nsock--; 105719618Sjulian if (sep->se_fd == maxsock) 105819618Sjulian maxsock--; 105919618Sjulian} 106019618Sjulian 10611553SrgrimesFILE *fconfig = NULL; 10621553Srgrimesstruct servtab serv; 10631553Srgrimeschar line[LINE_MAX]; 10641553Srgrimes 10651553Srgrimesint 10661553Srgrimessetconfig() 10671553Srgrimes{ 10681553Srgrimes 10691553Srgrimes if (fconfig != NULL) { 10701553Srgrimes fseek(fconfig, 0L, SEEK_SET); 10711553Srgrimes return (1); 10721553Srgrimes } 10731553Srgrimes fconfig = fopen(CONFIG, "r"); 10741553Srgrimes return (fconfig != NULL); 10751553Srgrimes} 10761553Srgrimes 10771553Srgrimesvoid 10781553Srgrimesendconfig() 10791553Srgrimes{ 10801553Srgrimes if (fconfig) { 10811553Srgrimes (void) fclose(fconfig); 10821553Srgrimes fconfig = NULL; 10831553Srgrimes } 10841553Srgrimes} 10851553Srgrimes 10861553Srgrimesstruct servtab * 10871553Srgrimesgetconfigent() 10881553Srgrimes{ 10891553Srgrimes struct servtab *sep = &serv; 10901553Srgrimes int argc; 109119618Sjulian char *cp, *arg, *s; 10922657Scsgr char *versp; 10931553Srgrimes static char TCPMUX_TOKEN[] = "tcpmux/"; 10941553Srgrimes#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1) 10951553Srgrimes 10961553Srgrimesmore: 10971553Srgrimes while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0')) 10981553Srgrimes ; 10991553Srgrimes if (cp == NULL) 11001553Srgrimes return ((struct servtab *)0); 11011553Srgrimes /* 11021553Srgrimes * clear the static buffer, since some fields (se_ctrladdr, 11031553Srgrimes * for example) don't get initialized here. 11041553Srgrimes */ 11051553Srgrimes memset((caddr_t)sep, 0, sizeof *sep); 11061553Srgrimes arg = skip(&cp); 11071553Srgrimes if (cp == NULL) { 11081553Srgrimes /* got an empty line containing just blanks/tabs. */ 11091553Srgrimes goto more; 11101553Srgrimes } 11111553Srgrimes if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { 11121553Srgrimes char *c = arg + MUX_LEN; 11131553Srgrimes if (*c == '+') { 11141553Srgrimes sep->se_type = MUXPLUS_TYPE; 11151553Srgrimes c++; 11161553Srgrimes } else 11171553Srgrimes sep->se_type = MUX_TYPE; 11181553Srgrimes sep->se_service = newstr(c); 11191553Srgrimes } else { 11201553Srgrimes sep->se_service = newstr(arg); 11211553Srgrimes sep->se_type = NORM_TYPE; 11221553Srgrimes } 11231553Srgrimes arg = sskip(&cp); 11241553Srgrimes if (strcmp(arg, "stream") == 0) 11251553Srgrimes sep->se_socktype = SOCK_STREAM; 11261553Srgrimes else if (strcmp(arg, "dgram") == 0) 11271553Srgrimes sep->se_socktype = SOCK_DGRAM; 11281553Srgrimes else if (strcmp(arg, "rdm") == 0) 11291553Srgrimes sep->se_socktype = SOCK_RDM; 11301553Srgrimes else if (strcmp(arg, "seqpacket") == 0) 11311553Srgrimes sep->se_socktype = SOCK_SEQPACKET; 11321553Srgrimes else if (strcmp(arg, "raw") == 0) 11331553Srgrimes sep->se_socktype = SOCK_RAW; 11341553Srgrimes else 11351553Srgrimes sep->se_socktype = -1; 11361553Srgrimes sep->se_proto = newstr(sskip(&cp)); 11372657Scsgr if (strncmp(sep->se_proto, "rpc/", 4) == 0) { 113819237Sjoerg memmove(sep->se_proto, sep->se_proto + 4, 113919237Sjoerg strlen(sep->se_proto) + 1 - 4); 11402657Scsgr sep->se_rpc = 1; 11412657Scsgr sep->se_rpc_prog = sep->se_rpc_lowvers = 11422657Scsgr sep->se_rpc_lowvers = 0; 11432657Scsgr sep->se_ctrladdr.sin_family = AF_INET; 11442657Scsgr sep->se_ctrladdr.sin_port = 0; 114517482Sjulian sep->se_ctrladdr.sin_addr = bind_address; 11462657Scsgr if ((versp = rindex(sep->se_service, '/'))) { 11472657Scsgr *versp++ = '\0'; 11482657Scsgr switch (sscanf(versp, "%d-%d", 11492657Scsgr &sep->se_rpc_lowvers, 11502657Scsgr &sep->se_rpc_highvers)) { 11512657Scsgr case 2: 11522657Scsgr break; 11532657Scsgr case 1: 11542657Scsgr sep->se_rpc_highvers = 11552657Scsgr sep->se_rpc_lowvers; 11562657Scsgr break; 11572657Scsgr default: 11588857Srgrimes syslog(LOG_ERR, 11598857Srgrimes "bad RPC version specifier; %s\n", 11602657Scsgr sep->se_service); 11612657Scsgr freeconfig(sep); 11622657Scsgr goto more; 11632657Scsgr } 11642657Scsgr } 11652657Scsgr else { 11662657Scsgr sep->se_rpc_lowvers = 11672657Scsgr sep->se_rpc_highvers = 1; 11682657Scsgr } 11692657Scsgr } 11701553Srgrimes arg = sskip(&cp); 117119618Sjulian if (!strncmp(arg, "wait", 4)) 117219618Sjulian sep->se_accept = 0; 117319618Sjulian else if (!strncmp(arg, "nowait", 6)) 117419618Sjulian sep->se_accept = 1; 117519618Sjulian else { 117619618Sjulian syslog(LOG_ERR, 117719618Sjulian "%s: bad wait/nowait for service %s", 117819618Sjulian CONFIG, sep->se_service); 117919618Sjulian goto more; 118019618Sjulian } 118133794Spst sep->se_maxchild = maxchild; 118233794Spst sep->se_maxcpm = maxcpm; 118319618Sjulian if ((s = strchr(arg, '/')) != NULL) { 118419618Sjulian char *eptr; 118519618Sjulian u_long val; 118619618Sjulian 118719618Sjulian val = strtoul(s + 1, &eptr, 10); 118830847Sdima if (eptr == s + 1 || val > MAX_MAXCHLD) { 118919618Sjulian syslog(LOG_ERR, 119019618Sjulian "%s: bad max-child for service %s", 119119618Sjulian CONFIG, sep->se_service); 119219618Sjulian goto more; 119319618Sjulian } 119419618Sjulian sep->se_maxchild = val; 119530847Sdima if (*eptr == '/') 119630847Sdima sep->se_maxcpm = strtol(eptr + 1, &eptr, 10); 119730847Sdima /* 119830847Sdima * explicitly do not check for \0 for future expansion / 119930847Sdima * backwards compatibility 120030847Sdima */ 120119618Sjulian } 12021553Srgrimes if (ISMUX(sep)) { 12031553Srgrimes /* 120419618Sjulian * Silently enforce "nowait" mode for TCPMUX services 120519618Sjulian * since they don't have an assigned port to listen on. 12061553Srgrimes */ 120719618Sjulian sep->se_accept = 1; 12081553Srgrimes if (strcmp(sep->se_proto, "tcp")) { 12098857Srgrimes syslog(LOG_ERR, 12101553Srgrimes "%s: bad protocol for tcpmux service %s", 12111553Srgrimes CONFIG, sep->se_service); 12121553Srgrimes goto more; 12131553Srgrimes } 12141553Srgrimes if (sep->se_socktype != SOCK_STREAM) { 12158857Srgrimes syslog(LOG_ERR, 12161553Srgrimes "%s: bad socket type for tcpmux service %s", 12171553Srgrimes CONFIG, sep->se_service); 12181553Srgrimes goto more; 12191553Srgrimes } 12201553Srgrimes } 12211553Srgrimes sep->se_user = newstr(sskip(&cp)); 122230792Sache#ifdef LOGIN_CAP 122330792Sache if ((s = strrchr(sep->se_user, '/')) != NULL) { 122430792Sache *s = '\0'; 122530792Sache sep->se_class = newstr(s + 1); 122630792Sache } else 122730792Sache sep->se_class = newstr(RESOURCE_RC); 122830792Sache#endif 122930807Sache if ((s = strrchr(sep->se_user, ':')) != NULL) { 123030807Sache *s = '\0'; 123130807Sache sep->se_group = newstr(s + 1); 123230807Sache } else 123330807Sache sep->se_group = NULL; 12341553Srgrimes sep->se_server = newstr(sskip(&cp)); 12351553Srgrimes if (strcmp(sep->se_server, "internal") == 0) { 12361553Srgrimes struct biltin *bi; 12371553Srgrimes 12381553Srgrimes for (bi = biltins; bi->bi_service; bi++) 12391553Srgrimes if (bi->bi_socktype == sep->se_socktype && 12401553Srgrimes strcmp(bi->bi_service, sep->se_service) == 0) 12411553Srgrimes break; 12421553Srgrimes if (bi->bi_service == 0) { 12431553Srgrimes syslog(LOG_ERR, "internal service %s unknown", 12441553Srgrimes sep->se_service); 12451553Srgrimes goto more; 12461553Srgrimes } 124719618Sjulian sep->se_accept = 1; /* force accept mode for built-ins */ 12481553Srgrimes sep->se_bi = bi; 12491553Srgrimes } else 12501553Srgrimes sep->se_bi = NULL; 125119618Sjulian if (sep->se_maxchild < 0) /* apply default max-children */ 125219618Sjulian if (sep->se_bi) 125319618Sjulian sep->se_maxchild = sep->se_bi->bi_maxchild; 125419618Sjulian else 125519618Sjulian sep->se_maxchild = sep->se_accept ? 0 : 1; 125619618Sjulian if (sep->se_maxchild) { 125719618Sjulian sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); 125819618Sjulian if (sep->se_pids == NULL) { 125919618Sjulian syslog(LOG_ERR, "Out of memory."); 126019618Sjulian exit(EX_OSERR); 126119618Sjulian } 126219618Sjulian } 12631553Srgrimes argc = 0; 12641553Srgrimes for (arg = skip(&cp); cp; arg = skip(&cp)) 126519618Sjulian if (argc < MAXARGV) { 12661553Srgrimes sep->se_argv[argc++] = newstr(arg); 126719618Sjulian } else { 126819618Sjulian syslog(LOG_ERR, 126919618Sjulian "%s: too many arguments for service %s", 127019618Sjulian CONFIG, sep->se_service); 127119618Sjulian goto more; 127219618Sjulian } 12731553Srgrimes while (argc <= MAXARGV) 12741553Srgrimes sep->se_argv[argc++] = NULL; 12751553Srgrimes return (sep); 12761553Srgrimes} 12771553Srgrimes 12781553Srgrimesvoid 12791553Srgrimesfreeconfig(cp) 12801553Srgrimes struct servtab *cp; 12811553Srgrimes{ 12821553Srgrimes int i; 12831553Srgrimes 12841553Srgrimes if (cp->se_service) 12851553Srgrimes free(cp->se_service); 12861553Srgrimes if (cp->se_proto) 12871553Srgrimes free(cp->se_proto); 12881553Srgrimes if (cp->se_user) 12891553Srgrimes free(cp->se_user); 129030807Sache if (cp->se_group) 129130807Sache free(cp->se_group); 129230792Sache#ifdef LOGIN_CAP 129330792Sache if (cp->se_class) 129430792Sache free(cp->se_class); 129530792Sache#endif 12961553Srgrimes if (cp->se_server) 12971553Srgrimes free(cp->se_server); 129819618Sjulian if (cp->se_pids) 129919618Sjulian free(cp->se_pids); 13001553Srgrimes for (i = 0; i < MAXARGV; i++) 13011553Srgrimes if (cp->se_argv[i]) 13021553Srgrimes free(cp->se_argv[i]); 13031553Srgrimes} 13041553Srgrimes 13051553Srgrimes 13061553Srgrimes/* 13071553Srgrimes * Safe skip - if skip returns null, log a syntax error in the 13081553Srgrimes * configuration file and exit. 13091553Srgrimes */ 13101553Srgrimeschar * 13111553Srgrimessskip(cpp) 13121553Srgrimes char **cpp; 13131553Srgrimes{ 13141553Srgrimes char *cp; 13151553Srgrimes 13161553Srgrimes cp = skip(cpp); 13171553Srgrimes if (cp == NULL) { 13181553Srgrimes syslog(LOG_ERR, "%s: syntax error", CONFIG); 131919617Sjulian exit(EX_DATAERR); 13201553Srgrimes } 13211553Srgrimes return (cp); 13221553Srgrimes} 13231553Srgrimes 13241553Srgrimeschar * 13251553Srgrimesskip(cpp) 13261553Srgrimes char **cpp; 13271553Srgrimes{ 13281553Srgrimes char *cp = *cpp; 13291553Srgrimes char *start; 133011933Sadam char quote = '\0'; 13311553Srgrimes 13321553Srgrimesagain: 13331553Srgrimes while (*cp == ' ' || *cp == '\t') 13341553Srgrimes cp++; 13351553Srgrimes if (*cp == '\0') { 13361553Srgrimes int c; 13371553Srgrimes 13381553Srgrimes c = getc(fconfig); 13391553Srgrimes (void) ungetc(c, fconfig); 13401553Srgrimes if (c == ' ' || c == '\t') 134119617Sjulian if ((cp = nextline(fconfig))) 13421553Srgrimes goto again; 13431553Srgrimes *cpp = (char *)0; 13441553Srgrimes return ((char *)0); 13451553Srgrimes } 134611933Sadam if (*cp == '"' || *cp == '\'') 134711933Sadam quote = *cp++; 13481553Srgrimes start = cp; 134911933Sadam if (quote) 135011933Sadam while (*cp && *cp != quote) 135111933Sadam cp++; 135211933Sadam else 135311933Sadam while (*cp && *cp != ' ' && *cp != '\t') 135411933Sadam cp++; 13551553Srgrimes if (*cp != '\0') 13561553Srgrimes *cp++ = '\0'; 13571553Srgrimes *cpp = cp; 13581553Srgrimes return (start); 13591553Srgrimes} 13601553Srgrimes 13611553Srgrimeschar * 13621553Srgrimesnextline(fd) 13631553Srgrimes FILE *fd; 13641553Srgrimes{ 13651553Srgrimes char *cp; 13661553Srgrimes 13671553Srgrimes if (fgets(line, sizeof (line), fd) == NULL) 13681553Srgrimes return ((char *)0); 13691553Srgrimes cp = strchr(line, '\n'); 13701553Srgrimes if (cp) 13711553Srgrimes *cp = '\0'; 13721553Srgrimes return (line); 13731553Srgrimes} 13741553Srgrimes 13751553Srgrimeschar * 13761553Srgrimesnewstr(cp) 13771553Srgrimes char *cp; 13781553Srgrimes{ 137919617Sjulian if ((cp = strdup(cp ? cp : ""))) 13801553Srgrimes return (cp); 13811553Srgrimes syslog(LOG_ERR, "strdup: %m"); 138219617Sjulian exit(EX_OSERR); 13831553Srgrimes} 13841553Srgrimes 138513142Speter#ifdef OLD_SETPROCTITLE 13861553Srgrimesvoid 138713142Speterinetd_setproctitle(a, s) 13881553Srgrimes char *a; 13891553Srgrimes int s; 13901553Srgrimes{ 13911553Srgrimes int size; 13921553Srgrimes char *cp; 13931553Srgrimes struct sockaddr_in sin; 13941553Srgrimes char buf[80]; 13951553Srgrimes 13961553Srgrimes cp = Argv[0]; 13971553Srgrimes size = sizeof(sin); 13981553Srgrimes if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) 13998857Srgrimes (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr)); 14001553Srgrimes else 14018857Srgrimes (void) sprintf(buf, "-%s", a); 14021553Srgrimes strncpy(cp, buf, LastArg - cp); 14031553Srgrimes cp += strlen(cp); 14041553Srgrimes while (cp < LastArg) 14051553Srgrimes *cp++ = ' '; 14061553Srgrimes} 140713142Speter#else 140813142Spetervoid 140913142Speterinetd_setproctitle(a, s) 141013142Speter char *a; 141113142Speter int s; 141213142Speter{ 141313142Speter int size; 141413142Speter struct sockaddr_in sin; 141513142Speter char buf[80]; 14161553Srgrimes 141713142Speter size = sizeof(sin); 141813142Speter if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) 141913142Speter (void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr)); 142013142Speter else 142113142Speter (void) sprintf(buf, "%s", a); 142213142Speter setproctitle("%s", buf); 142313142Speter} 142413142Speter#endif 142513142Speter 142613142Speter 14271553Srgrimes/* 14281553Srgrimes * Internet services provided internally by inetd: 14291553Srgrimes */ 14301553Srgrimes#define BUFSIZE 8192 14311553Srgrimes 14321553Srgrimes/* ARGSUSED */ 14331553Srgrimesvoid 14341553Srgrimesecho_stream(s, sep) /* Echo service -- echo data back */ 14351553Srgrimes int s; 14361553Srgrimes struct servtab *sep; 14371553Srgrimes{ 14381553Srgrimes char buffer[BUFSIZE]; 14391553Srgrimes int i; 14401553Srgrimes 144113142Speter inetd_setproctitle(sep->se_service, s); 14421553Srgrimes while ((i = read(s, buffer, sizeof(buffer))) > 0 && 14431553Srgrimes write(s, buffer, i) > 0) 14441553Srgrimes ; 14451553Srgrimes exit(0); 14461553Srgrimes} 14471553Srgrimes 14485182Swollmanint check_loop(sin, sep) 14495182Swollman struct sockaddr_in *sin; 14505182Swollman struct servtab *sep; 14515182Swollman{ 14525182Swollman struct servtab *se2; 14535182Swollman 14545182Swollman for (se2 = servtab; se2; se2 = se2->se_next) { 14555182Swollman if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM) 14565182Swollman continue; 14575182Swollman 14585182Swollman if (sin->sin_port == se2->se_ctrladdr.sin_port) { 14595182Swollman syslog(LOG_WARNING, 14605182Swollman "%s/%s:%s/%s loop request REFUSED from %s", 14618857Srgrimes sep->se_service, sep->se_proto, 14625182Swollman se2->se_service, se2->se_proto, 14635182Swollman inet_ntoa(sin->sin_addr)); 14645182Swollman return 1; 14655182Swollman } 14665182Swollman } 14675182Swollman return 0; 14685182Swollman} 14695182Swollman 14701553Srgrimes/* ARGSUSED */ 14711553Srgrimesvoid 14721553Srgrimesecho_dg(s, sep) /* Echo service -- echo data back */ 14731553Srgrimes int s; 14741553Srgrimes struct servtab *sep; 14751553Srgrimes{ 14761553Srgrimes char buffer[BUFSIZE]; 14771553Srgrimes int i, size; 14785182Swollman struct sockaddr_in sin; 14791553Srgrimes 14805182Swollman size = sizeof(sin); 14818857Srgrimes if ((i = recvfrom(s, buffer, sizeof(buffer), 0, 14825182Swollman (struct sockaddr *)&sin, &size)) < 0) 14831553Srgrimes return; 14845182Swollman 14855182Swollman if (check_loop(&sin, sep)) 14865182Swollman return; 14875182Swollman 14885182Swollman (void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin, 14895182Swollman sizeof(sin)); 14901553Srgrimes} 14911553Srgrimes 14921553Srgrimes/* ARGSUSED */ 14931553Srgrimesvoid 14941553Srgrimesdiscard_stream(s, sep) /* Discard service -- ignore data */ 14951553Srgrimes int s; 14961553Srgrimes struct servtab *sep; 14971553Srgrimes{ 14981553Srgrimes int ret; 14991553Srgrimes char buffer[BUFSIZE]; 15001553Srgrimes 150113142Speter inetd_setproctitle(sep->se_service, s); 15021553Srgrimes while (1) { 15031553Srgrimes while ((ret = read(s, buffer, sizeof(buffer))) > 0) 15041553Srgrimes ; 15051553Srgrimes if (ret == 0 || errno != EINTR) 15061553Srgrimes break; 15071553Srgrimes } 15081553Srgrimes exit(0); 15091553Srgrimes} 15101553Srgrimes 15111553Srgrimes/* ARGSUSED */ 15121553Srgrimesvoid 15131553Srgrimesdiscard_dg(s, sep) /* Discard service -- ignore data */ 15141553Srgrimes int s; 15151553Srgrimes struct servtab *sep; 15161553Srgrimes{ 15171553Srgrimes char buffer[BUFSIZE]; 15181553Srgrimes 15191553Srgrimes (void) read(s, buffer, sizeof(buffer)); 15201553Srgrimes} 15211553Srgrimes 15221553Srgrimes#include <ctype.h> 15231553Srgrimes#define LINESIZ 72 15241553Srgrimeschar ring[128]; 15251553Srgrimeschar *endring; 15261553Srgrimes 15271553Srgrimesvoid 15281553Srgrimesinitring() 15291553Srgrimes{ 15301553Srgrimes int i; 15311553Srgrimes 15321553Srgrimes endring = ring; 15331553Srgrimes 15341553Srgrimes for (i = 0; i <= 128; ++i) 15351553Srgrimes if (isprint(i)) 15361553Srgrimes *endring++ = i; 15371553Srgrimes} 15381553Srgrimes 15391553Srgrimes/* ARGSUSED */ 15401553Srgrimesvoid 15411553Srgrimeschargen_stream(s, sep) /* Character generator */ 15421553Srgrimes int s; 15431553Srgrimes struct servtab *sep; 15441553Srgrimes{ 15451553Srgrimes int len; 15461553Srgrimes char *rs, text[LINESIZ+2]; 15471553Srgrimes 154813142Speter inetd_setproctitle(sep->se_service, s); 15491553Srgrimes 15501553Srgrimes if (!endring) { 15511553Srgrimes initring(); 15521553Srgrimes rs = ring; 15531553Srgrimes } 15541553Srgrimes 15551553Srgrimes text[LINESIZ] = '\r'; 15561553Srgrimes text[LINESIZ + 1] = '\n'; 15571553Srgrimes for (rs = ring;;) { 15581553Srgrimes if ((len = endring - rs) >= LINESIZ) 15591553Srgrimes memmove(text, rs, LINESIZ); 15601553Srgrimes else { 15611553Srgrimes memmove(text, rs, len); 15621553Srgrimes memmove(text + len, ring, LINESIZ - len); 15631553Srgrimes } 15641553Srgrimes if (++rs == endring) 15651553Srgrimes rs = ring; 15661553Srgrimes if (write(s, text, sizeof(text)) != sizeof(text)) 15671553Srgrimes break; 15681553Srgrimes } 15691553Srgrimes exit(0); 15701553Srgrimes} 15711553Srgrimes 15721553Srgrimes/* ARGSUSED */ 15731553Srgrimesvoid 15741553Srgrimeschargen_dg(s, sep) /* Character generator */ 15751553Srgrimes int s; 15761553Srgrimes struct servtab *sep; 15771553Srgrimes{ 15785182Swollman struct sockaddr_in sin; 15791553Srgrimes static char *rs; 15801553Srgrimes int len, size; 15811553Srgrimes char text[LINESIZ+2]; 15821553Srgrimes 15831553Srgrimes if (endring == 0) { 15841553Srgrimes initring(); 15851553Srgrimes rs = ring; 15861553Srgrimes } 15871553Srgrimes 15885182Swollman size = sizeof(sin); 15898857Srgrimes if (recvfrom(s, text, sizeof(text), 0, 15905182Swollman (struct sockaddr *)&sin, &size) < 0) 15911553Srgrimes return; 15921553Srgrimes 15935182Swollman if (check_loop(&sin, sep)) 15945182Swollman return; 15955182Swollman 15961553Srgrimes if ((len = endring - rs) >= LINESIZ) 15971553Srgrimes memmove(text, rs, LINESIZ); 15981553Srgrimes else { 15991553Srgrimes memmove(text, rs, len); 16001553Srgrimes memmove(text + len, ring, LINESIZ - len); 16011553Srgrimes } 16021553Srgrimes if (++rs == endring) 16031553Srgrimes rs = ring; 16041553Srgrimes text[LINESIZ] = '\r'; 16051553Srgrimes text[LINESIZ + 1] = '\n'; 16068857Srgrimes (void) sendto(s, text, sizeof(text), 0, 16075182Swollman (struct sockaddr *)&sin, sizeof(sin)); 16081553Srgrimes} 16091553Srgrimes 16101553Srgrimes/* 16111553Srgrimes * Return a machine readable date and time, in the form of the 16121553Srgrimes * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 16131553Srgrimes * returns the number of seconds since midnight, Jan 1, 1970, 16141553Srgrimes * we must add 2208988800 seconds to this figure to make up for 16151553Srgrimes * some seventy years Bell Labs was asleep. 16161553Srgrimes */ 16171553Srgrimes 16181553Srgrimeslong 16191553Srgrimesmachtime() 16201553Srgrimes{ 16211553Srgrimes struct timeval tv; 16221553Srgrimes 16231553Srgrimes if (gettimeofday(&tv, (struct timezone *)0) < 0) { 16241553Srgrimes if (debug) 162529602Scharnier warnx("unable to get time of day"); 16261553Srgrimes return (0L); 16271553Srgrimes } 16281553Srgrimes#define OFFSET ((u_long)25567 * 24*60*60) 16291553Srgrimes return (htonl((long)(tv.tv_sec + OFFSET))); 16301553Srgrimes#undef OFFSET 16311553Srgrimes} 16321553Srgrimes 16331553Srgrimes/* ARGSUSED */ 16341553Srgrimesvoid 16351553Srgrimesmachtime_stream(s, sep) 16361553Srgrimes int s; 16371553Srgrimes struct servtab *sep; 16381553Srgrimes{ 16391553Srgrimes long result; 16401553Srgrimes 16411553Srgrimes result = machtime(); 16421553Srgrimes (void) write(s, (char *) &result, sizeof(result)); 16431553Srgrimes} 16441553Srgrimes 16451553Srgrimes/* ARGSUSED */ 16461553Srgrimesvoid 16471553Srgrimesmachtime_dg(s, sep) 16481553Srgrimes int s; 16491553Srgrimes struct servtab *sep; 16501553Srgrimes{ 16511553Srgrimes long result; 16525182Swollman struct sockaddr_in sin; 16531553Srgrimes int size; 16541553Srgrimes 16555182Swollman size = sizeof(sin); 16568857Srgrimes if (recvfrom(s, (char *)&result, sizeof(result), 0, 16575182Swollman (struct sockaddr *)&sin, &size) < 0) 16581553Srgrimes return; 16595182Swollman 16605182Swollman if (check_loop(&sin, sep)) 16615182Swollman return; 16625182Swollman 16631553Srgrimes result = machtime(); 16648857Srgrimes (void) sendto(s, (char *) &result, sizeof(result), 0, 16655182Swollman (struct sockaddr *)&sin, sizeof(sin)); 16661553Srgrimes} 16671553Srgrimes 16681553Srgrimes/* ARGSUSED */ 16691553Srgrimesvoid 16701553Srgrimesdaytime_stream(s, sep) /* Return human-readable time of day */ 16711553Srgrimes int s; 16721553Srgrimes struct servtab *sep; 16731553Srgrimes{ 16741553Srgrimes char buffer[256]; 16751553Srgrimes time_t clock; 16761553Srgrimes 16771553Srgrimes clock = time((time_t *) 0); 16781553Srgrimes 16791553Srgrimes (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 16801553Srgrimes (void) write(s, buffer, strlen(buffer)); 16811553Srgrimes} 16821553Srgrimes 16831553Srgrimes/* ARGSUSED */ 16841553Srgrimesvoid 16851553Srgrimesdaytime_dg(s, sep) /* Return human-readable time of day */ 16861553Srgrimes int s; 16871553Srgrimes struct servtab *sep; 16881553Srgrimes{ 16891553Srgrimes char buffer[256]; 16901553Srgrimes time_t clock; 16915182Swollman struct sockaddr_in sin; 16921553Srgrimes int size; 16931553Srgrimes 16941553Srgrimes clock = time((time_t *) 0); 16951553Srgrimes 16965182Swollman size = sizeof(sin); 16978857Srgrimes if (recvfrom(s, buffer, sizeof(buffer), 0, 16985182Swollman (struct sockaddr *)&sin, &size) < 0) 16991553Srgrimes return; 17005182Swollman 17015182Swollman if (check_loop(&sin, sep)) 17025182Swollman return; 17035182Swollman 17041553Srgrimes (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 17058857Srgrimes (void) sendto(s, buffer, strlen(buffer), 0, 17065182Swollman (struct sockaddr *)&sin, sizeof(sin)); 17071553Srgrimes} 17081553Srgrimes 17091553Srgrimes/* 17101553Srgrimes * print_service: 17111553Srgrimes * Dump relevant information to stderr 17121553Srgrimes */ 17131553Srgrimesvoid 17141553Srgrimesprint_service(action, sep) 17151553Srgrimes char *action; 17161553Srgrimes struct servtab *sep; 17171553Srgrimes{ 171819617Sjulian fprintf(stderr, 171930792Sache#ifdef LOGIN_CAP 172030807Sache "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%x server=%s\n", 172130792Sache#else 172230807Sache "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%x server=%s\n", 172330792Sache#endif 172419617Sjulian action, sep->se_service, sep->se_proto, 172530807Sache sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group, 172630792Sache#ifdef LOGIN_CAP 172730792Sache sep->se_class, 172830792Sache#endif 172919617Sjulian (int)sep->se_bi, sep->se_server); 17301553Srgrimes} 17311553Srgrimes 17321553Srgrimes/* 17331553Srgrimes * Based on TCPMUX.C by Mark K. Lottor November 1988 17341553Srgrimes * sri-nic::ps:<mkl>tcpmux.c 17351553Srgrimes */ 17361553Srgrimes 17371553Srgrimes 17381553Srgrimesstatic int /* # of characters upto \r,\n or \0 */ 17391553Srgrimesgetline(fd, buf, len) 17401553Srgrimes int fd; 17411553Srgrimes char *buf; 17421553Srgrimes int len; 17431553Srgrimes{ 17441553Srgrimes int count = 0, n; 17451553Srgrimes 17461553Srgrimes do { 17471553Srgrimes n = read(fd, buf, len-count); 17481553Srgrimes if (n == 0) 17491553Srgrimes return (count); 17501553Srgrimes if (n < 0) 17511553Srgrimes return (-1); 17521553Srgrimes while (--n >= 0) { 17531553Srgrimes if (*buf == '\r' || *buf == '\n' || *buf == '\0') 17541553Srgrimes return (count); 17551553Srgrimes count++; 17561553Srgrimes buf++; 17571553Srgrimes } 17581553Srgrimes } while (count < len); 17591553Srgrimes return (count); 17601553Srgrimes} 17611553Srgrimes 17621553Srgrimes#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 17631553Srgrimes 17641553Srgrimes#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 17651553Srgrimes 17661553Srgrimesstruct servtab * 17671553Srgrimestcpmux(s) 17681553Srgrimes int s; 17691553Srgrimes{ 17701553Srgrimes struct servtab *sep; 17711553Srgrimes char service[MAX_SERV_LEN+1]; 17721553Srgrimes int len; 17731553Srgrimes 17741553Srgrimes /* Get requested service name */ 17751553Srgrimes if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 17761553Srgrimes strwrite(s, "-Error reading service name\r\n"); 17771553Srgrimes return (NULL); 17781553Srgrimes } 17791553Srgrimes service[len] = '\0'; 17801553Srgrimes 17811553Srgrimes if (debug) 178229602Scharnier warnx("tcpmux: someone wants %s", service); 17831553Srgrimes 17841553Srgrimes /* 17851553Srgrimes * Help is a required command, and lists available services, 17861553Srgrimes * one per line. 17871553Srgrimes */ 17881553Srgrimes if (!strcasecmp(service, "help")) { 17891553Srgrimes for (sep = servtab; sep; sep = sep->se_next) { 17901553Srgrimes if (!ISMUX(sep)) 17911553Srgrimes continue; 17921553Srgrimes (void)write(s,sep->se_service,strlen(sep->se_service)); 17931553Srgrimes strwrite(s, "\r\n"); 17941553Srgrimes } 17951553Srgrimes return (NULL); 17961553Srgrimes } 17971553Srgrimes 17981553Srgrimes /* Try matching a service in inetd.conf with the request */ 17991553Srgrimes for (sep = servtab; sep; sep = sep->se_next) { 18001553Srgrimes if (!ISMUX(sep)) 18011553Srgrimes continue; 18021553Srgrimes if (!strcasecmp(service, sep->se_service)) { 18031553Srgrimes if (ISMUXPLUS(sep)) { 18041553Srgrimes strwrite(s, "+Go\r\n"); 18051553Srgrimes } 18061553Srgrimes return (sep); 18071553Srgrimes } 18081553Srgrimes } 18091553Srgrimes strwrite(s, "-Service not available\r\n"); 18101553Srgrimes return (NULL); 18111553Srgrimes} 181230847Sdima 181330847Sdima#define CPMHSIZE 256 181430847Sdima#define CPMHMASK (CPMHSIZE-1) 181530847Sdima#define CHTGRAN 10 181630847Sdima#define CHTSIZE 6 181730847Sdima 181830847Sdimatypedef struct CTime { 181930847Sdima unsigned long ct_Ticks; 182030847Sdima int ct_Count; 182130847Sdima} CTime; 182230847Sdima 182330847Sdimatypedef struct CHash { 182430847Sdima struct in_addr ch_Addr; 182530847Sdima time_t ch_LTime; 182630847Sdima char *ch_Service; 182730847Sdima CTime ch_Times[CHTSIZE]; 182830847Sdima} CHash; 182930847Sdima 183030847SdimaCHash CHashAry[CPMHSIZE]; 183130847Sdima 183230847Sdimaint 183330847Sdimacpmip(sep, ctrl) 183430847Sdima struct servtab *sep; 183530847Sdima int ctrl; 183630847Sdima{ 183730847Sdima struct sockaddr_in rsin; 183830847Sdima int rsinLen = sizeof(rsin); 183930847Sdima int r = 0; 184030847Sdima 184130847Sdima /* 184230847Sdima * If getpeername() fails, just let it through (if logging is 184330847Sdima * enabled the condition is caught elsewhere) 184430847Sdima */ 184530847Sdima 184630847Sdima if (sep->se_maxcpm > 0 && 184730847Sdima getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 0 ) { 184830847Sdima time_t t = time(NULL); 184930847Sdima int hv = 0xABC3D20F; 185030847Sdima int i; 185130847Sdima int cnt = 0; 185230847Sdima CHash *chBest = NULL; 185330847Sdima unsigned int ticks = t / CHTGRAN; 185430847Sdima 185530847Sdima { 185630847Sdima char *p; 185730847Sdima int i; 185830847Sdima 185930847Sdima for (i = 0, p = (char *)&rsin.sin_addr; 186030847Sdima i < sizeof(rsin.sin_addr); 186130847Sdima ++i, ++p) { 186230847Sdima hv = (hv << 5) ^ (hv >> 23) ^ *p; 186330847Sdima } 186430847Sdima hv = (hv ^ (hv >> 16)); 186530847Sdima } 186630847Sdima for (i = 0; i < 5; ++i) { 186730847Sdima CHash *ch = &CHashAry[(hv + i) & CPMHMASK]; 186830847Sdima 186930847Sdima if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr && 187030847Sdima ch->ch_Service && strcmp(sep->se_service, 187130847Sdima ch->ch_Service) == 0) { 187230847Sdima chBest = ch; 187330847Sdima break; 187430847Sdima } 187530847Sdima if (chBest == NULL || ch->ch_LTime == 0 || 187630847Sdima ch->ch_LTime < chBest->ch_LTime) { 187730847Sdima chBest = ch; 187830847Sdima } 187930847Sdima } 188030847Sdima if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr || 188130847Sdima chBest->ch_Service == NULL || 188230847Sdima strcmp(sep->se_service, chBest->ch_Service) != 0) { 188330847Sdima chBest->ch_Addr = rsin.sin_addr; 188430847Sdima if (chBest->ch_Service) 188530847Sdima free(chBest->ch_Service); 188630847Sdima chBest->ch_Service = strdup(sep->se_service); 188730847Sdima bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 188830847Sdima } 188930847Sdima chBest->ch_LTime = t; 189030847Sdima { 189130847Sdima CTime *ct = &chBest->ch_Times[ticks % CHTSIZE]; 189230847Sdima if (ct->ct_Ticks != ticks) { 189330847Sdima ct->ct_Ticks = ticks; 189430847Sdima ct->ct_Count = 0; 189530847Sdima } 189630847Sdima ++ct->ct_Count; 189730847Sdima } 189830847Sdima for (i = 0; i < CHTSIZE; ++i) { 189930847Sdima CTime *ct = &chBest->ch_Times[i]; 190030847Sdima if (ct->ct_Ticks <= ticks && 190130847Sdima ct->ct_Ticks >= ticks - CHTSIZE) { 190230847Sdima cnt += ct->ct_Count; 190330847Sdima } 190430847Sdima } 190530847Sdima if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) { 190630847Sdima r = -1; 190730847Sdima syslog(LOG_ERR, 190833794Spst "%s from %s exceeded counts/min (limit %d/min)", 190933794Spst sep->se_service, inet_ntoa(rsin.sin_addr), 191033794Spst sep->se_maxcpm); 191130847Sdima } 191230847Sdima } 191330847Sdima return(r); 191430847Sdima} 1915