inetd.c revision 56482
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1991, 1993, 1994 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * Redistribution and use in source and binary forms, with or without 61553Srgrimes * modification, are permitted provided that the following conditions 71553Srgrimes * are met: 81553Srgrimes * 1. Redistributions of source code must retain the above copyright 91553Srgrimes * notice, this list of conditions and the following disclaimer. 101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111553Srgrimes * notice, this list of conditions and the following disclaimer in the 121553Srgrimes * documentation and/or other materials provided with the distribution. 131553Srgrimes * 3. All advertising materials mentioning features or use of this software 141553Srgrimes * must display the following acknowledgement: 151553Srgrimes * This product includes software developed by the University of 161553Srgrimes * California, Berkeley and its contributors. 171553Srgrimes * 4. Neither the name of the University nor the names of its contributors 181553Srgrimes * may be used to endorse or promote products derived from this software 191553Srgrimes * without specific prior written permission. 201553Srgrimes * 211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311553Srgrimes * SUCH DAMAGE. 321553Srgrimes */ 331553Srgrimes 341553Srgrimes#ifndef lint 3529602Scharnierstatic const char copyright[] = 361553Srgrimes"@(#) Copyright (c) 1983, 1991, 1993, 1994\n\ 371553Srgrimes The Regents of the University of California. All rights reserved.\n"; 381553Srgrimes#endif /* not lint */ 391553Srgrimes 401553Srgrimes#ifndef lint 4129602Scharnier#if 0 4229602Scharnierstatic char sccsid[] = "@(#)from: inetd.c 8.4 (Berkeley) 4/13/94"; 4329602Scharnier#endif 4429602Scharnierstatic const char rcsid[] = 4550479Speter "$FreeBSD: head/usr.sbin/inetd/inetd.c 56482 2000-01-23 20:17:41Z charnier $"; 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/ioctl.h> 1081553Srgrimes#include <sys/wait.h> 1091553Srgrimes#include <sys/time.h> 1101553Srgrimes#include <sys/resource.h> 1111553Srgrimes 1121553Srgrimes#include <netinet/in.h> 11336042Sguido#include <netinet/tcp.h> 1141553Srgrimes#include <arpa/inet.h> 1152657Scsgr#include <rpc/rpc.h> 11619617Sjulian#include <rpc/pmap_clnt.h> 1171553Srgrimes 1181553Srgrimes#include <errno.h> 11929602Scharnier#include <err.h> 1201553Srgrimes#include <fcntl.h> 12130807Sache#include <grp.h> 1221553Srgrimes#include <netdb.h> 1231553Srgrimes#include <pwd.h> 1241553Srgrimes#include <signal.h> 1251553Srgrimes#include <stdio.h> 1261553Srgrimes#include <stdlib.h> 1271553Srgrimes#include <string.h> 1281553Srgrimes#include <syslog.h> 12948279Ssheldonh#include <tcpd.h> 1301553Srgrimes#include <unistd.h> 13113142Speter#include <libutil.h> 13219617Sjulian#include <sysexits.h> 1331553Srgrimes 13448981Ssheldonh#include "inetd.h" 13548981Ssheldonh#include "pathnames.h" 13648981Ssheldonh 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 14945089Smarkm 15048382Ssheldonh#define ISWRAP(sep) \ 15148697Ssheldonh ( ((wrap_ex && !(sep)->se_bi) || (wrap_bi && (sep)->se_bi)) \ 15248382Ssheldonh && ( ((sep)->se_accept && (sep)->se_socktype == SOCK_STREAM) \ 15348382Ssheldonh || (sep)->se_socktype == SOCK_DGRAM)) 15448382Ssheldonh 15521640Speter#ifdef LOGIN_CAP 15621640Speter#include <login_cap.h> 15730792Sache 15830792Sache/* see init.c */ 15930792Sache#define RESOURCE_RC "daemon" 16030792Sache 16121640Speter#endif 16221640Speter 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 18148279Ssheldonhint allow_severity; 18248279Ssheldonhint deny_severity; 18348697Ssheldonhint wrap_ex = 0; 18448279Ssheldonhint wrap_bi = 0; 1851553Srgrimesint debug = 0; 1862659Scsgrint log = 0; 18748988Ssheldonhint maxsock; /* highest-numbered descriptor */ 1881553Srgrimesfd_set allsock; 1891553Srgrimesint options; 1901553Srgrimesint timingout; 1911553Srgrimesint toomany = TOOMANY; 19248069Ssheldonhint maxchild = MAXCHILD; 19348069Ssheldonhint maxcpm = MAXCPM; 1941553Srgrimesstruct servent *sp; 1952657Scsgrstruct rpcent *rpc; 19617482Sjulianstruct in_addr bind_address; 19742122Sdesint signalpipe[2]; 19848991Ssheldonh#ifdef SANITY_CHECK 19948991Ssheldonhint nsock; 20048991Ssheldonh#endif 2011553Srgrimes 20248981Ssheldonhstruct servtab *servtab; 2031553Srgrimes 20448981Ssheldonhextern struct biltin biltins[]; 2051553Srgrimes 2061553Srgrimes#define NUMINT (sizeof(intab) / sizeof(struct inent)) 2071553Srgrimeschar *CONFIG = _PATH_INETDCONF; 20817482Sjulianchar *pid_file = _PATH_INETDPID; 20913142Speter 21013142Speter#ifdef OLD_SETPROCTITLE 2111553Srgrimeschar **Argv; 2121553Srgrimeschar *LastArg; 21313142Speter#endif 2141553Srgrimes 2151553Srgrimesint 21633794Spstgetvalue(arg, value, whine) 21733794Spst char *arg, *whine; 21833794Spst int *value; 21933794Spst{ 22033794Spst int tmp; 22133794Spst char *p; 22233794Spst 22333794Spst tmp = strtol(arg, &p, 0); 22433794Spst if (tmp < 1 || *p) { 22533794Spst syslog(LOG_ERR, whine, arg); 22633794Spst return 1; /* failure */ 22733794Spst } 22833794Spst *value = tmp; 22933794Spst return 0; /* success */ 23033794Spst} 23133794Spst 23233794Spstint 2331553Srgrimesmain(argc, argv, envp) 2341553Srgrimes int argc; 2351553Srgrimes char *argv[], *envp[]; 2361553Srgrimes{ 2371553Srgrimes struct servtab *sep; 2381553Srgrimes struct passwd *pwd; 23930807Sache struct group *grp; 24048962Ssheldonh struct sigaction sa, saalrm, sachld, sahup, sapipe; 2411553Srgrimes int tmpint, ch, dofork; 2421553Srgrimes pid_t pid; 2431553Srgrimes char buf[50]; 24421640Speter#ifdef LOGIN_CAP 24521640Speter login_cap_t *lc = NULL; 24621640Speter#endif 24745089Smarkm struct request_info req; 24845089Smarkm int denied; 24945089Smarkm char *service = NULL; 25048382Ssheldonh char *pnm; 25147972Ssheldonh struct sockaddr_in peer; 25247972Ssheldonh int i; 2531553Srgrimes 25413142Speter 25513142Speter#ifdef OLD_SETPROCTITLE 2561553Srgrimes Argv = argv; 2571553Srgrimes if (envp == 0 || *envp == 0) 2581553Srgrimes envp = argv; 2591553Srgrimes while (*envp) 2601553Srgrimes envp++; 2611553Srgrimes LastArg = envp[-1] + strlen(envp[-1]); 26213142Speter#endif 2631553Srgrimes 2641553Srgrimes openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); 2651553Srgrimes 26617482Sjulian bind_address.s_addr = htonl(INADDR_ANY); 26748697Ssheldonh while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:")) != -1) 2681553Srgrimes switch(ch) { 2691553Srgrimes case 'd': 2701553Srgrimes debug = 1; 2711553Srgrimes options |= SO_DEBUG; 2721553Srgrimes break; 2732659Scsgr case 'l': 2742659Scsgr log = 1; 2752659Scsgr break; 27633794Spst case 'R': 27733794Spst getvalue(optarg, &toomany, 27833794Spst "-R %s: bad value for service invocation rate"); 2791553Srgrimes break; 28033794Spst case 'c': 28133794Spst getvalue(optarg, &maxchild, 28233794Spst "-c %s: bad value for maximum children"); 28333794Spst break; 28433794Spst case 'C': 28533794Spst getvalue(optarg, &maxcpm, 28633794Spst "-C %s: bad value for maximum children/minute"); 28733794Spst break; 28817482Sjulian case 'a': 28917482Sjulian if (!inet_aton(optarg, &bind_address)) { 29017482Sjulian syslog(LOG_ERR, 29117482Sjulian "-a %s: invalid IP address", optarg); 29219617Sjulian exit(EX_USAGE); 29317482Sjulian } 29417482Sjulian break; 29517482Sjulian case 'p': 29617482Sjulian pid_file = optarg; 29717482Sjulian break; 29848279Ssheldonh case 'w': 29948697Ssheldonh wrap_ex++; 30048279Ssheldonh break; 30148697Ssheldonh case 'W': 30248697Ssheldonh wrap_bi++; 30348697Ssheldonh break; 3041553Srgrimes case '?': 3051553Srgrimes default: 3061553Srgrimes syslog(LOG_ERR, 30748697Ssheldonh "usage: inetd [-dlwW] [-a address] [-R rate]" 30833794Spst " [-c maximum] [-C rate]" 30917482Sjulian " [-p pidfile] [conf-file]"); 31019617Sjulian exit(EX_USAGE); 3111553Srgrimes } 3121553Srgrimes argc -= optind; 3131553Srgrimes argv += optind; 3141553Srgrimes 3151553Srgrimes if (argc > 0) 3161553Srgrimes CONFIG = argv[0]; 3171553Srgrimes if (debug == 0) { 31811447Swollman FILE *fp; 31912024Speter if (daemon(0, 0) < 0) { 32012024Speter syslog(LOG_WARNING, "daemon(0,0) failed: %m"); 32112024Speter } 32212024Speter /* 32312024Speter * In case somebody has started inetd manually, we need to 32412024Speter * clear the logname, so that old servers run as root do not 32512024Speter * get the user's logname.. 32612024Speter */ 32712024Speter if (setlogin("") < 0) { 32812024Speter syslog(LOG_WARNING, "cannot clear logname: %m"); 32912024Speter /* no big deal if it fails.. */ 33012024Speter } 33111447Swollman pid = getpid(); 33217482Sjulian fp = fopen(pid_file, "w"); 33311447Swollman if (fp) { 33411447Swollman fprintf(fp, "%ld\n", (long)pid); 33511447Swollman fclose(fp); 33611447Swollman } else { 33717482Sjulian syslog(LOG_WARNING, "%s: %m", pid_file); 33811447Swollman } 3391553Srgrimes } 34035948Sbde sa.sa_flags = 0; 34135948Sbde sigemptyset(&sa.sa_mask); 34235948Sbde sigaddset(&sa.sa_mask, SIGALRM); 34335948Sbde sigaddset(&sa.sa_mask, SIGCHLD); 34435948Sbde sigaddset(&sa.sa_mask, SIGHUP); 34542122Sdes sa.sa_handler = flag_retry; 34648962Ssheldonh sigaction(SIGALRM, &sa, &saalrm); 34742122Sdes config(); 34842122Sdes sa.sa_handler = flag_config; 34948962Ssheldonh sigaction(SIGHUP, &sa, &sahup); 35042122Sdes sa.sa_handler = flag_reapchild; 35148962Ssheldonh sigaction(SIGCHLD, &sa, &sachld); 35235848Sguido sa.sa_handler = SIG_IGN; 35335948Sbde sigaction(SIGPIPE, &sa, &sapipe); 3541553Srgrimes 3551553Srgrimes { 3561553Srgrimes /* space for daemons to overwrite environment for ps */ 3571553Srgrimes#define DUMMYSIZE 100 3581553Srgrimes char dummy[DUMMYSIZE]; 3591553Srgrimes 36019298Salex (void)memset(dummy, 'x', DUMMYSIZE - 1); 3611553Srgrimes dummy[DUMMYSIZE - 1] = '\0'; 3621553Srgrimes (void)setenv("inetd_dummy", dummy, 1); 3631553Srgrimes } 3641553Srgrimes 36542250Sdes if (pipe(signalpipe) != 0) { 36652219Scharnier syslog(LOG_ERR, "pipe: %m"); 36742250Sdes exit(EX_OSERR); 36842122Sdes } 36942122Sdes FD_SET(signalpipe[0], &allsock); 37048991Ssheldonh#ifdef SANITY_CHECK 37147015Sdes nsock++; 37248991Ssheldonh#endif 37348989Ssheldonh if (signalpipe[0] > maxsock) 37448989Ssheldonh maxsock = signalpipe[0]; 37548989Ssheldonh if (signalpipe[1] > maxsock) 37648989Ssheldonh maxsock = signalpipe[1]; 37741685Sdillon 3781553Srgrimes for (;;) { 3791553Srgrimes int n, ctrl; 3801553Srgrimes fd_set readable; 3811553Srgrimes 38248991Ssheldonh#ifdef SANITY_CHECK 3831553Srgrimes if (nsock == 0) { 38447015Sdes syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 38547015Sdes exit(EX_SOFTWARE); 3861553Srgrimes } 38748991Ssheldonh#endif 3881553Srgrimes readable = allsock; 38942122Sdes if ((n = select(maxsock + 1, &readable, (fd_set *)0, 39042122Sdes (fd_set *)0, (struct timeval *)0)) <= 0) { 39142122Sdes if (n < 0 && errno != EINTR) { 3921553Srgrimes syslog(LOG_WARNING, "select: %m"); 39328907Simp sleep(1); 39428907Simp } 3951553Srgrimes continue; 3961553Srgrimes } 39742122Sdes /* handle any queued signal flags */ 39842250Sdes if (FD_ISSET(signalpipe[0], &readable)) { 39942122Sdes int n; 40042250Sdes if (ioctl(signalpipe[0], FIONREAD, &n) != 0) { 40142122Sdes syslog(LOG_ERR, "ioctl: %m"); 40242122Sdes exit(EX_OSERR); 40342122Sdes } 40442250Sdes while (--n >= 0) { 40542250Sdes char c; 40642250Sdes if (read(signalpipe[0], &c, 1) != 1) { 40742250Sdes syslog(LOG_ERR, "read: %m"); 40842250Sdes exit(EX_OSERR); 40942250Sdes } 41042250Sdes if (debug) 41156482Scharnier warnx("handling signal flag %c", c); 41242250Sdes switch(c) { 41342250Sdes case 'A': /* sigalrm */ 41442250Sdes retry(); 41542250Sdes break; 41642250Sdes case 'C': /* sigchld */ 41742250Sdes reapchild(); 41842250Sdes break; 41942250Sdes case 'H': /* sighup */ 42042250Sdes config(); 42142250Sdes break; 42242250Sdes } 42342250Sdes } 42442122Sdes } 4251553Srgrimes for (sep = servtab; n && sep; sep = sep->se_next) 4261553Srgrimes if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { 4271553Srgrimes n--; 4281553Srgrimes if (debug) 42929602Scharnier warnx("someone wants %s", sep->se_service); 43019618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) { 43153256Speter i = 1; 43253256Speter if (ioctl(sep->se_fd, FIONBIO, &i) < 0) 43353256Speter syslog(LOG_ERR, "ioctl (FIONBIO, 1): %m"); 4341553Srgrimes ctrl = accept(sep->se_fd, (struct sockaddr *)0, 4351553Srgrimes (int *)0); 4361553Srgrimes if (debug) 43729602Scharnier warnx("accept, ctrl %d", ctrl); 4381553Srgrimes if (ctrl < 0) { 4391553Srgrimes if (errno != EINTR) 4401553Srgrimes syslog(LOG_WARNING, 4411553Srgrimes "accept (for %s): %m", 44237844Sphk sep->se_service); 44337816Sphk if (sep->se_accept && 44437816Sphk sep->se_socktype == SOCK_STREAM) 44537816Sphk close(ctrl); 4461553Srgrimes continue; 4471553Srgrimes } 44853256Speter i = 0; 44953256Speter if (ioctl(sep->se_fd, FIONBIO, &i) < 0) 45053256Speter syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m"); 45153256Speter if (ioctl(ctrl, FIONBIO, &i) < 0) 45253256Speter syslog(LOG_ERR, "ioctl2(FIONBIO, 0): %m"); 45330847Sdima if (cpmip(sep, ctrl) < 0) { 45430847Sdima close(ctrl); 45530847Sdima continue; 45630847Sdima } 4571553Srgrimes } else 4581553Srgrimes ctrl = sep->se_fd; 45948382Ssheldonh if (log && !ISWRAP(sep)) { 46048382Ssheldonh pnm = "unknown"; 46148382Ssheldonh i = sizeof peer; 46248382Ssheldonh if (getpeername(ctrl, (struct sockaddr *) 46348382Ssheldonh &peer, &i)) { 46448382Ssheldonh i = sizeof peer; 46548382Ssheldonh if (recvfrom(ctrl, buf, sizeof(buf), 46648382Ssheldonh MSG_PEEK, 46748382Ssheldonh (struct sockaddr *)&peer, &i) >= 0) 46848382Ssheldonh pnm = inet_ntoa(peer.sin_addr); 46948382Ssheldonh } 47048382Ssheldonh else 47148382Ssheldonh pnm = inet_ntoa(peer.sin_addr); 47248382Ssheldonh syslog(LOG_INFO,"%s from %s", sep->se_service, pnm); 47348382Ssheldonh } 47442122Sdes (void) sigblock(SIGBLOCK); 4751553Srgrimes pid = 0; 47647972Ssheldonh /* 47748958Ssheldonh * Fork for all external services, builtins which need to 47848958Ssheldonh * fork and anything we're wrapping (as wrapping might 47948958Ssheldonh * block or use hosts_options(5) twist). 48047972Ssheldonh */ 48148382Ssheldonh dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep); 4821553Srgrimes if (dofork) { 4831553Srgrimes if (sep->se_count++ == 0) 48437856Sache (void)gettimeofday(&sep->se_time, (struct timezone *)NULL); 4851553Srgrimes else if (sep->se_count >= toomany) { 4861553Srgrimes struct timeval now; 4871553Srgrimes 48837856Sache (void)gettimeofday(&now, (struct timezone *)NULL); 4891553Srgrimes if (now.tv_sec - sep->se_time.tv_sec > 4901553Srgrimes CNT_INTVL) { 4911553Srgrimes sep->se_time = now; 4921553Srgrimes sep->se_count = 1; 4931553Srgrimes } else { 4941553Srgrimes syslog(LOG_ERR, 4951553Srgrimes "%s/%s server failing (looping), service terminated", 4961553Srgrimes sep->se_service, sep->se_proto); 4971553Srgrimes close_sep(sep); 49842122Sdes sigsetmask(0L); 4991553Srgrimes if (!timingout) { 5001553Srgrimes timingout = 1; 5011553Srgrimes alarm(RETRYTIME); 5021553Srgrimes } 5031553Srgrimes continue; 5041553Srgrimes } 5051553Srgrimes } 5061553Srgrimes pid = fork(); 5071553Srgrimes } 5081553Srgrimes if (pid < 0) { 5091553Srgrimes syslog(LOG_ERR, "fork: %m"); 51019618Sjulian if (sep->se_accept && 5111553Srgrimes sep->se_socktype == SOCK_STREAM) 5121553Srgrimes close(ctrl); 51342122Sdes sigsetmask(0L); 5141553Srgrimes sleep(1); 5151553Srgrimes continue; 5161553Srgrimes } 51719618Sjulian if (pid) 51819618Sjulian addchild(sep, pid); 51942122Sdes sigsetmask(0L); 5201553Srgrimes if (pid == 0) { 5211553Srgrimes if (dofork) { 5221553Srgrimes if (debug) 52329602Scharnier warnx("+ closing from %d", maxsock); 5241553Srgrimes for (tmpint = maxsock; tmpint > 2; tmpint--) 5251553Srgrimes if (tmpint != ctrl) 52619617Sjulian (void) close(tmpint); 52748962Ssheldonh sigaction(SIGALRM, &saalrm, (struct sigaction *)0); 52848962Ssheldonh sigaction(SIGCHLD, &sachld, (struct sigaction *)0); 52948962Ssheldonh sigaction(SIGHUP, &sahup, (struct sigaction *)0); 53048962Ssheldonh /* SIGPIPE reset before exec */ 5311553Srgrimes } 53235829Sguido /* 53335829Sguido * Call tcpmux to find the real service to exec. 53435829Sguido */ 53535829Sguido if (sep->se_bi && 53635829Sguido sep->se_bi->bi_fn == (void (*)()) tcpmux) { 53735829Sguido sep = tcpmux(ctrl); 53835829Sguido if (sep == NULL) { 53935829Sguido close(ctrl); 54035829Sguido _exit(0); 54135829Sguido } 54235829Sguido } 54348382Ssheldonh if (ISWRAP(sep)) { 54448698Ssheldonh inetd_setproctitle("wrapping", ctrl); 54547972Ssheldonh service = sep->se_server_name ? 54647972Ssheldonh sep->se_server_name : sep->se_service; 54747972Ssheldonh request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, NULL); 54845089Smarkm fromhost(&req); 54947972Ssheldonh deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY; 55047972Ssheldonh allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY; 55145089Smarkm denied = !hosts_access(&req); 55245089Smarkm if (denied) { 55345089Smarkm syslog(deny_severity, 55445089Smarkm "refused connection from %.500s, service %s (%s)", 55545089Smarkm eval_client(&req), service, sep->se_proto); 55648382Ssheldonh if (sep->se_socktype != SOCK_STREAM) 55748382Ssheldonh recv(ctrl, buf, sizeof (buf), 0); 55848382Ssheldonh if (dofork) 55948382Ssheldonh _exit(0); 56045089Smarkm } 56145089Smarkm if (log) { 56245089Smarkm syslog(allow_severity, 56345089Smarkm "connection from %.500s, service %s (%s)", 56445089Smarkm eval_client(&req), service, sep->se_proto); 56545089Smarkm } 56645089Smarkm } 56719617Sjulian if (sep->se_bi) { 5681553Srgrimes (*sep->se_bi->bi_fn)(ctrl, sep); 56919617Sjulian } else { 5701553Srgrimes if (debug) 57129602Scharnier warnx("%d execl %s", 57229602Scharnier getpid(), sep->se_server); 5731553Srgrimes dup2(ctrl, 0); 5741553Srgrimes close(ctrl); 5751553Srgrimes dup2(0, 1); 5761553Srgrimes dup2(0, 2); 5771553Srgrimes if ((pwd = getpwnam(sep->se_user)) == NULL) { 5781553Srgrimes syslog(LOG_ERR, 57956482Scharnier "%s/%s: %s: no such user", 5801553Srgrimes sep->se_service, sep->se_proto, 5811553Srgrimes sep->se_user); 5821553Srgrimes if (sep->se_socktype != SOCK_STREAM) 5831553Srgrimes recv(0, buf, sizeof (buf), 0); 58419617Sjulian _exit(EX_NOUSER); 5851553Srgrimes } 58630807Sache grp = NULL; 58730807Sache if ( sep->se_group != NULL 58830807Sache && (grp = getgrnam(sep->se_group)) == NULL 58930807Sache ) { 59030807Sache syslog(LOG_ERR, 59156482Scharnier "%s/%s: %s: no such group", 59230807Sache sep->se_service, sep->se_proto, 59330807Sache sep->se_group); 59430807Sache if (sep->se_socktype != SOCK_STREAM) 59530807Sache recv(0, buf, sizeof (buf), 0); 59630807Sache _exit(EX_NOUSER); 59730807Sache } 59830807Sache if (grp != NULL) 59930807Sache pwd->pw_gid = grp->gr_gid; 60021640Speter#ifdef LOGIN_CAP 60130792Sache if ((lc = login_getclass(sep->se_class)) == NULL) { 60230792Sache /* error syslogged by getclass */ 60330792Sache syslog(LOG_ERR, 60430792Sache "%s/%s: %s: login class error", 60537850Sache sep->se_service, sep->se_proto, 60637850Sache sep->se_class); 60730792Sache if (sep->se_socktype != SOCK_STREAM) 60830792Sache recv(0, buf, sizeof (buf), 0); 60930792Sache _exit(EX_NOUSER); 61030792Sache } 61121640Speter#endif 61212024Speter if (setsid() < 0) { 61312024Speter syslog(LOG_ERR, 61412024Speter "%s: can't setsid(): %m", 61512024Speter sep->se_service); 61619617Sjulian /* _exit(EX_OSERR); not fatal yet */ 61712024Speter } 61821640Speter#ifdef LOGIN_CAP 61921640Speter if (setusercontext(lc, pwd, pwd->pw_uid, 62021640Speter LOGIN_SETALL) != 0) { 62121640Speter syslog(LOG_ERR, 62221640Speter "%s: can't setusercontext(..%s..): %m", 62321640Speter sep->se_service, sep->se_user); 62421640Speter _exit(EX_OSERR); 62521640Speter } 62621640Speter#else 6271553Srgrimes if (pwd->pw_uid) { 62812024Speter if (setlogin(sep->se_user) < 0) { 62912024Speter syslog(LOG_ERR, 63012024Speter "%s: can't setlogin(%s): %m", 63112024Speter sep->se_service, sep->se_user); 63219617Sjulian /* _exit(EX_OSERR); not yet */ 63312024Speter } 6341553Srgrimes if (setgid(pwd->pw_gid) < 0) { 6351553Srgrimes syslog(LOG_ERR, 6368857Srgrimes "%s: can't set gid %d: %m", 6371553Srgrimes sep->se_service, pwd->pw_gid); 63819617Sjulian _exit(EX_OSERR); 6391553Srgrimes } 6401553Srgrimes (void) initgroups(pwd->pw_name, 6411553Srgrimes pwd->pw_gid); 6421553Srgrimes if (setuid(pwd->pw_uid) < 0) { 6431553Srgrimes syslog(LOG_ERR, 6448857Srgrimes "%s: can't set uid %d: %m", 6451553Srgrimes sep->se_service, pwd->pw_uid); 64619617Sjulian _exit(EX_OSERR); 6471553Srgrimes } 6481553Srgrimes } 64921640Speter#endif 65035948Sbde sigaction(SIGPIPE, &sapipe, 65135948Sbde (struct sigaction *)0); 6521553Srgrimes execv(sep->se_server, sep->se_argv); 65345089Smarkm syslog(LOG_ERR, 65445089Smarkm "cannot execute %s: %m", sep->se_server); 6551553Srgrimes if (sep->se_socktype != SOCK_STREAM) 6561553Srgrimes recv(0, buf, sizeof (buf), 0); 6571553Srgrimes } 65847972Ssheldonh if (dofork) 65947972Ssheldonh _exit(0); 6601553Srgrimes } 66119618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) 6621553Srgrimes close(ctrl); 6631553Srgrimes } 6641553Srgrimes } 6651553Srgrimes} 6661553Srgrimes 66719618Sjulian/* 66842122Sdes * Add a signal flag to the signal flag queue for later handling 66942122Sdes */ 67042122Sdes 67142122Sdesvoid flag_signal(c) 67242122Sdes char c; 67342122Sdes{ 67442250Sdes if (write(signalpipe[1], &c, 1) != 1) { 67542250Sdes syslog(LOG_ERR, "write: %m"); 67648985Ssheldonh _exit(EX_OSERR); 67742250Sdes } 67842122Sdes} 67942122Sdes 68042122Sdes/* 68119618Sjulian * Record a new child pid for this service. If we've reached the 68219618Sjulian * limit on children, then stop accepting incoming requests. 68319618Sjulian */ 68419618Sjulian 6851553Srgrimesvoid 68619618Sjulianaddchild(struct servtab *sep, pid_t pid) 68719618Sjulian{ 68819618Sjulian#ifdef SANITY_CHECK 68919618Sjulian if (sep->se_numchild >= sep->se_maxchild) { 69019618Sjulian syslog(LOG_ERR, "%s: %d >= %d", 69119618Sjulian __FUNCTION__, sep->se_numchild, sep->se_maxchild); 69219618Sjulian exit(EX_SOFTWARE); 69319618Sjulian } 69419618Sjulian#endif 69519618Sjulian if (sep->se_maxchild == 0) 69619618Sjulian return; 69719618Sjulian sep->se_pids[sep->se_numchild++] = pid; 69819618Sjulian if (sep->se_numchild == sep->se_maxchild) 69919618Sjulian disable(sep); 70019618Sjulian} 70119618Sjulian 70219618Sjulian/* 70319618Sjulian * Some child process has exited. See if it's on somebody's list. 70419618Sjulian */ 70519618Sjulian 70619618Sjulianvoid 70742122Sdesflag_reapchild(signo) 7081553Srgrimes int signo; 7091553Srgrimes{ 71042250Sdes flag_signal('C'); 71142122Sdes} 71242122Sdes 71342122Sdesvoid 71442122Sdesreapchild() 71542122Sdes{ 71619618Sjulian int k, status; 7171553Srgrimes pid_t pid; 7181553Srgrimes struct servtab *sep; 7191553Srgrimes 7201553Srgrimes for (;;) { 7211553Srgrimes pid = wait3(&status, WNOHANG, (struct rusage *)0); 7221553Srgrimes if (pid <= 0) 7231553Srgrimes break; 7241553Srgrimes if (debug) 72529602Scharnier warnx("%d reaped, status %#x", pid, status); 72619618Sjulian for (sep = servtab; sep; sep = sep->se_next) { 72719618Sjulian for (k = 0; k < sep->se_numchild; k++) 72819618Sjulian if (sep->se_pids[k] == pid) 72919618Sjulian break; 73019618Sjulian if (k == sep->se_numchild) 73119618Sjulian continue; 73219618Sjulian if (sep->se_numchild == sep->se_maxchild) 73319618Sjulian enable(sep); 73419618Sjulian sep->se_pids[k] = sep->se_pids[--sep->se_numchild]; 73519618Sjulian if (status) 73619618Sjulian syslog(LOG_WARNING, 73719618Sjulian "%s[%d]: exit status 0x%x", 73819618Sjulian sep->se_server, pid, status); 73919618Sjulian break; 74019618Sjulian } 7411553Srgrimes } 7421553Srgrimes} 7431553Srgrimes 7441553Srgrimesvoid 74542122Sdesflag_config(signo) 7461553Srgrimes int signo; 7471553Srgrimes{ 74842250Sdes flag_signal('H'); 74942122Sdes} 75042122Sdes 75142122Sdesvoid config() 75242122Sdes{ 75319618Sjulian struct servtab *sep, *new, **sepp; 75442122Sdes long omask; 7551553Srgrimes 7561553Srgrimes if (!setconfig()) { 7571553Srgrimes syslog(LOG_ERR, "%s: %m", CONFIG); 7581553Srgrimes return; 7591553Srgrimes } 7601553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 7611553Srgrimes sep->se_checked = 0; 76219618Sjulian while ((new = getconfigent())) { 76330807Sache if (getpwnam(new->se_user) == NULL) { 7641553Srgrimes syslog(LOG_ERR, 76556482Scharnier "%s/%s: no such user '%s', service ignored", 76619618Sjulian new->se_service, new->se_proto, new->se_user); 7671553Srgrimes continue; 7681553Srgrimes } 76930807Sache if (new->se_group && getgrnam(new->se_group) == NULL) { 77030807Sache syslog(LOG_ERR, 77156482Scharnier "%s/%s: no such group '%s', service ignored", 77230807Sache new->se_service, new->se_proto, new->se_group); 77330807Sache continue; 77430807Sache } 77530792Sache#ifdef LOGIN_CAP 77630792Sache if (login_getclass(new->se_class) == NULL) { 77730792Sache /* error syslogged by getclass */ 77830792Sache syslog(LOG_ERR, 77937850Sache "%s/%s: %s: login class error, service ignored", 78037850Sache new->se_service, new->se_proto, new->se_class); 78130792Sache continue; 78230792Sache } 78330792Sache#endif 7841553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 78519618Sjulian if (strcmp(sep->se_service, new->se_service) == 0 && 78619618Sjulian strcmp(sep->se_proto, new->se_proto) == 0) 7871553Srgrimes break; 7881553Srgrimes if (sep != 0) { 7891553Srgrimes int i; 7901553Srgrimes 79119618Sjulian#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; } 79242122Sdes omask = sigblock(SIGBLOCK); 79319618Sjulian /* copy over outstanding child pids */ 79419618Sjulian if (sep->se_maxchild && new->se_maxchild) { 79519618Sjulian new->se_numchild = sep->se_numchild; 79619618Sjulian if (new->se_numchild > new->se_maxchild) 79719618Sjulian new->se_numchild = new->se_maxchild; 79819618Sjulian memcpy(new->se_pids, sep->se_pids, 79919618Sjulian new->se_numchild * sizeof(*new->se_pids)); 80019618Sjulian } 80119618Sjulian SWAP(sep->se_pids, new->se_pids); 80219618Sjulian sep->se_maxchild = new->se_maxchild; 80319618Sjulian sep->se_numchild = new->se_numchild; 80430847Sdima sep->se_maxcpm = new->se_maxcpm; 80519618Sjulian /* might need to turn on or off service now */ 80619618Sjulian if (sep->se_fd >= 0) { 80719618Sjulian if (sep->se_maxchild 80819618Sjulian && sep->se_numchild == sep->se_maxchild) { 80919618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 81019618Sjulian disable(sep); 81119618Sjulian } else { 81219618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) 81319618Sjulian enable(sep); 81419618Sjulian } 81519618Sjulian } 81619618Sjulian sep->se_accept = new->se_accept; 81730807Sache SWAP(sep->se_user, new->se_user); 81830807Sache SWAP(sep->se_group, new->se_group); 81930792Sache#ifdef LOGIN_CAP 82030807Sache SWAP(sep->se_class, new->se_class); 82130792Sache#endif 82230807Sache SWAP(sep->se_server, new->se_server); 82347972Ssheldonh SWAP(sep->se_server_name, new->se_server_name); 8241553Srgrimes for (i = 0; i < MAXARGV; i++) 82519618Sjulian SWAP(sep->se_argv[i], new->se_argv[i]); 82642122Sdes sigsetmask(omask); 82719618Sjulian freeconfig(new); 8281553Srgrimes if (debug) 8291553Srgrimes print_service("REDO", sep); 8301553Srgrimes } else { 83119618Sjulian sep = enter(new); 8321553Srgrimes if (debug) 8331553Srgrimes print_service("ADD ", sep); 8341553Srgrimes } 8351553Srgrimes sep->se_checked = 1; 8361553Srgrimes if (ISMUX(sep)) { 8371553Srgrimes sep->se_fd = -1; 8381553Srgrimes continue; 8391553Srgrimes } 8402657Scsgr if (!sep->se_rpc) { 8412657Scsgr sp = getservbyname(sep->se_service, sep->se_proto); 8422657Scsgr if (sp == 0) { 8432657Scsgr syslog(LOG_ERR, "%s/%s: unknown service", 8442657Scsgr sep->se_service, sep->se_proto); 8452657Scsgr sep->se_checked = 0; 8462657Scsgr continue; 8472657Scsgr } 8482657Scsgr if (sp->s_port != sep->se_ctrladdr.sin_port) { 8492657Scsgr sep->se_ctrladdr.sin_family = AF_INET; 85022306Sjulian sep->se_ctrladdr.sin_addr = bind_address; 8512657Scsgr sep->se_ctrladdr.sin_port = sp->s_port; 8522657Scsgr if (sep->se_fd >= 0) 8532657Scsgr close_sep(sep); 8542657Scsgr } 8552657Scsgr } else { 8562657Scsgr rpc = getrpcbyname(sep->se_service); 8572657Scsgr if (rpc == 0) { 85852219Scharnier syslog(LOG_ERR, "%s/%s unknown RPC service", 8592657Scsgr sep->se_service, sep->se_proto); 8602657Scsgr if (sep->se_fd != -1) 8612657Scsgr (void) close(sep->se_fd); 8622657Scsgr sep->se_fd = -1; 8632657Scsgr continue; 8642657Scsgr } 8652657Scsgr if (rpc->r_number != sep->se_rpc_prog) { 8662657Scsgr if (sep->se_rpc_prog) 8672657Scsgr unregisterrpc(sep); 8682657Scsgr sep->se_rpc_prog = rpc->r_number; 8692657Scsgr if (sep->se_fd != -1) 8702657Scsgr (void) close(sep->se_fd); 8712657Scsgr sep->se_fd = -1; 8722657Scsgr } 8731553Srgrimes } 8741553Srgrimes if (sep->se_fd == -1) 8751553Srgrimes setup(sep); 8761553Srgrimes } 8771553Srgrimes endconfig(); 8781553Srgrimes /* 8791553Srgrimes * Purge anything not looked at above. 8801553Srgrimes */ 88142122Sdes omask = sigblock(SIGBLOCK); 8821553Srgrimes sepp = &servtab; 88319617Sjulian while ((sep = *sepp)) { 8841553Srgrimes if (sep->se_checked) { 8851553Srgrimes sepp = &sep->se_next; 8861553Srgrimes continue; 8871553Srgrimes } 8881553Srgrimes *sepp = sep->se_next; 8891553Srgrimes if (sep->se_fd >= 0) 8901553Srgrimes close_sep(sep); 8911553Srgrimes if (debug) 8921553Srgrimes print_service("FREE", sep); 8932657Scsgr if (sep->se_rpc && sep->se_rpc_prog > 0) 8942657Scsgr unregisterrpc(sep); 8951553Srgrimes freeconfig(sep); 8961553Srgrimes free((char *)sep); 8971553Srgrimes } 89842122Sdes (void) sigsetmask(omask); 8991553Srgrimes} 9001553Srgrimes 9011553Srgrimesvoid 9022657Scsgrunregisterrpc(sep) 9032657Scsgr struct servtab *sep; 9042657Scsgr{ 9052657Scsgr int i; 9062657Scsgr struct servtab *sepp; 90742122Sdes long omask; 9082657Scsgr 90942122Sdes omask = sigblock(SIGBLOCK); 9102657Scsgr for (sepp = servtab; sepp; sepp = sepp->se_next) { 9112657Scsgr if (sepp == sep) 9122657Scsgr continue; 9132657Scsgr if (sep->se_checked == 0 || 9142657Scsgr !sepp->se_rpc || 9152657Scsgr sep->se_rpc_prog != sepp->se_rpc_prog) 9162657Scsgr continue; 9172657Scsgr return; 9182657Scsgr } 9192657Scsgr if (debug) 9202657Scsgr print_service("UNREG", sep); 9212657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) 9222657Scsgr pmap_unset(sep->se_rpc_prog, i); 9232657Scsgr if (sep->se_fd != -1) 9242657Scsgr (void) close(sep->se_fd); 9252657Scsgr sep->se_fd = -1; 92642122Sdes (void) sigsetmask(omask); 9272657Scsgr} 9282657Scsgr 9292657Scsgrvoid 93042122Sdesflag_retry(signo) 9311553Srgrimes int signo; 9321553Srgrimes{ 93342250Sdes flag_signal('A'); 93442122Sdes} 93542122Sdes 93642122Sdesvoid 93742122Sdesretry() 93842122Sdes{ 9391553Srgrimes struct servtab *sep; 9401553Srgrimes 9411553Srgrimes timingout = 0; 9421553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 94319617Sjulian if (sep->se_fd == -1 && !ISMUX(sep)) 9441553Srgrimes setup(sep); 9451553Srgrimes} 9461553Srgrimes 9471553Srgrimesvoid 9481553Srgrimessetup(sep) 9491553Srgrimes struct servtab *sep; 9501553Srgrimes{ 9511553Srgrimes int on = 1; 9521553Srgrimes 9531553Srgrimes if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) { 9541553Srgrimes if (debug) 95529602Scharnier warn("socket failed on %s/%s", 95629602Scharnier sep->se_service, sep->se_proto); 9571553Srgrimes syslog(LOG_ERR, "%s/%s: socket: %m", 9581553Srgrimes sep->se_service, sep->se_proto); 9591553Srgrimes return; 9601553Srgrimes } 9611553Srgrimes#define turnon(fd, opt) \ 9621553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) 9631553Srgrimes if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && 9641553Srgrimes turnon(sep->se_fd, SO_DEBUG) < 0) 9651553Srgrimes syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 9661553Srgrimes if (turnon(sep->se_fd, SO_REUSEADDR) < 0) 9671553Srgrimes syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); 96825253Swollman#ifdef SO_PRIVSTATE 96913956Swollman if (turnon(sep->se_fd, SO_PRIVSTATE) < 0) 97013956Swollman syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m"); 97125253Swollman#endif 9721553Srgrimes#undef turnon 97336042Sguido if (sep->se_type == TTCP_TYPE) 97436042Sguido if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH, 97536042Sguido (char *)&on, sizeof (on)) < 0) 97636042Sguido syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m"); 9771553Srgrimes if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, 9781553Srgrimes sizeof (sep->se_ctrladdr)) < 0) { 9791553Srgrimes if (debug) 98029602Scharnier warn("bind failed on %s/%s", 98129602Scharnier sep->se_service, sep->se_proto); 9821553Srgrimes syslog(LOG_ERR, "%s/%s: bind: %m", 9831553Srgrimes sep->se_service, sep->se_proto); 9841553Srgrimes (void) close(sep->se_fd); 9851553Srgrimes sep->se_fd = -1; 9861553Srgrimes if (!timingout) { 9871553Srgrimes timingout = 1; 9881553Srgrimes alarm(RETRYTIME); 9891553Srgrimes } 9901553Srgrimes return; 9911553Srgrimes } 9922657Scsgr if (sep->se_rpc) { 9932657Scsgr int i, len = sizeof(struct sockaddr); 9942657Scsgr 9958857Srgrimes if (getsockname(sep->se_fd, 9962657Scsgr (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){ 9972657Scsgr syslog(LOG_ERR, "%s/%s: getsockname: %m", 9982657Scsgr sep->se_service, sep->se_proto); 9992657Scsgr (void) close(sep->se_fd); 10002657Scsgr sep->se_fd = -1; 10018857Srgrimes return; 10022657Scsgr } 10032657Scsgr if (debug) 10042657Scsgr print_service("REG ", sep); 10052657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { 10062657Scsgr pmap_unset(sep->se_rpc_prog, i); 10072657Scsgr pmap_set(sep->se_rpc_prog, i, 10082657Scsgr (sep->se_socktype == SOCK_DGRAM) 10092657Scsgr ? IPPROTO_UDP : IPPROTO_TCP, 10102657Scsgr ntohs(sep->se_ctrladdr.sin_port)); 10112657Scsgr } 10128857Srgrimes 10132657Scsgr } 10141553Srgrimes if (sep->se_socktype == SOCK_STREAM) 101517197Sdg listen(sep->se_fd, 64); 101619618Sjulian enable(sep); 10171553Srgrimes if (debug) { 101829602Scharnier warnx("registered %s on %d", 10191553Srgrimes sep->se_server, sep->se_fd); 10201553Srgrimes } 10211553Srgrimes} 10221553Srgrimes 10231553Srgrimes/* 10241553Srgrimes * Finish with a service and its socket. 10251553Srgrimes */ 10261553Srgrimesvoid 10271553Srgrimesclose_sep(sep) 10281553Srgrimes struct servtab *sep; 10291553Srgrimes{ 10301553Srgrimes if (sep->se_fd >= 0) { 103119618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 103219618Sjulian disable(sep); 10331553Srgrimes (void) close(sep->se_fd); 10341553Srgrimes sep->se_fd = -1; 10351553Srgrimes } 10361553Srgrimes sep->se_count = 0; 103719618Sjulian sep->se_numchild = 0; /* forget about any existing children */ 10381553Srgrimes} 10391553Srgrimes 104048467Ssheldonhint 104148467Ssheldonhmatchservent(name1, name2, proto) 104248467Ssheldonh char *name1, *name2, *proto; 104348467Ssheldonh{ 104448467Ssheldonh char **alias; 104548467Ssheldonh struct servent *se; 104648467Ssheldonh 104749026Sdes if (strcmp(name1, name2) == 0) 104849026Sdes return(1); 104948467Ssheldonh if ((se = getservbyname(name1, proto)) != NULL) { 105048467Ssheldonh if (strcmp(name2, se->s_name) == 0) 105148467Ssheldonh return(1); 105248467Ssheldonh for (alias = se->s_aliases; *alias; alias++) 105348467Ssheldonh if (strcmp(name2, *alias) == 0) 105448467Ssheldonh return(1); 105548467Ssheldonh } 105648467Ssheldonh return(0); 105748467Ssheldonh} 105848467Ssheldonh 10591553Srgrimesstruct servtab * 10601553Srgrimesenter(cp) 10611553Srgrimes struct servtab *cp; 10621553Srgrimes{ 10631553Srgrimes struct servtab *sep; 106442122Sdes long omask; 10651553Srgrimes 10661553Srgrimes sep = (struct servtab *)malloc(sizeof (*sep)); 10671553Srgrimes if (sep == (struct servtab *)0) { 106849102Ssheldonh syslog(LOG_ERR, "malloc: %m"); 106919617Sjulian exit(EX_OSERR); 10701553Srgrimes } 10711553Srgrimes *sep = *cp; 10721553Srgrimes sep->se_fd = -1; 107342122Sdes omask = sigblock(SIGBLOCK); 10741553Srgrimes sep->se_next = servtab; 10751553Srgrimes servtab = sep; 107642122Sdes sigsetmask(omask); 10771553Srgrimes return (sep); 10781553Srgrimes} 10791553Srgrimes 108019618Sjulianvoid 108119618Sjulianenable(struct servtab *sep) 108219618Sjulian{ 108319618Sjulian if (debug) 108429602Scharnier warnx( 108519618Sjulian "enabling %s, fd %d", sep->se_service, sep->se_fd); 108619618Sjulian#ifdef SANITY_CHECK 108719618Sjulian if (sep->se_fd < 0) { 108819618Sjulian syslog(LOG_ERR, 108919618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 109019618Sjulian exit(EX_SOFTWARE); 109119618Sjulian } 109219618Sjulian if (ISMUX(sep)) { 109319618Sjulian syslog(LOG_ERR, 109419618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 109519618Sjulian exit(EX_SOFTWARE); 109619618Sjulian } 109719618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) { 109819618Sjulian syslog(LOG_ERR, 109919618Sjulian "%s: %s: not off", __FUNCTION__, sep->se_service); 110019618Sjulian exit(EX_SOFTWARE); 110119618Sjulian } 110248991Ssheldonh nsock++; 110319618Sjulian#endif 110419618Sjulian FD_SET(sep->se_fd, &allsock); 110519618Sjulian if (sep->se_fd > maxsock) 110619618Sjulian maxsock = sep->se_fd; 110719618Sjulian} 110819618Sjulian 110919618Sjulianvoid 111019618Sjuliandisable(struct servtab *sep) 111119618Sjulian{ 111219618Sjulian if (debug) 111329602Scharnier warnx( 111419618Sjulian "disabling %s, fd %d", sep->se_service, sep->se_fd); 111519618Sjulian#ifdef SANITY_CHECK 111619618Sjulian if (sep->se_fd < 0) { 111719618Sjulian syslog(LOG_ERR, 111819618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 111919618Sjulian exit(EX_SOFTWARE); 112019618Sjulian } 112119618Sjulian if (ISMUX(sep)) { 112219618Sjulian syslog(LOG_ERR, 112319618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 112419618Sjulian exit(EX_SOFTWARE); 112519618Sjulian } 112619618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) { 112719618Sjulian syslog(LOG_ERR, 112819618Sjulian "%s: %s: not on", __FUNCTION__, sep->se_service); 112919618Sjulian exit(EX_SOFTWARE); 113019618Sjulian } 113119618Sjulian if (nsock == 0) { 113219618Sjulian syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 113319618Sjulian exit(EX_SOFTWARE); 113419618Sjulian } 113548991Ssheldonh nsock--; 113619618Sjulian#endif 113719618Sjulian FD_CLR(sep->se_fd, &allsock); 113819618Sjulian if (sep->se_fd == maxsock) 113919618Sjulian maxsock--; 114019618Sjulian} 114119618Sjulian 11421553SrgrimesFILE *fconfig = NULL; 11431553Srgrimesstruct servtab serv; 11441553Srgrimeschar line[LINE_MAX]; 11451553Srgrimes 11461553Srgrimesint 11471553Srgrimessetconfig() 11481553Srgrimes{ 11491553Srgrimes 11501553Srgrimes if (fconfig != NULL) { 11511553Srgrimes fseek(fconfig, 0L, SEEK_SET); 11521553Srgrimes return (1); 11531553Srgrimes } 11541553Srgrimes fconfig = fopen(CONFIG, "r"); 11551553Srgrimes return (fconfig != NULL); 11561553Srgrimes} 11571553Srgrimes 11581553Srgrimesvoid 11591553Srgrimesendconfig() 11601553Srgrimes{ 11611553Srgrimes if (fconfig) { 11621553Srgrimes (void) fclose(fconfig); 11631553Srgrimes fconfig = NULL; 11641553Srgrimes } 11651553Srgrimes} 11661553Srgrimes 11671553Srgrimesstruct servtab * 11681553Srgrimesgetconfigent() 11691553Srgrimes{ 11701553Srgrimes struct servtab *sep = &serv; 11711553Srgrimes int argc; 117219618Sjulian char *cp, *arg, *s; 11732657Scsgr char *versp; 11741553Srgrimes static char TCPMUX_TOKEN[] = "tcpmux/"; 11751553Srgrimes#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1) 11761553Srgrimes 11771553Srgrimesmore: 11781553Srgrimes while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0')) 11791553Srgrimes ; 11801553Srgrimes if (cp == NULL) 11811553Srgrimes return ((struct servtab *)0); 11821553Srgrimes /* 11831553Srgrimes * clear the static buffer, since some fields (se_ctrladdr, 11841553Srgrimes * for example) don't get initialized here. 11851553Srgrimes */ 11861553Srgrimes memset((caddr_t)sep, 0, sizeof *sep); 11871553Srgrimes arg = skip(&cp); 11881553Srgrimes if (cp == NULL) { 11891553Srgrimes /* got an empty line containing just blanks/tabs. */ 11901553Srgrimes goto more; 11911553Srgrimes } 11921553Srgrimes if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { 11931553Srgrimes char *c = arg + MUX_LEN; 11941553Srgrimes if (*c == '+') { 11951553Srgrimes sep->se_type = MUXPLUS_TYPE; 11961553Srgrimes c++; 11971553Srgrimes } else 11981553Srgrimes sep->se_type = MUX_TYPE; 11991553Srgrimes sep->se_service = newstr(c); 12001553Srgrimes } else { 12011553Srgrimes sep->se_service = newstr(arg); 12021553Srgrimes sep->se_type = NORM_TYPE; 12031553Srgrimes } 12041553Srgrimes arg = sskip(&cp); 12051553Srgrimes if (strcmp(arg, "stream") == 0) 12061553Srgrimes sep->se_socktype = SOCK_STREAM; 12071553Srgrimes else if (strcmp(arg, "dgram") == 0) 12081553Srgrimes sep->se_socktype = SOCK_DGRAM; 12091553Srgrimes else if (strcmp(arg, "rdm") == 0) 12101553Srgrimes sep->se_socktype = SOCK_RDM; 12111553Srgrimes else if (strcmp(arg, "seqpacket") == 0) 12121553Srgrimes sep->se_socktype = SOCK_SEQPACKET; 12131553Srgrimes else if (strcmp(arg, "raw") == 0) 12141553Srgrimes sep->se_socktype = SOCK_RAW; 12151553Srgrimes else 12161553Srgrimes sep->se_socktype = -1; 121736042Sguido 121836042Sguido arg = sskip(&cp); 121936042Sguido if (strcmp(arg, "tcp/ttcp") == 0) { 122036042Sguido sep->se_type = TTCP_TYPE; 122136042Sguido sep->se_proto = newstr("tcp"); 122236042Sguido } else { 122336042Sguido sep->se_proto = newstr(arg); 122436042Sguido } 12252657Scsgr if (strncmp(sep->se_proto, "rpc/", 4) == 0) { 122619237Sjoerg memmove(sep->se_proto, sep->se_proto + 4, 122719237Sjoerg strlen(sep->se_proto) + 1 - 4); 12282657Scsgr sep->se_rpc = 1; 12292657Scsgr sep->se_rpc_prog = sep->se_rpc_lowvers = 12302657Scsgr sep->se_rpc_lowvers = 0; 12312657Scsgr sep->se_ctrladdr.sin_family = AF_INET; 12322657Scsgr sep->se_ctrladdr.sin_port = 0; 123317482Sjulian sep->se_ctrladdr.sin_addr = bind_address; 12342657Scsgr if ((versp = rindex(sep->se_service, '/'))) { 12352657Scsgr *versp++ = '\0'; 12362657Scsgr switch (sscanf(versp, "%d-%d", 12372657Scsgr &sep->se_rpc_lowvers, 12382657Scsgr &sep->se_rpc_highvers)) { 12392657Scsgr case 2: 12402657Scsgr break; 12412657Scsgr case 1: 12422657Scsgr sep->se_rpc_highvers = 12432657Scsgr sep->se_rpc_lowvers; 12442657Scsgr break; 12452657Scsgr default: 12468857Srgrimes syslog(LOG_ERR, 124752219Scharnier "bad RPC version specifier; %s", 12482657Scsgr sep->se_service); 12492657Scsgr freeconfig(sep); 12502657Scsgr goto more; 12512657Scsgr } 12522657Scsgr } 12532657Scsgr else { 12542657Scsgr sep->se_rpc_lowvers = 12552657Scsgr sep->se_rpc_highvers = 1; 12562657Scsgr } 12572657Scsgr } 12581553Srgrimes arg = sskip(&cp); 125919618Sjulian if (!strncmp(arg, "wait", 4)) 126019618Sjulian sep->se_accept = 0; 126119618Sjulian else if (!strncmp(arg, "nowait", 6)) 126219618Sjulian sep->se_accept = 1; 126319618Sjulian else { 126419618Sjulian syslog(LOG_ERR, 126519618Sjulian "%s: bad wait/nowait for service %s", 126619618Sjulian CONFIG, sep->se_service); 126719618Sjulian goto more; 126819618Sjulian } 126948069Ssheldonh sep->se_maxchild = -1; 127048069Ssheldonh sep->se_maxcpm = -1; 127119618Sjulian if ((s = strchr(arg, '/')) != NULL) { 127219618Sjulian char *eptr; 127319618Sjulian u_long val; 127419618Sjulian 127519618Sjulian val = strtoul(s + 1, &eptr, 10); 127630847Sdima if (eptr == s + 1 || val > MAX_MAXCHLD) { 127719618Sjulian syslog(LOG_ERR, 127819618Sjulian "%s: bad max-child for service %s", 127919618Sjulian CONFIG, sep->se_service); 128019618Sjulian goto more; 128119618Sjulian } 128248069Ssheldonh if (debug) 128348069Ssheldonh if (!sep->se_accept && val != 1) 128448069Ssheldonh warnx("maxchild=%lu for wait service %s" 128548069Ssheldonh " not recommended", val, sep->se_service); 128619618Sjulian sep->se_maxchild = val; 128730847Sdima if (*eptr == '/') 128830847Sdima sep->se_maxcpm = strtol(eptr + 1, &eptr, 10); 128930847Sdima /* 129030847Sdima * explicitly do not check for \0 for future expansion / 129130847Sdima * backwards compatibility 129230847Sdima */ 129319618Sjulian } 12941553Srgrimes if (ISMUX(sep)) { 12951553Srgrimes /* 129619618Sjulian * Silently enforce "nowait" mode for TCPMUX services 129719618Sjulian * since they don't have an assigned port to listen on. 12981553Srgrimes */ 129919618Sjulian sep->se_accept = 1; 13001553Srgrimes if (strcmp(sep->se_proto, "tcp")) { 13018857Srgrimes syslog(LOG_ERR, 13021553Srgrimes "%s: bad protocol for tcpmux service %s", 13031553Srgrimes CONFIG, sep->se_service); 13041553Srgrimes goto more; 13051553Srgrimes } 13061553Srgrimes if (sep->se_socktype != SOCK_STREAM) { 13078857Srgrimes syslog(LOG_ERR, 13081553Srgrimes "%s: bad socket type for tcpmux service %s", 13091553Srgrimes CONFIG, sep->se_service); 13101553Srgrimes goto more; 13111553Srgrimes } 13121553Srgrimes } 13131553Srgrimes sep->se_user = newstr(sskip(&cp)); 131430792Sache#ifdef LOGIN_CAP 131530792Sache if ((s = strrchr(sep->se_user, '/')) != NULL) { 131630792Sache *s = '\0'; 131730792Sache sep->se_class = newstr(s + 1); 131830792Sache } else 131930792Sache sep->se_class = newstr(RESOURCE_RC); 132030792Sache#endif 132130807Sache if ((s = strrchr(sep->se_user, ':')) != NULL) { 132230807Sache *s = '\0'; 132330807Sache sep->se_group = newstr(s + 1); 132430807Sache } else 132530807Sache sep->se_group = NULL; 13261553Srgrimes sep->se_server = newstr(sskip(&cp)); 132745588Smarkm if ((sep->se_server_name = rindex(sep->se_server, '/'))) 132845588Smarkm sep->se_server_name++; 13291553Srgrimes if (strcmp(sep->se_server, "internal") == 0) { 13301553Srgrimes struct biltin *bi; 13311553Srgrimes 13321553Srgrimes for (bi = biltins; bi->bi_service; bi++) 133349026Sdes if (bi->bi_socktype == sep->se_socktype && 133448467Ssheldonh matchservent(bi->bi_service, sep->se_service, 133548467Ssheldonh sep->se_proto)) 13361553Srgrimes break; 13371553Srgrimes if (bi->bi_service == 0) { 13381553Srgrimes syslog(LOG_ERR, "internal service %s unknown", 13391553Srgrimes sep->se_service); 13401553Srgrimes goto more; 13411553Srgrimes } 134219618Sjulian sep->se_accept = 1; /* force accept mode for built-ins */ 13431553Srgrimes sep->se_bi = bi; 13441553Srgrimes } else 13451553Srgrimes sep->se_bi = NULL; 134648069Ssheldonh if (sep->se_maxcpm < 0) 134748069Ssheldonh sep->se_maxcpm = maxcpm; 134845588Smarkm if (sep->se_maxchild < 0) { /* apply default max-children */ 134948069Ssheldonh if (sep->se_bi && sep->se_bi->bi_maxchild >= 0) 135019618Sjulian sep->se_maxchild = sep->se_bi->bi_maxchild; 135148069Ssheldonh else if (sep->se_accept) 135248069Ssheldonh sep->se_maxchild = maxchild > 0 ? maxchild : 0; 135319618Sjulian else 135448069Ssheldonh sep->se_maxchild = 1; 135545588Smarkm } 135619618Sjulian if (sep->se_maxchild) { 135719618Sjulian sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); 135819618Sjulian if (sep->se_pids == NULL) { 135949102Ssheldonh syslog(LOG_ERR, "malloc: %m"); 136019618Sjulian exit(EX_OSERR); 136119618Sjulian } 136219618Sjulian } 13631553Srgrimes argc = 0; 13641553Srgrimes for (arg = skip(&cp); cp; arg = skip(&cp)) 136519618Sjulian if (argc < MAXARGV) { 13661553Srgrimes sep->se_argv[argc++] = newstr(arg); 136719618Sjulian } else { 136819618Sjulian syslog(LOG_ERR, 136919618Sjulian "%s: too many arguments for service %s", 137019618Sjulian CONFIG, sep->se_service); 137119618Sjulian goto more; 137219618Sjulian } 13731553Srgrimes while (argc <= MAXARGV) 13741553Srgrimes sep->se_argv[argc++] = NULL; 13751553Srgrimes return (sep); 13761553Srgrimes} 13771553Srgrimes 13781553Srgrimesvoid 13791553Srgrimesfreeconfig(cp) 13801553Srgrimes struct servtab *cp; 13811553Srgrimes{ 13821553Srgrimes int i; 13831553Srgrimes 13841553Srgrimes if (cp->se_service) 13851553Srgrimes free(cp->se_service); 13861553Srgrimes if (cp->se_proto) 13871553Srgrimes free(cp->se_proto); 13881553Srgrimes if (cp->se_user) 13891553Srgrimes free(cp->se_user); 139030807Sache if (cp->se_group) 139130807Sache free(cp->se_group); 139230792Sache#ifdef LOGIN_CAP 139330792Sache if (cp->se_class) 139430792Sache free(cp->se_class); 139530792Sache#endif 13961553Srgrimes if (cp->se_server) 13971553Srgrimes free(cp->se_server); 139819618Sjulian if (cp->se_pids) 139919618Sjulian free(cp->se_pids); 14001553Srgrimes for (i = 0; i < MAXARGV; i++) 14011553Srgrimes if (cp->se_argv[i]) 14021553Srgrimes free(cp->se_argv[i]); 14031553Srgrimes} 14041553Srgrimes 14051553Srgrimes 14061553Srgrimes/* 14071553Srgrimes * Safe skip - if skip returns null, log a syntax error in the 14081553Srgrimes * configuration file and exit. 14091553Srgrimes */ 14101553Srgrimeschar * 14111553Srgrimessskip(cpp) 14121553Srgrimes char **cpp; 14131553Srgrimes{ 14141553Srgrimes char *cp; 14151553Srgrimes 14161553Srgrimes cp = skip(cpp); 14171553Srgrimes if (cp == NULL) { 14181553Srgrimes syslog(LOG_ERR, "%s: syntax error", CONFIG); 141919617Sjulian exit(EX_DATAERR); 14201553Srgrimes } 14211553Srgrimes return (cp); 14221553Srgrimes} 14231553Srgrimes 14241553Srgrimeschar * 14251553Srgrimesskip(cpp) 14261553Srgrimes char **cpp; 14271553Srgrimes{ 14281553Srgrimes char *cp = *cpp; 14291553Srgrimes char *start; 143011933Sadam char quote = '\0'; 14311553Srgrimes 14321553Srgrimesagain: 14331553Srgrimes while (*cp == ' ' || *cp == '\t') 14341553Srgrimes cp++; 14351553Srgrimes if (*cp == '\0') { 14361553Srgrimes int c; 14371553Srgrimes 14381553Srgrimes c = getc(fconfig); 14391553Srgrimes (void) ungetc(c, fconfig); 14401553Srgrimes if (c == ' ' || c == '\t') 144119617Sjulian if ((cp = nextline(fconfig))) 14421553Srgrimes goto again; 14431553Srgrimes *cpp = (char *)0; 14441553Srgrimes return ((char *)0); 14451553Srgrimes } 144611933Sadam if (*cp == '"' || *cp == '\'') 144711933Sadam quote = *cp++; 14481553Srgrimes start = cp; 144911933Sadam if (quote) 145011933Sadam while (*cp && *cp != quote) 145111933Sadam cp++; 145211933Sadam else 145311933Sadam while (*cp && *cp != ' ' && *cp != '\t') 145411933Sadam cp++; 14551553Srgrimes if (*cp != '\0') 14561553Srgrimes *cp++ = '\0'; 14571553Srgrimes *cpp = cp; 14581553Srgrimes return (start); 14591553Srgrimes} 14601553Srgrimes 14611553Srgrimeschar * 14621553Srgrimesnextline(fd) 14631553Srgrimes FILE *fd; 14641553Srgrimes{ 14651553Srgrimes char *cp; 14661553Srgrimes 14671553Srgrimes if (fgets(line, sizeof (line), fd) == NULL) 14681553Srgrimes return ((char *)0); 14691553Srgrimes cp = strchr(line, '\n'); 14701553Srgrimes if (cp) 14711553Srgrimes *cp = '\0'; 14721553Srgrimes return (line); 14731553Srgrimes} 14741553Srgrimes 14751553Srgrimeschar * 14761553Srgrimesnewstr(cp) 14771553Srgrimes char *cp; 14781553Srgrimes{ 147919617Sjulian if ((cp = strdup(cp ? cp : ""))) 14801553Srgrimes return (cp); 14811553Srgrimes syslog(LOG_ERR, "strdup: %m"); 148219617Sjulian exit(EX_OSERR); 14831553Srgrimes} 14841553Srgrimes 148513142Speter#ifdef OLD_SETPROCTITLE 14861553Srgrimesvoid 148713142Speterinetd_setproctitle(a, s) 14881553Srgrimes char *a; 14891553Srgrimes int s; 14901553Srgrimes{ 14911553Srgrimes int size; 14921553Srgrimes char *cp; 14931553Srgrimes struct sockaddr_in sin; 14941553Srgrimes char buf[80]; 14951553Srgrimes 14961553Srgrimes cp = Argv[0]; 14971553Srgrimes size = sizeof(sin); 14981553Srgrimes if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) 14998857Srgrimes (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr)); 15001553Srgrimes else 15018857Srgrimes (void) sprintf(buf, "-%s", a); 15021553Srgrimes strncpy(cp, buf, LastArg - cp); 15031553Srgrimes cp += strlen(cp); 15041553Srgrimes while (cp < LastArg) 15051553Srgrimes *cp++ = ' '; 15061553Srgrimes} 150713142Speter#else 150813142Spetervoid 150913142Speterinetd_setproctitle(a, s) 151013142Speter char *a; 151113142Speter int s; 151213142Speter{ 151313142Speter int size; 151413142Speter struct sockaddr_in sin; 151513142Speter char buf[80]; 15161553Srgrimes 151713142Speter size = sizeof(sin); 151813142Speter if (getpeername(s, (struct sockaddr *)&sin, &size) == 0) 151913142Speter (void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr)); 152013142Speter else 152113142Speter (void) sprintf(buf, "%s", a); 152213142Speter setproctitle("%s", buf); 152313142Speter} 152413142Speter#endif 152513142Speter 152613142Speter 15271553Srgrimes/* 15281553Srgrimes * Internet services provided internally by inetd: 15291553Srgrimes */ 15301553Srgrimes 15315182Swollmanint check_loop(sin, sep) 15325182Swollman struct sockaddr_in *sin; 15335182Swollman struct servtab *sep; 15345182Swollman{ 15355182Swollman struct servtab *se2; 15365182Swollman 15375182Swollman for (se2 = servtab; se2; se2 = se2->se_next) { 15385182Swollman if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM) 15395182Swollman continue; 15405182Swollman 15415182Swollman if (sin->sin_port == se2->se_ctrladdr.sin_port) { 15425182Swollman syslog(LOG_WARNING, 15435182Swollman "%s/%s:%s/%s loop request REFUSED from %s", 15448857Srgrimes sep->se_service, sep->se_proto, 15455182Swollman se2->se_service, se2->se_proto, 15465182Swollman inet_ntoa(sin->sin_addr)); 15475182Swollman return 1; 15485182Swollman } 15495182Swollman } 15505182Swollman return 0; 15515182Swollman} 15525182Swollman 15531553Srgrimes/* 15541553Srgrimes * print_service: 15551553Srgrimes * Dump relevant information to stderr 15561553Srgrimes */ 15571553Srgrimesvoid 15581553Srgrimesprint_service(action, sep) 15591553Srgrimes char *action; 15601553Srgrimes struct servtab *sep; 15611553Srgrimes{ 156219617Sjulian fprintf(stderr, 156330792Sache#ifdef LOGIN_CAP 156438380Sjb "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%p server=%s\n", 156530792Sache#else 156638380Sjb "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%p server=%s\n", 156730792Sache#endif 156819617Sjulian action, sep->se_service, sep->se_proto, 156930807Sache sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group, 157030792Sache#ifdef LOGIN_CAP 157130792Sache sep->se_class, 157230792Sache#endif 157338417Sjb (void *) sep->se_bi, sep->se_server); 15741553Srgrimes} 15751553Srgrimes 157630847Sdima#define CPMHSIZE 256 157730847Sdima#define CPMHMASK (CPMHSIZE-1) 157830847Sdima#define CHTGRAN 10 157930847Sdima#define CHTSIZE 6 158030847Sdima 158130847Sdimatypedef struct CTime { 158230847Sdima unsigned long ct_Ticks; 158330847Sdima int ct_Count; 158430847Sdima} CTime; 158530847Sdima 158630847Sdimatypedef struct CHash { 158730847Sdima struct in_addr ch_Addr; 158830847Sdima time_t ch_LTime; 158930847Sdima char *ch_Service; 159030847Sdima CTime ch_Times[CHTSIZE]; 159130847Sdima} CHash; 159230847Sdima 159330847SdimaCHash CHashAry[CPMHSIZE]; 159430847Sdima 159530847Sdimaint 159630847Sdimacpmip(sep, ctrl) 159730847Sdima struct servtab *sep; 159830847Sdima int ctrl; 159930847Sdima{ 160030847Sdima struct sockaddr_in rsin; 160130847Sdima int rsinLen = sizeof(rsin); 160230847Sdima int r = 0; 160330847Sdima 160430847Sdima /* 160530847Sdima * If getpeername() fails, just let it through (if logging is 160630847Sdima * enabled the condition is caught elsewhere) 160730847Sdima */ 160830847Sdima 160930847Sdima if (sep->se_maxcpm > 0 && 161030847Sdima getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 0 ) { 161130847Sdima time_t t = time(NULL); 161230847Sdima int hv = 0xABC3D20F; 161330847Sdima int i; 161430847Sdima int cnt = 0; 161530847Sdima CHash *chBest = NULL; 161630847Sdima unsigned int ticks = t / CHTGRAN; 161730847Sdima 161830847Sdima { 161930847Sdima char *p; 162030847Sdima int i; 162130847Sdima 162230847Sdima for (i = 0, p = (char *)&rsin.sin_addr; 162330847Sdima i < sizeof(rsin.sin_addr); 162430847Sdima ++i, ++p) { 162530847Sdima hv = (hv << 5) ^ (hv >> 23) ^ *p; 162630847Sdima } 162730847Sdima hv = (hv ^ (hv >> 16)); 162830847Sdima } 162930847Sdima for (i = 0; i < 5; ++i) { 163030847Sdima CHash *ch = &CHashAry[(hv + i) & CPMHMASK]; 163130847Sdima 163230847Sdima if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr && 163330847Sdima ch->ch_Service && strcmp(sep->se_service, 163430847Sdima ch->ch_Service) == 0) { 163530847Sdima chBest = ch; 163630847Sdima break; 163730847Sdima } 163830847Sdima if (chBest == NULL || ch->ch_LTime == 0 || 163930847Sdima ch->ch_LTime < chBest->ch_LTime) { 164030847Sdima chBest = ch; 164130847Sdima } 164230847Sdima } 164330847Sdima if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr || 164430847Sdima chBest->ch_Service == NULL || 164530847Sdima strcmp(sep->se_service, chBest->ch_Service) != 0) { 164630847Sdima chBest->ch_Addr = rsin.sin_addr; 164730847Sdima if (chBest->ch_Service) 164830847Sdima free(chBest->ch_Service); 164930847Sdima chBest->ch_Service = strdup(sep->se_service); 165030847Sdima bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 165130847Sdima } 165230847Sdima chBest->ch_LTime = t; 165330847Sdima { 165430847Sdima CTime *ct = &chBest->ch_Times[ticks % CHTSIZE]; 165530847Sdima if (ct->ct_Ticks != ticks) { 165630847Sdima ct->ct_Ticks = ticks; 165730847Sdima ct->ct_Count = 0; 165830847Sdima } 165930847Sdima ++ct->ct_Count; 166030847Sdima } 166130847Sdima for (i = 0; i < CHTSIZE; ++i) { 166230847Sdima CTime *ct = &chBest->ch_Times[i]; 166330847Sdima if (ct->ct_Ticks <= ticks && 166430847Sdima ct->ct_Ticks >= ticks - CHTSIZE) { 166530847Sdima cnt += ct->ct_Count; 166630847Sdima } 166730847Sdima } 166830847Sdima if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) { 166930847Sdima r = -1; 167030847Sdima syslog(LOG_ERR, 167133794Spst "%s from %s exceeded counts/min (limit %d/min)", 167233794Spst sep->se_service, inet_ntoa(rsin.sin_addr), 167333794Spst sep->se_maxcpm); 167430847Sdima } 167530847Sdima } 167630847Sdima return(r); 167730847Sdima} 1678