inetd.c revision 102859
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 441553Srgrimes#endif /* not lint */ 451553Srgrimes 4698563Sjmallett#include <sys/cdefs.h> 4798563Sjmallett__FBSDID("$FreeBSD: head/usr.sbin/inetd/inetd.c 102859 2002-09-02 19:58:15Z dwmalone $"); 4898563Sjmallett 491553Srgrimes/* 501553Srgrimes * Inetd - Internet super-server 511553Srgrimes * 521553Srgrimes * This program invokes all internet services as needed. Connection-oriented 531553Srgrimes * services are invoked each time a connection is made, by creating a process. 541553Srgrimes * This process is passed the connection as file descriptor 0 and is expected 551553Srgrimes * to do a getpeername to find out the source host and port. 561553Srgrimes * 571553Srgrimes * Datagram oriented services are invoked when a datagram 581553Srgrimes * arrives; a process is created and passed a pending message 591553Srgrimes * on file descriptor 0. Datagram servers may either connect 601553Srgrimes * to their peer, freeing up the original socket for inetd 611553Srgrimes * to receive further messages on, or ``take over the socket'', 621553Srgrimes * processing all arriving datagrams and, eventually, timing 631553Srgrimes * out. The first type of server is said to be ``multi-threaded''; 648857Srgrimes * the second type of server ``single-threaded''. 651553Srgrimes * 661553Srgrimes * Inetd uses a configuration file which is read at startup 671553Srgrimes * and, possibly, at some later time in response to a hangup signal. 681553Srgrimes * The configuration file is ``free format'' with fields given in the 6967514Sdwmalone * order shown below. Continuation lines for an entry must begin with 701553Srgrimes * a space or tab. All fields must be present in each entry. 711553Srgrimes * 7278356Sdwmalone * service name must be in /etc/services 7378356Sdwmalone * or name a tcpmux service 7478356Sdwmalone * or specify a unix domain socket 751553Srgrimes * socket type stream/dgram/raw/rdm/seqpacket 7678356Sdwmalone * protocol tcp[4][6][/faith,ttcp], udp[4][6], unix 771553Srgrimes * wait/nowait single-threaded/multi-threaded 781553Srgrimes * user user to run daemon as 791553Srgrimes * server program full path name 801553Srgrimes * server program arguments maximum of MAXARGS (20) 811553Srgrimes * 821553Srgrimes * TCP services without official port numbers are handled with the 831553Srgrimes * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for 841553Srgrimes * requests. When a connection is made from a foreign host, the service 851553Srgrimes * requested is passed to tcpmux, which looks it up in the servtab list 861553Srgrimes * and returns the proper entry for the service. Tcpmux returns a 871553Srgrimes * negative reply if the service doesn't exist, otherwise the invoked 881553Srgrimes * server is expected to return the positive reply if the service type in 891553Srgrimes * inetd.conf file has the prefix "tcpmux/". If the service type has the 901553Srgrimes * prefix "tcpmux/+", tcpmux will return the positive reply for the 911553Srgrimes * process; this is for compatibility with older server code, and also 921553Srgrimes * allows you to invoke programs that use stdin/stdout without putting any 931553Srgrimes * special server code in them. Services that use tcpmux are "nowait" 941553Srgrimes * because they do not have a well-known port and hence cannot listen 951553Srgrimes * for new requests. 961553Srgrimes * 972657Scsgr * For RPC services 982657Scsgr * service name/version must be in /etc/rpc 992657Scsgr * socket type stream/dgram/raw/rdm/seqpacket 100100127Salfred * protocol rpc/tcp[4][6], rpc/udp[4][6] 1012657Scsgr * wait/nowait single-threaded/multi-threaded 1022657Scsgr * user user to run daemon as 1032657Scsgr * server program full path name 1042657Scsgr * server program arguments maximum of MAXARGS 1052657Scsgr * 1061553Srgrimes * Comment lines are indicated by a `#' in column 1. 10756590Sshin * 10856590Sshin * #ifdef IPSEC 10956590Sshin * Comment lines that start with "#@" denote IPsec policy string, as described 11056590Sshin * in ipsec_set_policy(3). This will affect all the following items in 11156590Sshin * inetd.conf(8). To reset the policy, just use "#@" line. By default, 11256590Sshin * there's no IPsec policy. 11356590Sshin * #endif 1141553Srgrimes */ 1151553Srgrimes#include <sys/param.h> 1161553Srgrimes#include <sys/ioctl.h> 1171553Srgrimes#include <sys/wait.h> 1181553Srgrimes#include <sys/time.h> 1191553Srgrimes#include <sys/resource.h> 12078356Sdwmalone#include <sys/stat.h> 12178356Sdwmalone#include <sys/un.h> 1221553Srgrimes 1231553Srgrimes#include <netinet/in.h> 12436042Sguido#include <netinet/tcp.h> 1251553Srgrimes#include <arpa/inet.h> 1262657Scsgr#include <rpc/rpc.h> 12719617Sjulian#include <rpc/pmap_clnt.h> 1281553Srgrimes 1291553Srgrimes#include <errno.h> 13029602Scharnier#include <err.h> 1311553Srgrimes#include <fcntl.h> 13230807Sache#include <grp.h> 1331553Srgrimes#include <netdb.h> 1341553Srgrimes#include <pwd.h> 1351553Srgrimes#include <signal.h> 1361553Srgrimes#include <stdio.h> 1371553Srgrimes#include <stdlib.h> 1381553Srgrimes#include <string.h> 1391553Srgrimes#include <syslog.h> 14048279Ssheldonh#include <tcpd.h> 1411553Srgrimes#include <unistd.h> 14213142Speter#include <libutil.h> 14319617Sjulian#include <sysexits.h> 14456590Sshin#include <ctype.h> 1451553Srgrimes 14648981Ssheldonh#include "inetd.h" 14748981Ssheldonh#include "pathnames.h" 14848981Ssheldonh 14956590Sshin#ifdef IPSEC 15056590Sshin#include <netinet6/ipsec.h> 15156590Sshin#ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */ 15256590Sshin#undef IPSEC 15356590Sshin#endif 15456590Sshin#endif 15556590Sshin 15656590Sshin/* wrapper for KAME-special getnameinfo() */ 15756590Sshin#ifndef NI_WITHSCOPEID 15856590Sshin#define NI_WITHSCOPEID 0 15956590Sshin#endif 16056590Sshin 16145089Smarkm#ifndef LIBWRAP_ALLOW_FACILITY 16245089Smarkm# define LIBWRAP_ALLOW_FACILITY LOG_AUTH 16345089Smarkm#endif 16445089Smarkm#ifndef LIBWRAP_ALLOW_SEVERITY 16545089Smarkm# define LIBWRAP_ALLOW_SEVERITY LOG_INFO 16645089Smarkm#endif 16745089Smarkm#ifndef LIBWRAP_DENY_FACILITY 16845089Smarkm# define LIBWRAP_DENY_FACILITY LOG_AUTH 16945089Smarkm#endif 17045089Smarkm#ifndef LIBWRAP_DENY_SEVERITY 17145089Smarkm# define LIBWRAP_DENY_SEVERITY LOG_WARNING 17245089Smarkm#endif 17345089Smarkm 17448382Ssheldonh#define ISWRAP(sep) \ 17548697Ssheldonh ( ((wrap_ex && !(sep)->se_bi) || (wrap_bi && (sep)->se_bi)) \ 17678356Sdwmalone && (sep->se_family == AF_INET || sep->se_family == AF_INET6) \ 17748382Ssheldonh && ( ((sep)->se_accept && (sep)->se_socktype == SOCK_STREAM) \ 17848382Ssheldonh || (sep)->se_socktype == SOCK_DGRAM)) 17948382Ssheldonh 18021640Speter#ifdef LOGIN_CAP 18121640Speter#include <login_cap.h> 18230792Sache 18330792Sache/* see init.c */ 18430792Sache#define RESOURCE_RC "daemon" 18530792Sache 18621640Speter#endif 18721640Speter 18833794Spst#ifndef MAXCHILD 18933794Spst#define MAXCHILD -1 /* maximum number of this service 19033794Spst < 0 = no limit */ 19133794Spst#endif 19233794Spst 19333794Spst#ifndef MAXCPM 19433794Spst#define MAXCPM -1 /* rate limit invocations from a 19533794Spst single remote address, 19633794Spst < 0 = no limit */ 19733794Spst#endif 19833794Spst 199101474Sume#ifndef MAXPERIP 200101474Sume#define MAXPERIP -1 /* maximum number of this service 201101474Sume from a single remote address, 202101474Sume < 0 = no limit */ 203101474Sume#endif 204101474Sume 20564197Sdwmalone#ifndef TOOMANY 2062659Scsgr#define TOOMANY 256 /* don't start more than TOOMANY */ 20764197Sdwmalone#endif 2081553Srgrimes#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ 2091553Srgrimes#define RETRYTIME (60*10) /* retry after bind or server fail */ 21019618Sjulian#define MAX_MAXCHLD 32767 /* max allowable max children */ 2111553Srgrimes 2121553Srgrimes#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) 2131553Srgrimes 21498562Sjmallettvoid close_sep(struct servtab *); 21598562Sjmallettvoid flag_signal(int); 21698562Sjmallettvoid flag_config(int); 21798562Sjmallettvoid config(void); 21898562Sjmallettint cpmip(const struct servtab *, int); 21998562Sjmallettvoid endconfig(void); 22098562Sjmallettstruct servtab *enter(struct servtab *); 22198562Sjmallettvoid freeconfig(struct servtab *); 22298562Sjmallettstruct servtab *getconfigent(void); 22398562Sjmallettint matchservent(const char *, const char *, const char *); 22498562Sjmallettchar *nextline(FILE *); 22598562Sjmallettvoid addchild(struct servtab *, int); 22698562Sjmallettvoid flag_reapchild(int); 22798562Sjmallettvoid reapchild(void); 22898562Sjmallettvoid enable(struct servtab *); 22998562Sjmallettvoid disable(struct servtab *); 23098562Sjmallettvoid flag_retry(int); 23198562Sjmallettvoid retry(void); 23298562Sjmallettint setconfig(void); 23398562Sjmallettvoid setup(struct servtab *); 23478694Sdwmalone#ifdef IPSEC 23598562Sjmallettvoid ipsecsetup(struct servtab *); 23678694Sdwmalone#endif 23798562Sjmallettvoid unregisterrpc(register struct servtab *sep); 238101474Sumestatic struct conninfo *search_conn(struct servtab *sep, int ctrl); 239101474Sumestatic int room_conn(struct servtab *sep, struct conninfo *conn); 240101474Sumestatic void addchild_conn(struct conninfo *conn, pid_t pid); 241101474Sumestatic void reapchild_conn(pid_t pid); 242101474Sumestatic void free_conn(struct conninfo *conn); 243101474Sumestatic void resize_conn(struct servtab *sep, int maxperip); 244101474Sumestatic void free_connlist(struct servtab *sep); 245101474Sumestatic void free_proc(struct procinfo *); 246101474Sumestatic struct procinfo *search_proc(pid_t pid, int add); 247101474Sumestatic int hashval(char *p, int len); 24878694Sdwmalone 24948279Ssheldonhint allow_severity; 25048279Ssheldonhint deny_severity; 25148697Ssheldonhint wrap_ex = 0; 25248279Ssheldonhint wrap_bi = 0; 2531553Srgrimesint debug = 0; 2542659Scsgrint log = 0; 25548988Ssheldonhint maxsock; /* highest-numbered descriptor */ 2561553Srgrimesfd_set allsock; 2571553Srgrimesint options; 2581553Srgrimesint timingout; 2591553Srgrimesint toomany = TOOMANY; 26048069Ssheldonhint maxchild = MAXCHILD; 26148069Ssheldonhint maxcpm = MAXCPM; 262101474Sumeint maxperip = MAXPERIP; 2631553Srgrimesstruct servent *sp; 2642657Scsgrstruct rpcent *rpc; 26556590Sshinchar *hostname = NULL; 26656590Sshinstruct sockaddr_in *bind_sa4; 26756590Sshinint no_v4bind = 1; 26856590Sshin#ifdef INET6 26956590Sshinstruct sockaddr_in6 *bind_sa6; 27056590Sshinint no_v6bind = 1; 27156590Sshin#endif 27242122Sdesint signalpipe[2]; 27348991Ssheldonh#ifdef SANITY_CHECK 27448991Ssheldonhint nsock; 27548991Ssheldonh#endif 27678356Sdwmaloneuid_t euid; 27778356Sdwmalonegid_t egid; 27878356Sdwmalonemode_t mask; 2791553Srgrimes 28048981Ssheldonhstruct servtab *servtab; 2811553Srgrimes 28248981Ssheldonhextern struct biltin biltins[]; 2831553Srgrimes 28478694Sdwmaloneconst char *CONFIG = _PATH_INETDCONF; 28578694Sdwmaloneconst char *pid_file = _PATH_INETDPID; 28613142Speter 287100127Salfredstruct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf; 288100127Salfred 289101474Sumestatic LIST_HEAD(, procinfo) proctable[PERIPSIZE]; 290101474Sume 2911553Srgrimesint 29298558Sjmallettgetvalue(const char *arg, int *value, const char *whine) 29333794Spst{ 29433794Spst int tmp; 29533794Spst char *p; 29633794Spst 29733794Spst tmp = strtol(arg, &p, 0); 29864197Sdwmalone if (tmp < 0 || *p) { 29933794Spst syslog(LOG_ERR, whine, arg); 30033794Spst return 1; /* failure */ 30133794Spst } 30233794Spst *value = tmp; 30333794Spst return 0; /* success */ 30433794Spst} 30533794Spst 30633794Spstint 30798558Sjmallettmain(int argc, char **argv) 3081553Srgrimes{ 3091553Srgrimes struct servtab *sep; 3101553Srgrimes struct passwd *pwd; 31130807Sache struct group *grp; 31248962Ssheldonh struct sigaction sa, saalrm, sachld, sahup, sapipe; 3131553Srgrimes int tmpint, ch, dofork; 3141553Srgrimes pid_t pid; 3151553Srgrimes char buf[50]; 31621640Speter#ifdef LOGIN_CAP 31721640Speter login_cap_t *lc = NULL; 31821640Speter#endif 31945089Smarkm struct request_info req; 32045089Smarkm int denied; 32145089Smarkm char *service = NULL; 32256590Sshin union { 32356590Sshin struct sockaddr peer_un; 32456590Sshin struct sockaddr_in peer_un4; 32556590Sshin struct sockaddr_in6 peer_un6; 32656590Sshin struct sockaddr_storage peer_max; 32756590Sshin } p_un; 32856590Sshin#define peer p_un.peer_un 32956590Sshin#define peer4 p_un.peer_un4 33056590Sshin#define peer6 p_un.peer_un6 33156590Sshin#define peermax p_un.peer_max 33247972Ssheldonh int i; 33356590Sshin struct addrinfo hints, *res; 33478694Sdwmalone const char *servname; 33556590Sshin int error; 336101474Sume struct conninfo *conn; 3371553Srgrimes 33897293Sjwd openlog("inetd", LOG_PID | LOG_NOWAIT | LOG_PERROR, LOG_DAEMON); 3391553Srgrimes 340101474Sume while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:s:")) != -1) 3411553Srgrimes switch(ch) { 3421553Srgrimes case 'd': 3431553Srgrimes debug = 1; 3441553Srgrimes options |= SO_DEBUG; 3451553Srgrimes break; 3462659Scsgr case 'l': 3472659Scsgr log = 1; 3482659Scsgr break; 34933794Spst case 'R': 35033794Spst getvalue(optarg, &toomany, 35133794Spst "-R %s: bad value for service invocation rate"); 3521553Srgrimes break; 35333794Spst case 'c': 35433794Spst getvalue(optarg, &maxchild, 35533794Spst "-c %s: bad value for maximum children"); 35633794Spst break; 35733794Spst case 'C': 35833794Spst getvalue(optarg, &maxcpm, 35933794Spst "-C %s: bad value for maximum children/minute"); 36033794Spst break; 36117482Sjulian case 'a': 36256590Sshin hostname = optarg; 36317482Sjulian break; 36417482Sjulian case 'p': 36517482Sjulian pid_file = optarg; 36617482Sjulian break; 367101474Sume case 's': 368101474Sume getvalue(optarg, &maxperip, 369101474Sume "-s %s: bad value for maximum children per source address"); 370101474Sume break; 37148279Ssheldonh case 'w': 37248697Ssheldonh wrap_ex++; 37348279Ssheldonh break; 37448697Ssheldonh case 'W': 37548697Ssheldonh wrap_bi++; 37648697Ssheldonh break; 3771553Srgrimes case '?': 3781553Srgrimes default: 3791553Srgrimes syslog(LOG_ERR, 38048697Ssheldonh "usage: inetd [-dlwW] [-a address] [-R rate]" 38133794Spst " [-c maximum] [-C rate]" 38217482Sjulian " [-p pidfile] [conf-file]"); 38319617Sjulian exit(EX_USAGE); 3841553Srgrimes } 38556590Sshin /* 38656590Sshin * Initialize Bind Addrs. 38756590Sshin * When hostname is NULL, wild card bind addrs are obtained from 38856590Sshin * getaddrinfo(). But getaddrinfo() requires at least one of 38956590Sshin * hostname or servname is non NULL. 39056590Sshin * So when hostname is NULL, set dummy value to servname. 39156590Sshin */ 39256590Sshin servname = (hostname == NULL) ? "discard" /* dummy */ : NULL; 39356590Sshin 39456590Sshin bzero(&hints, sizeof(struct addrinfo)); 39556590Sshin hints.ai_flags = AI_PASSIVE; 39656590Sshin hints.ai_family = AF_UNSPEC; 39756590Sshin error = getaddrinfo(hostname, servname, &hints, &res); 39856590Sshin if (error != 0) { 39956590Sshin syslog(LOG_ERR, "-a %s: %s", hostname, gai_strerror(error)); 40056590Sshin if (error == EAI_SYSTEM) 40156590Sshin syslog(LOG_ERR, "%s", strerror(errno)); 40256590Sshin exit(EX_USAGE); 40356590Sshin } 40456590Sshin do { 40556590Sshin if (res->ai_addr == NULL) { 40656590Sshin syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname); 40756590Sshin exit(EX_USAGE); 40856590Sshin } 40956590Sshin switch (res->ai_addr->sa_family) { 41056590Sshin case AF_INET: 41156590Sshin if (no_v4bind == 0) 41256590Sshin continue; 41356590Sshin bind_sa4 = (struct sockaddr_in *)res->ai_addr; 41456590Sshin /* init port num in case servname is dummy */ 41556590Sshin bind_sa4->sin_port = 0; 41656590Sshin no_v4bind = 0; 41756590Sshin continue; 41856590Sshin#ifdef INET6 41956590Sshin case AF_INET6: 42056590Sshin if (no_v6bind == 0) 42156590Sshin continue; 42256590Sshin bind_sa6 = (struct sockaddr_in6 *)res->ai_addr; 42356590Sshin /* init port num in case servname is dummy */ 42456590Sshin bind_sa6->sin6_port = 0; 42556590Sshin no_v6bind = 0; 42656590Sshin continue; 42756590Sshin#endif 42856590Sshin } 42956590Sshin if (no_v4bind == 0 43056590Sshin#ifdef INET6 43156590Sshin && no_v6bind == 0 43256590Sshin#endif 43356590Sshin ) 43456590Sshin break; 43556590Sshin } while ((res = res->ai_next) != NULL); 43656590Sshin if (no_v4bind != 0 43756590Sshin#ifdef INET6 43856590Sshin && no_v6bind != 0 43956590Sshin#endif 44056590Sshin ) { 44156590Sshin syslog(LOG_ERR, "-a %s: unknown address family", hostname); 44256590Sshin exit(EX_USAGE); 44356590Sshin } 44456590Sshin 44578356Sdwmalone euid = geteuid(); 44678356Sdwmalone egid = getegid(); 44778356Sdwmalone umask(mask = umask(0777)); 44878356Sdwmalone 4491553Srgrimes argc -= optind; 4501553Srgrimes argv += optind; 4511553Srgrimes 4521553Srgrimes if (argc > 0) 4531553Srgrimes CONFIG = argv[0]; 4541553Srgrimes if (debug == 0) { 45511447Swollman FILE *fp; 45612024Speter if (daemon(0, 0) < 0) { 45712024Speter syslog(LOG_WARNING, "daemon(0,0) failed: %m"); 45812024Speter } 45997293Sjwd /* From now on we don't want syslog messages going to stderr. */ 46097293Sjwd closelog(); 46197293Sjwd openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); 46212024Speter /* 46312024Speter * In case somebody has started inetd manually, we need to 46412024Speter * clear the logname, so that old servers run as root do not 46512024Speter * get the user's logname.. 46612024Speter */ 46712024Speter if (setlogin("") < 0) { 46812024Speter syslog(LOG_WARNING, "cannot clear logname: %m"); 46912024Speter /* no big deal if it fails.. */ 47012024Speter } 47111447Swollman pid = getpid(); 47217482Sjulian fp = fopen(pid_file, "w"); 47311447Swollman if (fp) { 47411447Swollman fprintf(fp, "%ld\n", (long)pid); 47511447Swollman fclose(fp); 47611447Swollman } else { 47717482Sjulian syslog(LOG_WARNING, "%s: %m", pid_file); 47811447Swollman } 4791553Srgrimes } 480100127Salfred 481101474Sume for (i = 0; i < PERIPSIZE; ++i) 482101474Sume LIST_INIT(&proctable[i]); 483101474Sume 484100127Salfred if (!no_v4bind) { 485100127Salfred udpconf = getnetconfigent("udp"); 486100127Salfred tcpconf = getnetconfigent("tcp"); 487100127Salfred if (udpconf == NULL || tcpconf == NULL) { 488100127Salfred syslog(LOG_ERR, "unknown rpc/udp or rpc/tpc"); 489100127Salfred exit(EX_USAGE); 490100127Salfred } 491100127Salfred } 492100127Salfred#ifdef INET6 493100127Salfred if (!no_v6bind) { 494100127Salfred udp6conf = getnetconfigent("udp6"); 495100127Salfred tcp6conf = getnetconfigent("tcp6"); 496100127Salfred if (udp6conf == NULL || tcp6conf == NULL) { 497100127Salfred syslog(LOG_ERR, "unknown rpc/udp6 or rpc/tpc6"); 498100127Salfred exit(EX_USAGE); 499100127Salfred } 500100127Salfred } 501100127Salfred#endif 502100127Salfred 50335948Sbde sa.sa_flags = 0; 50435948Sbde sigemptyset(&sa.sa_mask); 50535948Sbde sigaddset(&sa.sa_mask, SIGALRM); 50635948Sbde sigaddset(&sa.sa_mask, SIGCHLD); 50735948Sbde sigaddset(&sa.sa_mask, SIGHUP); 50842122Sdes sa.sa_handler = flag_retry; 50948962Ssheldonh sigaction(SIGALRM, &sa, &saalrm); 51042122Sdes config(); 51142122Sdes sa.sa_handler = flag_config; 51248962Ssheldonh sigaction(SIGHUP, &sa, &sahup); 51342122Sdes sa.sa_handler = flag_reapchild; 51448962Ssheldonh sigaction(SIGCHLD, &sa, &sachld); 51535848Sguido sa.sa_handler = SIG_IGN; 51635948Sbde sigaction(SIGPIPE, &sa, &sapipe); 5171553Srgrimes 5181553Srgrimes { 5191553Srgrimes /* space for daemons to overwrite environment for ps */ 5201553Srgrimes#define DUMMYSIZE 100 5211553Srgrimes char dummy[DUMMYSIZE]; 5221553Srgrimes 52319298Salex (void)memset(dummy, 'x', DUMMYSIZE - 1); 5241553Srgrimes dummy[DUMMYSIZE - 1] = '\0'; 5251553Srgrimes (void)setenv("inetd_dummy", dummy, 1); 5261553Srgrimes } 5271553Srgrimes 52842250Sdes if (pipe(signalpipe) != 0) { 52952219Scharnier syslog(LOG_ERR, "pipe: %m"); 53042250Sdes exit(EX_OSERR); 53142122Sdes } 53242122Sdes FD_SET(signalpipe[0], &allsock); 53348991Ssheldonh#ifdef SANITY_CHECK 53447015Sdes nsock++; 53548991Ssheldonh#endif 53648989Ssheldonh if (signalpipe[0] > maxsock) 53748989Ssheldonh maxsock = signalpipe[0]; 53848989Ssheldonh if (signalpipe[1] > maxsock) 53948989Ssheldonh maxsock = signalpipe[1]; 54041685Sdillon 5411553Srgrimes for (;;) { 5421553Srgrimes int n, ctrl; 5431553Srgrimes fd_set readable; 5441553Srgrimes 54548991Ssheldonh#ifdef SANITY_CHECK 5461553Srgrimes if (nsock == 0) { 54747015Sdes syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 54847015Sdes exit(EX_SOFTWARE); 5491553Srgrimes } 55048991Ssheldonh#endif 5511553Srgrimes readable = allsock; 55242122Sdes if ((n = select(maxsock + 1, &readable, (fd_set *)0, 55342122Sdes (fd_set *)0, (struct timeval *)0)) <= 0) { 55442122Sdes if (n < 0 && errno != EINTR) { 5551553Srgrimes syslog(LOG_WARNING, "select: %m"); 55628907Simp sleep(1); 55728907Simp } 5581553Srgrimes continue; 5591553Srgrimes } 56042122Sdes /* handle any queued signal flags */ 56142250Sdes if (FD_ISSET(signalpipe[0], &readable)) { 56271399Sdwmalone int nsig; 56371399Sdwmalone if (ioctl(signalpipe[0], FIONREAD, &nsig) != 0) { 56442122Sdes syslog(LOG_ERR, "ioctl: %m"); 56542122Sdes exit(EX_OSERR); 56642122Sdes } 56771399Sdwmalone while (--nsig >= 0) { 56842250Sdes char c; 56942250Sdes if (read(signalpipe[0], &c, 1) != 1) { 57042250Sdes syslog(LOG_ERR, "read: %m"); 57142250Sdes exit(EX_OSERR); 57242250Sdes } 57342250Sdes if (debug) 57456482Scharnier warnx("handling signal flag %c", c); 57542250Sdes switch(c) { 57642250Sdes case 'A': /* sigalrm */ 57742250Sdes retry(); 57842250Sdes break; 57942250Sdes case 'C': /* sigchld */ 58042250Sdes reapchild(); 58142250Sdes break; 58242250Sdes case 'H': /* sighup */ 58342250Sdes config(); 58442250Sdes break; 58542250Sdes } 58642250Sdes } 58742122Sdes } 5881553Srgrimes for (sep = servtab; n && sep; sep = sep->se_next) 5891553Srgrimes if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { 5901553Srgrimes n--; 5911553Srgrimes if (debug) 59229602Scharnier warnx("someone wants %s", sep->se_service); 593101474Sume dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep); 594101474Sume conn = NULL; 59519618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) { 59653256Speter i = 1; 59753256Speter if (ioctl(sep->se_fd, FIONBIO, &i) < 0) 59853256Speter syslog(LOG_ERR, "ioctl (FIONBIO, 1): %m"); 5991553Srgrimes ctrl = accept(sep->se_fd, (struct sockaddr *)0, 60071399Sdwmalone (socklen_t *)0); 6011553Srgrimes if (debug) 60229602Scharnier warnx("accept, ctrl %d", ctrl); 6031553Srgrimes if (ctrl < 0) { 6041553Srgrimes if (errno != EINTR) 6051553Srgrimes syslog(LOG_WARNING, 6061553Srgrimes "accept (for %s): %m", 60737844Sphk sep->se_service); 60837816Sphk if (sep->se_accept && 60937816Sphk sep->se_socktype == SOCK_STREAM) 61037816Sphk close(ctrl); 6111553Srgrimes continue; 6121553Srgrimes } 61353256Speter i = 0; 61453256Speter if (ioctl(sep->se_fd, FIONBIO, &i) < 0) 61553256Speter syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m"); 61653256Speter if (ioctl(ctrl, FIONBIO, &i) < 0) 61753256Speter syslog(LOG_ERR, "ioctl2(FIONBIO, 0): %m"); 61830847Sdima if (cpmip(sep, ctrl) < 0) { 61930847Sdima close(ctrl); 62030847Sdima continue; 62130847Sdima } 622101474Sume if (dofork && 623101474Sume (conn = search_conn(sep, ctrl)) != NULL && 624101474Sume !room_conn(sep, conn)) { 625101474Sume close(ctrl); 626101474Sume continue; 627101474Sume } 6281553Srgrimes } else 6291553Srgrimes ctrl = sep->se_fd; 63048382Ssheldonh if (log && !ISWRAP(sep)) { 63171399Sdwmalone char pname[INET6_ADDRSTRLEN] = "unknown"; 63271399Sdwmalone socklen_t sl; 63371399Sdwmalone sl = sizeof peermax; 63448382Ssheldonh if (getpeername(ctrl, (struct sockaddr *) 63571399Sdwmalone &peermax, &sl)) { 63671399Sdwmalone sl = sizeof peermax; 63748382Ssheldonh if (recvfrom(ctrl, buf, sizeof(buf), 63848382Ssheldonh MSG_PEEK, 63956590Sshin (struct sockaddr *)&peermax, 64071399Sdwmalone &sl) >= 0) { 64156590Sshin getnameinfo((struct sockaddr *)&peermax, 64257383Sshin peer.sa_len, 64356590Sshin pname, sizeof(pname), 64456590Sshin NULL, 0, 64556590Sshin NI_NUMERICHOST| 64656590Sshin NI_WITHSCOPEID); 64756590Sshin } 64856590Sshin } else { 64956590Sshin getnameinfo((struct sockaddr *)&peermax, 65057383Sshin peer.sa_len, 65156590Sshin pname, sizeof(pname), 65256590Sshin NULL, 0, 65356590Sshin NI_NUMERICHOST| 65456590Sshin NI_WITHSCOPEID); 65548382Ssheldonh } 65671399Sdwmalone syslog(LOG_INFO,"%s from %s", sep->se_service, pname); 65748382Ssheldonh } 65842122Sdes (void) sigblock(SIGBLOCK); 6591553Srgrimes pid = 0; 66047972Ssheldonh /* 66148958Ssheldonh * Fork for all external services, builtins which need to 66248958Ssheldonh * fork and anything we're wrapping (as wrapping might 66348958Ssheldonh * block or use hosts_options(5) twist). 66447972Ssheldonh */ 6651553Srgrimes if (dofork) { 6661553Srgrimes if (sep->se_count++ == 0) 66737856Sache (void)gettimeofday(&sep->se_time, (struct timezone *)NULL); 66864197Sdwmalone else if (toomany > 0 && sep->se_count >= toomany) { 6691553Srgrimes struct timeval now; 6701553Srgrimes 67137856Sache (void)gettimeofday(&now, (struct timezone *)NULL); 6721553Srgrimes if (now.tv_sec - sep->se_time.tv_sec > 6731553Srgrimes CNT_INTVL) { 6741553Srgrimes sep->se_time = now; 6751553Srgrimes sep->se_count = 1; 6761553Srgrimes } else { 6771553Srgrimes syslog(LOG_ERR, 6781553Srgrimes "%s/%s server failing (looping), service terminated", 6791553Srgrimes sep->se_service, sep->se_proto); 68067415Sdwmalone if (sep->se_accept && 68167415Sdwmalone sep->se_socktype == SOCK_STREAM) 68267415Sdwmalone close(ctrl); 6831553Srgrimes close_sep(sep); 684101474Sume free_conn(conn); 68542122Sdes sigsetmask(0L); 6861553Srgrimes if (!timingout) { 6871553Srgrimes timingout = 1; 6881553Srgrimes alarm(RETRYTIME); 6891553Srgrimes } 6901553Srgrimes continue; 6911553Srgrimes } 6921553Srgrimes } 6931553Srgrimes pid = fork(); 6941553Srgrimes } 6951553Srgrimes if (pid < 0) { 6961553Srgrimes syslog(LOG_ERR, "fork: %m"); 69719618Sjulian if (sep->se_accept && 6981553Srgrimes sep->se_socktype == SOCK_STREAM) 6991553Srgrimes close(ctrl); 700101474Sume free_conn(conn); 70142122Sdes sigsetmask(0L); 7021553Srgrimes sleep(1); 7031553Srgrimes continue; 7041553Srgrimes } 705101474Sume if (pid) { 706101474Sume addchild_conn(conn, pid); 70719618Sjulian addchild(sep, pid); 708101474Sume } 70942122Sdes sigsetmask(0L); 7101553Srgrimes if (pid == 0) { 7111553Srgrimes if (dofork) { 7121553Srgrimes if (debug) 71329602Scharnier warnx("+ closing from %d", maxsock); 7141553Srgrimes for (tmpint = maxsock; tmpint > 2; tmpint--) 7151553Srgrimes if (tmpint != ctrl) 71619617Sjulian (void) close(tmpint); 71748962Ssheldonh sigaction(SIGALRM, &saalrm, (struct sigaction *)0); 71848962Ssheldonh sigaction(SIGCHLD, &sachld, (struct sigaction *)0); 71948962Ssheldonh sigaction(SIGHUP, &sahup, (struct sigaction *)0); 72048962Ssheldonh /* SIGPIPE reset before exec */ 7211553Srgrimes } 72235829Sguido /* 72335829Sguido * Call tcpmux to find the real service to exec. 72435829Sguido */ 72535829Sguido if (sep->se_bi && 72678694Sdwmalone sep->se_bi->bi_fn == (bi_fn_t *) tcpmux) { 72735829Sguido sep = tcpmux(ctrl); 72835829Sguido if (sep == NULL) { 72935829Sguido close(ctrl); 73035829Sguido _exit(0); 73135829Sguido } 73235829Sguido } 73348382Ssheldonh if (ISWRAP(sep)) { 73448698Ssheldonh inetd_setproctitle("wrapping", ctrl); 73547972Ssheldonh service = sep->se_server_name ? 73647972Ssheldonh sep->se_server_name : sep->se_service; 73747972Ssheldonh request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, NULL); 73845089Smarkm fromhost(&req); 73947972Ssheldonh deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY; 74047972Ssheldonh allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY; 74145089Smarkm denied = !hosts_access(&req); 74245089Smarkm if (denied) { 74345089Smarkm syslog(deny_severity, 74496224Sume "refused connection from %.500s, service %s (%s%s)", 74596224Sume eval_client(&req), service, sep->se_proto, 74696227Sume (((struct sockaddr *)req.client->sin)->sa_family == AF_INET6 && !IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)req.client->sin)->sin6_addr)) ? "6" : ""); 74748382Ssheldonh if (sep->se_socktype != SOCK_STREAM) 74848382Ssheldonh recv(ctrl, buf, sizeof (buf), 0); 74964059Sdwmalone if (dofork) { 75064059Sdwmalone sleep(1); 75148382Ssheldonh _exit(0); 75264059Sdwmalone } 75345089Smarkm } 75445089Smarkm if (log) { 75545089Smarkm syslog(allow_severity, 75696224Sume "connection from %.500s, service %s (%s%s)", 75796224Sume eval_client(&req), service, sep->se_proto, 75896227Sume (((struct sockaddr *)req.client->sin)->sa_family == AF_INET6 && !IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)req.client->sin)->sin6_addr)) ? "6" : ""); 75945089Smarkm } 76045089Smarkm } 76119617Sjulian if (sep->se_bi) { 7621553Srgrimes (*sep->se_bi->bi_fn)(ctrl, sep); 76319617Sjulian } else { 7641553Srgrimes if (debug) 76529602Scharnier warnx("%d execl %s", 76629602Scharnier getpid(), sep->se_server); 7671553Srgrimes dup2(ctrl, 0); 7681553Srgrimes close(ctrl); 7691553Srgrimes dup2(0, 1); 7701553Srgrimes dup2(0, 2); 7711553Srgrimes if ((pwd = getpwnam(sep->se_user)) == NULL) { 7721553Srgrimes syslog(LOG_ERR, 77356482Scharnier "%s/%s: %s: no such user", 7741553Srgrimes sep->se_service, sep->se_proto, 7751553Srgrimes sep->se_user); 7761553Srgrimes if (sep->se_socktype != SOCK_STREAM) 7771553Srgrimes recv(0, buf, sizeof (buf), 0); 77819617Sjulian _exit(EX_NOUSER); 7791553Srgrimes } 78030807Sache grp = NULL; 78130807Sache if ( sep->se_group != NULL 78230807Sache && (grp = getgrnam(sep->se_group)) == NULL 78330807Sache ) { 78430807Sache syslog(LOG_ERR, 78556482Scharnier "%s/%s: %s: no such group", 78630807Sache sep->se_service, sep->se_proto, 78730807Sache sep->se_group); 78830807Sache if (sep->se_socktype != SOCK_STREAM) 78930807Sache recv(0, buf, sizeof (buf), 0); 79030807Sache _exit(EX_NOUSER); 79130807Sache } 79230807Sache if (grp != NULL) 79330807Sache pwd->pw_gid = grp->gr_gid; 79421640Speter#ifdef LOGIN_CAP 79530792Sache if ((lc = login_getclass(sep->se_class)) == NULL) { 79630792Sache /* error syslogged by getclass */ 79730792Sache syslog(LOG_ERR, 79830792Sache "%s/%s: %s: login class error", 79937850Sache sep->se_service, sep->se_proto, 80037850Sache sep->se_class); 80130792Sache if (sep->se_socktype != SOCK_STREAM) 80230792Sache recv(0, buf, sizeof (buf), 0); 80330792Sache _exit(EX_NOUSER); 80430792Sache } 80521640Speter#endif 80612024Speter if (setsid() < 0) { 80712024Speter syslog(LOG_ERR, 80812024Speter "%s: can't setsid(): %m", 80912024Speter sep->se_service); 81019617Sjulian /* _exit(EX_OSERR); not fatal yet */ 81112024Speter } 81221640Speter#ifdef LOGIN_CAP 81321640Speter if (setusercontext(lc, pwd, pwd->pw_uid, 81421640Speter LOGIN_SETALL) != 0) { 81521640Speter syslog(LOG_ERR, 81621640Speter "%s: can't setusercontext(..%s..): %m", 81721640Speter sep->se_service, sep->se_user); 81821640Speter _exit(EX_OSERR); 81921640Speter } 82021640Speter#else 8211553Srgrimes if (pwd->pw_uid) { 82212024Speter if (setlogin(sep->se_user) < 0) { 82312024Speter syslog(LOG_ERR, 82412024Speter "%s: can't setlogin(%s): %m", 82512024Speter sep->se_service, sep->se_user); 82619617Sjulian /* _exit(EX_OSERR); not yet */ 82712024Speter } 8281553Srgrimes if (setgid(pwd->pw_gid) < 0) { 8291553Srgrimes syslog(LOG_ERR, 8308857Srgrimes "%s: can't set gid %d: %m", 8311553Srgrimes sep->se_service, pwd->pw_gid); 83219617Sjulian _exit(EX_OSERR); 8331553Srgrimes } 8341553Srgrimes (void) initgroups(pwd->pw_name, 8351553Srgrimes pwd->pw_gid); 8361553Srgrimes if (setuid(pwd->pw_uid) < 0) { 8371553Srgrimes syslog(LOG_ERR, 8388857Srgrimes "%s: can't set uid %d: %m", 8391553Srgrimes sep->se_service, pwd->pw_uid); 84019617Sjulian _exit(EX_OSERR); 8411553Srgrimes } 8421553Srgrimes } 84321640Speter#endif 84435948Sbde sigaction(SIGPIPE, &sapipe, 84535948Sbde (struct sigaction *)0); 8461553Srgrimes execv(sep->se_server, sep->se_argv); 84745089Smarkm syslog(LOG_ERR, 84845089Smarkm "cannot execute %s: %m", sep->se_server); 8491553Srgrimes if (sep->se_socktype != SOCK_STREAM) 8501553Srgrimes recv(0, buf, sizeof (buf), 0); 8511553Srgrimes } 85247972Ssheldonh if (dofork) 85347972Ssheldonh _exit(0); 8541553Srgrimes } 85519618Sjulian if (sep->se_accept && sep->se_socktype == SOCK_STREAM) 8561553Srgrimes close(ctrl); 8571553Srgrimes } 8581553Srgrimes } 8591553Srgrimes} 8601553Srgrimes 86119618Sjulian/* 86242122Sdes * Add a signal flag to the signal flag queue for later handling 86342122Sdes */ 86442122Sdes 86578694Sdwmalonevoid 86698558Sjmallettflag_signal(int c) 86742122Sdes{ 86869546Sdwmalone char ch = c; 86969546Sdwmalone 87069546Sdwmalone if (write(signalpipe[1], &ch, 1) != 1) { 87142250Sdes syslog(LOG_ERR, "write: %m"); 87248985Ssheldonh _exit(EX_OSERR); 87342250Sdes } 87442122Sdes} 87542122Sdes 87642122Sdes/* 87719618Sjulian * Record a new child pid for this service. If we've reached the 87819618Sjulian * limit on children, then stop accepting incoming requests. 87919618Sjulian */ 88019618Sjulian 8811553Srgrimesvoid 88219618Sjulianaddchild(struct servtab *sep, pid_t pid) 88319618Sjulian{ 88464197Sdwmalone if (sep->se_maxchild <= 0) 88564197Sdwmalone return; 88619618Sjulian#ifdef SANITY_CHECK 88719618Sjulian if (sep->se_numchild >= sep->se_maxchild) { 88819618Sjulian syslog(LOG_ERR, "%s: %d >= %d", 88919618Sjulian __FUNCTION__, sep->se_numchild, sep->se_maxchild); 89019618Sjulian exit(EX_SOFTWARE); 89119618Sjulian } 89219618Sjulian#endif 89319618Sjulian sep->se_pids[sep->se_numchild++] = pid; 89419618Sjulian if (sep->se_numchild == sep->se_maxchild) 89519618Sjulian disable(sep); 89619618Sjulian} 89719618Sjulian 89819618Sjulian/* 89919618Sjulian * Some child process has exited. See if it's on somebody's list. 90019618Sjulian */ 90119618Sjulian 90219618Sjulianvoid 90398561Sjmallettflag_reapchild(int signo __unused) 9041553Srgrimes{ 90542250Sdes flag_signal('C'); 90642122Sdes} 90742122Sdes 90842122Sdesvoid 90998558Sjmallettreapchild(void) 91042122Sdes{ 91119618Sjulian int k, status; 9121553Srgrimes pid_t pid; 9131553Srgrimes struct servtab *sep; 9141553Srgrimes 9151553Srgrimes for (;;) { 9161553Srgrimes pid = wait3(&status, WNOHANG, (struct rusage *)0); 9171553Srgrimes if (pid <= 0) 9181553Srgrimes break; 9191553Srgrimes if (debug) 92029602Scharnier warnx("%d reaped, status %#x", pid, status); 92119618Sjulian for (sep = servtab; sep; sep = sep->se_next) { 92219618Sjulian for (k = 0; k < sep->se_numchild; k++) 92319618Sjulian if (sep->se_pids[k] == pid) 92419618Sjulian break; 92519618Sjulian if (k == sep->se_numchild) 92619618Sjulian continue; 92719618Sjulian if (sep->se_numchild == sep->se_maxchild) 92819618Sjulian enable(sep); 92919618Sjulian sep->se_pids[k] = sep->se_pids[--sep->se_numchild]; 93019618Sjulian if (status) 93119618Sjulian syslog(LOG_WARNING, 93219618Sjulian "%s[%d]: exit status 0x%x", 93319618Sjulian sep->se_server, pid, status); 93419618Sjulian break; 93519618Sjulian } 936101474Sume reapchild_conn(pid); 9371553Srgrimes } 9381553Srgrimes} 9391553Srgrimes 9401553Srgrimesvoid 94198561Sjmallettflag_config(int signo __unused) 9421553Srgrimes{ 94342250Sdes flag_signal('H'); 94442122Sdes} 94542122Sdes 94678694Sdwmalonevoid 94798558Sjmallettconfig(void) 94842122Sdes{ 94919618Sjulian struct servtab *sep, *new, **sepp; 95042122Sdes long omask; 951100127Salfred int new_nomapped; 9521553Srgrimes 9531553Srgrimes if (!setconfig()) { 9541553Srgrimes syslog(LOG_ERR, "%s: %m", CONFIG); 9551553Srgrimes return; 9561553Srgrimes } 9571553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 9581553Srgrimes sep->se_checked = 0; 95919618Sjulian while ((new = getconfigent())) { 96030807Sache if (getpwnam(new->se_user) == NULL) { 9611553Srgrimes syslog(LOG_ERR, 96256482Scharnier "%s/%s: no such user '%s', service ignored", 96319618Sjulian new->se_service, new->se_proto, new->se_user); 9641553Srgrimes continue; 9651553Srgrimes } 96630807Sache if (new->se_group && getgrnam(new->se_group) == NULL) { 96730807Sache syslog(LOG_ERR, 96856482Scharnier "%s/%s: no such group '%s', service ignored", 96930807Sache new->se_service, new->se_proto, new->se_group); 97030807Sache continue; 97130807Sache } 97230792Sache#ifdef LOGIN_CAP 97330792Sache if (login_getclass(new->se_class) == NULL) { 97430792Sache /* error syslogged by getclass */ 97530792Sache syslog(LOG_ERR, 97637850Sache "%s/%s: %s: login class error, service ignored", 97737850Sache new->se_service, new->se_proto, new->se_class); 97830792Sache continue; 97930792Sache } 98030792Sache#endif 981100127Salfred new_nomapped = new->se_nomapped; 9821553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 98319618Sjulian if (strcmp(sep->se_service, new->se_service) == 0 && 98456590Sshin strcmp(sep->se_proto, new->se_proto) == 0 && 985100127Salfred sep->se_rpc == new->se_rpc && 98678356Sdwmalone sep->se_socktype == new->se_socktype && 98756590Sshin sep->se_family == new->se_family) 9881553Srgrimes break; 9891553Srgrimes if (sep != 0) { 9901553Srgrimes int i; 9911553Srgrimes 99298611Sjmallett#define SWAP(t,a, b) { t c = a; a = b; b = c; } 99342122Sdes omask = sigblock(SIGBLOCK); 99456590Sshin if (sep->se_nomapped != new->se_nomapped) { 995100127Salfred /* for rpc keep old nommaped till unregister */ 996100127Salfred if (!sep->se_rpc) 997100127Salfred sep->se_nomapped = new->se_nomapped; 99856590Sshin sep->se_reset = 1; 99956590Sshin } 100019618Sjulian /* copy over outstanding child pids */ 100164197Sdwmalone if (sep->se_maxchild > 0 && new->se_maxchild > 0) { 100219618Sjulian new->se_numchild = sep->se_numchild; 100319618Sjulian if (new->se_numchild > new->se_maxchild) 100419618Sjulian new->se_numchild = new->se_maxchild; 100519618Sjulian memcpy(new->se_pids, sep->se_pids, 100619618Sjulian new->se_numchild * sizeof(*new->se_pids)); 100719618Sjulian } 100898611Sjmallett SWAP(pid_t *, sep->se_pids, new->se_pids); 100919618Sjulian sep->se_maxchild = new->se_maxchild; 101019618Sjulian sep->se_numchild = new->se_numchild; 101130847Sdima sep->se_maxcpm = new->se_maxcpm; 1012101474Sume resize_conn(sep, new->se_maxperip); 1013101474Sume sep->se_maxperip = new->se_maxperip; 101466544Sdwmalone sep->se_bi = new->se_bi; 101519618Sjulian /* might need to turn on or off service now */ 101619618Sjulian if (sep->se_fd >= 0) { 101764197Sdwmalone if (sep->se_maxchild > 0 101819618Sjulian && sep->se_numchild == sep->se_maxchild) { 101919618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 102019618Sjulian disable(sep); 102119618Sjulian } else { 102219618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) 102319618Sjulian enable(sep); 102419618Sjulian } 102519618Sjulian } 102619618Sjulian sep->se_accept = new->se_accept; 102798611Sjmallett SWAP(char *, sep->se_user, new->se_user); 102898611Sjmallett SWAP(char *, sep->se_group, new->se_group); 102930792Sache#ifdef LOGIN_CAP 103098611Sjmallett SWAP(char *, sep->se_class, new->se_class); 103130792Sache#endif 103298611Sjmallett SWAP(char *, sep->se_server, new->se_server); 103398611Sjmallett SWAP(char *, sep->se_server_name, new->se_server_name); 10341553Srgrimes for (i = 0; i < MAXARGV; i++) 103598611Sjmallett SWAP(char *, sep->se_argv[i], new->se_argv[i]); 103656590Sshin#ifdef IPSEC 103798611Sjmallett SWAP(char *, sep->se_policy, new->se_policy); 103856590Sshin ipsecsetup(sep); 103956590Sshin#endif 104042122Sdes sigsetmask(omask); 104119618Sjulian freeconfig(new); 10421553Srgrimes if (debug) 10431553Srgrimes print_service("REDO", sep); 10441553Srgrimes } else { 104519618Sjulian sep = enter(new); 10461553Srgrimes if (debug) 10471553Srgrimes print_service("ADD ", sep); 10481553Srgrimes } 10491553Srgrimes sep->se_checked = 1; 10501553Srgrimes if (ISMUX(sep)) { 10511553Srgrimes sep->se_fd = -1; 10521553Srgrimes continue; 10531553Srgrimes } 105456590Sshin switch (sep->se_family) { 105556590Sshin case AF_INET: 105656590Sshin if (no_v4bind != 0) { 105756590Sshin sep->se_fd = -1; 105856590Sshin continue; 105956590Sshin } 106056590Sshin break; 106156590Sshin#ifdef INET6 106256590Sshin case AF_INET6: 106356590Sshin if (no_v6bind != 0) { 106456590Sshin sep->se_fd = -1; 106556590Sshin continue; 106656590Sshin } 106756590Sshin break; 106856590Sshin#endif 106956590Sshin } 10702657Scsgr if (!sep->se_rpc) { 107178356Sdwmalone if (sep->se_family != AF_UNIX) { 107278356Sdwmalone sp = getservbyname(sep->se_service, sep->se_proto); 107378356Sdwmalone if (sp == 0) { 107478356Sdwmalone syslog(LOG_ERR, "%s/%s: unknown service", 107578356Sdwmalone sep->se_service, sep->se_proto); 107678356Sdwmalone sep->se_checked = 0; 107778356Sdwmalone continue; 107878356Sdwmalone } 10792657Scsgr } 108056590Sshin switch (sep->se_family) { 108156590Sshin case AF_INET: 108256590Sshin if (sp->s_port != sep->se_ctrladdr4.sin_port) { 108356590Sshin sep->se_ctrladdr4.sin_port = 108456590Sshin sp->s_port; 108556590Sshin sep->se_reset = 1; 108656590Sshin } 108756590Sshin break; 108856590Sshin#ifdef INET6 108956590Sshin case AF_INET6: 109056590Sshin if (sp->s_port != 109156590Sshin sep->se_ctrladdr6.sin6_port) { 109256590Sshin sep->se_ctrladdr6.sin6_port = 109356590Sshin sp->s_port; 109456590Sshin sep->se_reset = 1; 109556590Sshin } 109656590Sshin break; 109756590Sshin#endif 10982657Scsgr } 109956590Sshin if (sep->se_reset != 0 && sep->se_fd >= 0) 110056590Sshin close_sep(sep); 11012657Scsgr } else { 11022657Scsgr rpc = getrpcbyname(sep->se_service); 11032657Scsgr if (rpc == 0) { 110452219Scharnier syslog(LOG_ERR, "%s/%s unknown RPC service", 11052657Scsgr sep->se_service, sep->se_proto); 11062657Scsgr if (sep->se_fd != -1) 11072657Scsgr (void) close(sep->se_fd); 11082657Scsgr sep->se_fd = -1; 11092657Scsgr continue; 11102657Scsgr } 1111100127Salfred if (sep->se_reset != 0 || 1112100127Salfred rpc->r_number != sep->se_rpc_prog) { 11132657Scsgr if (sep->se_rpc_prog) 11142657Scsgr unregisterrpc(sep); 11152657Scsgr sep->se_rpc_prog = rpc->r_number; 11162657Scsgr if (sep->se_fd != -1) 11172657Scsgr (void) close(sep->se_fd); 11182657Scsgr sep->se_fd = -1; 11192657Scsgr } 1120100127Salfred sep->se_nomapped = new_nomapped; 11211553Srgrimes } 1122100127Salfred sep->se_reset = 0; 11231553Srgrimes if (sep->se_fd == -1) 11241553Srgrimes setup(sep); 11251553Srgrimes } 11261553Srgrimes endconfig(); 11271553Srgrimes /* 11281553Srgrimes * Purge anything not looked at above. 11291553Srgrimes */ 113042122Sdes omask = sigblock(SIGBLOCK); 11311553Srgrimes sepp = &servtab; 113219617Sjulian while ((sep = *sepp)) { 11331553Srgrimes if (sep->se_checked) { 11341553Srgrimes sepp = &sep->se_next; 11351553Srgrimes continue; 11361553Srgrimes } 11371553Srgrimes *sepp = sep->se_next; 11381553Srgrimes if (sep->se_fd >= 0) 11391553Srgrimes close_sep(sep); 11401553Srgrimes if (debug) 11411553Srgrimes print_service("FREE", sep); 11422657Scsgr if (sep->se_rpc && sep->se_rpc_prog > 0) 11432657Scsgr unregisterrpc(sep); 11441553Srgrimes freeconfig(sep); 114571399Sdwmalone free(sep); 11461553Srgrimes } 114742122Sdes (void) sigsetmask(omask); 11481553Srgrimes} 11491553Srgrimes 11501553Srgrimesvoid 115198558Sjmallettunregisterrpc(struct servtab *sep) 11522657Scsgr{ 115378694Sdwmalone u_int i; 11542657Scsgr struct servtab *sepp; 115542122Sdes long omask; 1156100127Salfred struct netconfig *netid4, *netid6; 11572657Scsgr 115842122Sdes omask = sigblock(SIGBLOCK); 1159100127Salfred netid4 = sep->se_socktype == SOCK_DGRAM ? udpconf : tcpconf; 1160100127Salfred netid6 = sep->se_socktype == SOCK_DGRAM ? udp6conf : tcp6conf; 1161100127Salfred if (sep->se_family == AF_INET) 1162100127Salfred netid6 = NULL; 1163100127Salfred else if (sep->se_nomapped) 1164100127Salfred netid4 = NULL; 1165100127Salfred /* 1166100127Salfred * Conflict if same prog and protocol - In that case one should look 1167100127Salfred * to versions, but it is not interesting: having separate servers for 1168100127Salfred * different versions does not work well. 1169100127Salfred * Therefore one do not unregister if there is a conflict. 1170100127Salfred * There is also transport conflict if destroying INET when INET46 1171100127Salfred * exists, or destroying INET46 when INET exists 1172100127Salfred */ 11732657Scsgr for (sepp = servtab; sepp; sepp = sepp->se_next) { 11742657Scsgr if (sepp == sep) 11752657Scsgr continue; 1176100127Salfred if (sepp->se_checked == 0 || 11772657Scsgr !sepp->se_rpc || 1178100127Salfred strcmp(sep->se_proto, sepp->se_proto) != 0 || 11792657Scsgr sep->se_rpc_prog != sepp->se_rpc_prog) 11802657Scsgr continue; 1181100127Salfred if (sepp->se_family == AF_INET) 1182100127Salfred netid4 = NULL; 1183100127Salfred if (sepp->se_family == AF_INET6) { 1184100127Salfred netid6 = NULL; 1185100127Salfred if (!sep->se_nomapped) 1186100127Salfred netid4 = NULL; 1187100127Salfred } 1188100127Salfred if (netid4 == NULL && netid6 == NULL) 1189100127Salfred return; 11902657Scsgr } 11912657Scsgr if (debug) 11922657Scsgr print_service("UNREG", sep); 1193100127Salfred for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { 1194100127Salfred if (netid4) 1195100127Salfred rpcb_unset(sep->se_rpc_prog, i, netid4); 1196100127Salfred if (netid6) 1197100127Salfred rpcb_unset(sep->se_rpc_prog, i, netid6); 1198100127Salfred } 11992657Scsgr if (sep->se_fd != -1) 12002657Scsgr (void) close(sep->se_fd); 12012657Scsgr sep->se_fd = -1; 120242122Sdes (void) sigsetmask(omask); 12032657Scsgr} 12042657Scsgr 12052657Scsgrvoid 120698561Sjmallettflag_retry(int signo __unused) 12071553Srgrimes{ 120842250Sdes flag_signal('A'); 120942122Sdes} 121042122Sdes 121142122Sdesvoid 121298558Sjmallettretry(void) 121342122Sdes{ 12141553Srgrimes struct servtab *sep; 12151553Srgrimes 12161553Srgrimes timingout = 0; 12171553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 121819617Sjulian if (sep->se_fd == -1 && !ISMUX(sep)) 12191553Srgrimes setup(sep); 12201553Srgrimes} 12211553Srgrimes 12221553Srgrimesvoid 122398558Sjmallettsetup(struct servtab *sep) 12241553Srgrimes{ 12251553Srgrimes int on = 1; 12261553Srgrimes 122756590Sshin if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { 12281553Srgrimes if (debug) 122929602Scharnier warn("socket failed on %s/%s", 123029602Scharnier sep->se_service, sep->se_proto); 12311553Srgrimes syslog(LOG_ERR, "%s/%s: socket: %m", 12321553Srgrimes sep->se_service, sep->se_proto); 12331553Srgrimes return; 12341553Srgrimes } 12351553Srgrimes#define turnon(fd, opt) \ 12361553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) 12371553Srgrimes if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && 12381553Srgrimes turnon(sep->se_fd, SO_DEBUG) < 0) 12391553Srgrimes syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 12401553Srgrimes if (turnon(sep->se_fd, SO_REUSEADDR) < 0) 12411553Srgrimes syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); 124225253Swollman#ifdef SO_PRIVSTATE 124313956Swollman if (turnon(sep->se_fd, SO_PRIVSTATE) < 0) 124413956Swollman syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m"); 124525253Swollman#endif 124656590Sshin /* tftpd opens a new connection then needs more infos */ 124756590Sshin if ((sep->se_family == AF_INET6) && 124856590Sshin (strcmp(sep->se_proto, "udp") == 0) && 124956590Sshin (sep->se_accept == 0) && 125056590Sshin (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_PKTINFO, 125156590Sshin (char *)&on, sizeof (on)) < 0)) 125256590Sshin syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m"); 125358935Sume if (sep->se_family == AF_INET6) { 125458935Sume int flag = sep->se_nomapped ? 1 : 0; 1255100505Sume if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_V6ONLY, 125658935Sume (char *)&flag, sizeof (flag)) < 0) 1257100505Sume syslog(LOG_ERR, "setsockopt (IPV6_V6ONLY): %m"); 125858935Sume } 12591553Srgrimes#undef turnon 126036042Sguido if (sep->se_type == TTCP_TYPE) 126136042Sguido if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH, 126236042Sguido (char *)&on, sizeof (on)) < 0) 126336042Sguido syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m"); 126456590Sshin#ifdef IPV6_FAITH 126556590Sshin if (sep->se_type == FAITH_TYPE) { 126656590Sshin if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH, &on, 126756590Sshin sizeof(on)) < 0) { 126856590Sshin syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m"); 126956590Sshin } 127056590Sshin } 127156590Sshin#endif 127256590Sshin#ifdef IPSEC 127356590Sshin ipsecsetup(sep); 127456590Sshin#endif 127578356Sdwmalone if (sep->se_family == AF_UNIX) { 127678356Sdwmalone (void) unlink(sep->se_ctrladdr_un.sun_path); 127778356Sdwmalone umask(0777); /* Make socket with conservative permissions */ 127878356Sdwmalone } 12791553Srgrimes if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, 128056590Sshin sep->se_ctrladdr_size) < 0) { 12811553Srgrimes if (debug) 128229602Scharnier warn("bind failed on %s/%s", 128329602Scharnier sep->se_service, sep->se_proto); 12841553Srgrimes syslog(LOG_ERR, "%s/%s: bind: %m", 12851553Srgrimes sep->se_service, sep->se_proto); 12861553Srgrimes (void) close(sep->se_fd); 12871553Srgrimes sep->se_fd = -1; 12881553Srgrimes if (!timingout) { 12891553Srgrimes timingout = 1; 12901553Srgrimes alarm(RETRYTIME); 12911553Srgrimes } 129278356Sdwmalone if (sep->se_family == AF_UNIX) 129378356Sdwmalone umask(mask); 12941553Srgrimes return; 12951553Srgrimes } 129678356Sdwmalone if (sep->se_family == AF_UNIX) { 129778356Sdwmalone /* Ick - fch{own,mod} don't work on Unix domain sockets */ 129878356Sdwmalone if (chown(sep->se_service, sep->se_sockuid, sep->se_sockgid) < 0) 129978356Sdwmalone syslog(LOG_ERR, "chown socket: %m"); 130078356Sdwmalone if (chmod(sep->se_service, sep->se_sockmode) < 0) 130178356Sdwmalone syslog(LOG_ERR, "chmod socket: %m"); 130278356Sdwmalone umask(mask); 130378356Sdwmalone } 13042657Scsgr if (sep->se_rpc) { 130578694Sdwmalone u_int i; 130671399Sdwmalone socklen_t len = sep->se_ctrladdr_size; 1307100127Salfred struct netconfig *netid, *netid2 = NULL; 1308100127Salfred struct sockaddr_in sock; 1309100127Salfred struct netbuf nbuf, nbuf2; 13102657Scsgr 13118857Srgrimes if (getsockname(sep->se_fd, 13122657Scsgr (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){ 13132657Scsgr syslog(LOG_ERR, "%s/%s: getsockname: %m", 13142657Scsgr sep->se_service, sep->se_proto); 13152657Scsgr (void) close(sep->se_fd); 13162657Scsgr sep->se_fd = -1; 13178857Srgrimes return; 13182657Scsgr } 1319100127Salfred nbuf.buf = &sep->se_ctrladdr; 1320100127Salfred nbuf.len = sep->se_ctrladdr.sa_len; 1321100127Salfred if (sep->se_family == AF_INET) 1322100127Salfred netid = sep->se_socktype==SOCK_DGRAM? udpconf:tcpconf; 1323100127Salfred else { 1324100127Salfred netid = sep->se_socktype==SOCK_DGRAM? udp6conf:tcp6conf; 1325100127Salfred if (!sep->se_nomapped) { /* INET and INET6 */ 1326100127Salfred netid2 = netid==udp6conf? udpconf:tcpconf; 1327100127Salfred memset(&sock, 0, sizeof sock); /* ADDR_ANY */ 1328100127Salfred nbuf2.buf = &sock; 1329100127Salfred nbuf2.len = sock.sin_len = sizeof sock; 1330100127Salfred sock.sin_family = AF_INET; 1331100127Salfred sock.sin_port = sep->se_ctrladdr6.sin6_port; 1332100127Salfred } 1333100127Salfred } 13342657Scsgr if (debug) 13352657Scsgr print_service("REG ", sep); 13362657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { 1337100127Salfred rpcb_unset(sep->se_rpc_prog, i, netid); 1338100127Salfred rpcb_set(sep->se_rpc_prog, i, netid, &nbuf); 1339100127Salfred if (netid2) { 1340100127Salfred rpcb_unset(sep->se_rpc_prog, i, netid2); 1341100127Salfred rpcb_set(sep->se_rpc_prog, i, netid2, &nbuf2); 1342100127Salfred } 13432657Scsgr } 13442657Scsgr } 13451553Srgrimes if (sep->se_socktype == SOCK_STREAM) 134617197Sdg listen(sep->se_fd, 64); 134719618Sjulian enable(sep); 13481553Srgrimes if (debug) { 134929602Scharnier warnx("registered %s on %d", 13501553Srgrimes sep->se_server, sep->se_fd); 13511553Srgrimes } 13521553Srgrimes} 13531553Srgrimes 135456590Sshin#ifdef IPSEC 135556590Sshinvoid 135656590Sshinipsecsetup(sep) 135756590Sshin struct servtab *sep; 135856590Sshin{ 135956590Sshin char *buf; 136056590Sshin char *policy_in = NULL; 136156590Sshin char *policy_out = NULL; 136256590Sshin int level; 136356590Sshin int opt; 136456590Sshin 136556590Sshin switch (sep->se_family) { 136656590Sshin case AF_INET: 136756590Sshin level = IPPROTO_IP; 136856590Sshin opt = IP_IPSEC_POLICY; 136956590Sshin break; 137056590Sshin#ifdef INET6 137156590Sshin case AF_INET6: 137256590Sshin level = IPPROTO_IPV6; 137356590Sshin opt = IPV6_IPSEC_POLICY; 137456590Sshin break; 137556590Sshin#endif 137656590Sshin default: 137756590Sshin return; 137856590Sshin } 137956590Sshin 138056590Sshin if (!sep->se_policy || sep->se_policy[0] == '\0') { 138178694Sdwmalone static char def_in[] = "in entrust", def_out[] = "out entrust"; 138278694Sdwmalone policy_in = def_in; 138378694Sdwmalone policy_out = def_out; 138456590Sshin } else { 138556590Sshin if (!strncmp("in", sep->se_policy, 2)) 138656590Sshin policy_in = sep->se_policy; 138756590Sshin else if (!strncmp("out", sep->se_policy, 3)) 138856590Sshin policy_out = sep->se_policy; 138956590Sshin else { 139056590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 139156590Sshin sep->se_policy); 139256590Sshin return; 139356590Sshin } 139456590Sshin } 139556590Sshin 139656590Sshin if (policy_in != NULL) { 139756590Sshin buf = ipsec_set_policy(policy_in, strlen(policy_in)); 139856590Sshin if (buf != NULL) { 139956590Sshin if (setsockopt(sep->se_fd, level, opt, 140056675Sshin buf, ipsec_get_policylen(buf)) < 0 && 140156759Sshin debug != 0) 140256759Sshin warnx("%s/%s: ipsec initialization failed; %s", 140356759Sshin sep->se_service, sep->se_proto, 140456759Sshin policy_in); 140556590Sshin free(buf); 140656590Sshin } else 140756590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 140856590Sshin policy_in); 140956590Sshin } 141056590Sshin if (policy_out != NULL) { 141156590Sshin buf = ipsec_set_policy(policy_out, strlen(policy_out)); 141256590Sshin if (buf != NULL) { 141356590Sshin if (setsockopt(sep->se_fd, level, opt, 141456675Sshin buf, ipsec_get_policylen(buf)) < 0 && 141556759Sshin debug != 0) 141656759Sshin warnx("%s/%s: ipsec initialization failed; %s", 141756759Sshin sep->se_service, sep->se_proto, 141856759Sshin policy_out); 141956590Sshin free(buf); 142056590Sshin } else 142156590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 142256590Sshin policy_out); 142356590Sshin } 142456590Sshin} 142556590Sshin#endif 142656590Sshin 14271553Srgrimes/* 14281553Srgrimes * Finish with a service and its socket. 14291553Srgrimes */ 14301553Srgrimesvoid 143198558Sjmallettclose_sep(struct servtab *sep) 14321553Srgrimes{ 14331553Srgrimes if (sep->se_fd >= 0) { 143419618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 143519618Sjulian disable(sep); 14361553Srgrimes (void) close(sep->se_fd); 14371553Srgrimes sep->se_fd = -1; 14381553Srgrimes } 14391553Srgrimes sep->se_count = 0; 144019618Sjulian sep->se_numchild = 0; /* forget about any existing children */ 14411553Srgrimes} 14421553Srgrimes 144348467Ssheldonhint 144498558Sjmallettmatchservent(const char *name1, const char *name2, const char *proto) 144548467Ssheldonh{ 144678356Sdwmalone char **alias, *p; 144748467Ssheldonh struct servent *se; 144848467Ssheldonh 144978356Sdwmalone if (strcmp(proto, "unix") == 0) { 145078356Sdwmalone if ((p = strrchr(name1, '/')) != NULL) 145178356Sdwmalone name1 = p + 1; 145278356Sdwmalone if ((p = strrchr(name2, '/')) != NULL) 145378356Sdwmalone name2 = p + 1; 145478356Sdwmalone } 145549026Sdes if (strcmp(name1, name2) == 0) 145649026Sdes return(1); 145748467Ssheldonh if ((se = getservbyname(name1, proto)) != NULL) { 145848467Ssheldonh if (strcmp(name2, se->s_name) == 0) 145948467Ssheldonh return(1); 146048467Ssheldonh for (alias = se->s_aliases; *alias; alias++) 146148467Ssheldonh if (strcmp(name2, *alias) == 0) 146248467Ssheldonh return(1); 146348467Ssheldonh } 146448467Ssheldonh return(0); 146548467Ssheldonh} 146648467Ssheldonh 14671553Srgrimesstruct servtab * 146898558Sjmallettenter(struct servtab *cp) 14691553Srgrimes{ 14701553Srgrimes struct servtab *sep; 147142122Sdes long omask; 14721553Srgrimes 14731553Srgrimes sep = (struct servtab *)malloc(sizeof (*sep)); 14741553Srgrimes if (sep == (struct servtab *)0) { 147549102Ssheldonh syslog(LOG_ERR, "malloc: %m"); 147619617Sjulian exit(EX_OSERR); 14771553Srgrimes } 14781553Srgrimes *sep = *cp; 14791553Srgrimes sep->se_fd = -1; 148042122Sdes omask = sigblock(SIGBLOCK); 14811553Srgrimes sep->se_next = servtab; 14821553Srgrimes servtab = sep; 148342122Sdes sigsetmask(omask); 14841553Srgrimes return (sep); 14851553Srgrimes} 14861553Srgrimes 148719618Sjulianvoid 148898558Sjmallettenable(struct servtab *sep) 148919618Sjulian{ 149019618Sjulian if (debug) 149129602Scharnier warnx( 149219618Sjulian "enabling %s, fd %d", sep->se_service, sep->se_fd); 149319618Sjulian#ifdef SANITY_CHECK 149419618Sjulian if (sep->se_fd < 0) { 149519618Sjulian syslog(LOG_ERR, 149619618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 149719618Sjulian exit(EX_SOFTWARE); 149819618Sjulian } 149919618Sjulian if (ISMUX(sep)) { 150019618Sjulian syslog(LOG_ERR, 150119618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 150219618Sjulian exit(EX_SOFTWARE); 150319618Sjulian } 150419618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) { 150519618Sjulian syslog(LOG_ERR, 150619618Sjulian "%s: %s: not off", __FUNCTION__, sep->se_service); 150719618Sjulian exit(EX_SOFTWARE); 150819618Sjulian } 150948991Ssheldonh nsock++; 151019618Sjulian#endif 151119618Sjulian FD_SET(sep->se_fd, &allsock); 151219618Sjulian if (sep->se_fd > maxsock) 151319618Sjulian maxsock = sep->se_fd; 151419618Sjulian} 151519618Sjulian 151619618Sjulianvoid 151798558Sjmallettdisable(struct servtab *sep) 151819618Sjulian{ 151919618Sjulian if (debug) 152029602Scharnier warnx( 152119618Sjulian "disabling %s, fd %d", sep->se_service, sep->se_fd); 152219618Sjulian#ifdef SANITY_CHECK 152319618Sjulian if (sep->se_fd < 0) { 152419618Sjulian syslog(LOG_ERR, 152519618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 152619618Sjulian exit(EX_SOFTWARE); 152719618Sjulian } 152819618Sjulian if (ISMUX(sep)) { 152919618Sjulian syslog(LOG_ERR, 153019618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 153119618Sjulian exit(EX_SOFTWARE); 153219618Sjulian } 153319618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) { 153419618Sjulian syslog(LOG_ERR, 153519618Sjulian "%s: %s: not on", __FUNCTION__, sep->se_service); 153619618Sjulian exit(EX_SOFTWARE); 153719618Sjulian } 153819618Sjulian if (nsock == 0) { 153919618Sjulian syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 154019618Sjulian exit(EX_SOFTWARE); 154119618Sjulian } 154248991Ssheldonh nsock--; 154319618Sjulian#endif 154419618Sjulian FD_CLR(sep->se_fd, &allsock); 154519618Sjulian if (sep->se_fd == maxsock) 154619618Sjulian maxsock--; 154719618Sjulian} 154819618Sjulian 15491553SrgrimesFILE *fconfig = NULL; 15501553Srgrimesstruct servtab serv; 15511553Srgrimeschar line[LINE_MAX]; 15521553Srgrimes 15531553Srgrimesint 155498558Sjmallettsetconfig(void) 15551553Srgrimes{ 15561553Srgrimes 15571553Srgrimes if (fconfig != NULL) { 15581553Srgrimes fseek(fconfig, 0L, SEEK_SET); 15591553Srgrimes return (1); 15601553Srgrimes } 15611553Srgrimes fconfig = fopen(CONFIG, "r"); 15621553Srgrimes return (fconfig != NULL); 15631553Srgrimes} 15641553Srgrimes 15651553Srgrimesvoid 156698558Sjmallettendconfig(void) 15671553Srgrimes{ 15681553Srgrimes if (fconfig) { 15691553Srgrimes (void) fclose(fconfig); 15701553Srgrimes fconfig = NULL; 15711553Srgrimes } 15721553Srgrimes} 15731553Srgrimes 15741553Srgrimesstruct servtab * 157598558Sjmallettgetconfigent(void) 15761553Srgrimes{ 15771553Srgrimes struct servtab *sep = &serv; 15781553Srgrimes int argc; 157919618Sjulian char *cp, *arg, *s; 15802657Scsgr char *versp; 15811553Srgrimes static char TCPMUX_TOKEN[] = "tcpmux/"; 15821553Srgrimes#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1) 158356590Sshin#ifdef IPSEC 158456590Sshin char *policy = NULL; 158556590Sshin#endif 158656590Sshin int v4bind = 0; 158756590Sshin#ifdef INET6 158856590Sshin int v6bind = 0; 158956590Sshin#endif 1590101474Sume int i; 15911553Srgrimes 15921553Srgrimesmore: 159356590Sshin while ((cp = nextline(fconfig)) != NULL) { 159456590Sshin#ifdef IPSEC 159556590Sshin /* lines starting with #@ is not a comment, but the policy */ 159656590Sshin if (cp[0] == '#' && cp[1] == '@') { 159756590Sshin char *p; 159856590Sshin for (p = cp + 2; p && *p && isspace(*p); p++) 159956590Sshin ; 160056590Sshin if (*p == '\0') { 160156590Sshin if (policy) 160256590Sshin free(policy); 160356590Sshin policy = NULL; 160456590Sshin } else if (ipsec_get_policylen(p) >= 0) { 160556590Sshin if (policy) 160656590Sshin free(policy); 160756590Sshin policy = newstr(p); 160856590Sshin } else { 160956590Sshin syslog(LOG_ERR, 161056590Sshin "%s: invalid ipsec policy \"%s\"", 161156590Sshin CONFIG, p); 161256590Sshin exit(EX_CONFIG); 161356590Sshin } 161456590Sshin } 161556590Sshin#endif 161656590Sshin if (*cp == '#' || *cp == '\0') 161756590Sshin continue; 161856590Sshin break; 161956590Sshin } 16201553Srgrimes if (cp == NULL) 16211553Srgrimes return ((struct servtab *)0); 16221553Srgrimes /* 16231553Srgrimes * clear the static buffer, since some fields (se_ctrladdr, 16241553Srgrimes * for example) don't get initialized here. 16251553Srgrimes */ 162671399Sdwmalone memset(sep, 0, sizeof *sep); 16271553Srgrimes arg = skip(&cp); 16281553Srgrimes if (cp == NULL) { 16291553Srgrimes /* got an empty line containing just blanks/tabs. */ 16301553Srgrimes goto more; 16311553Srgrimes } 163278356Sdwmalone if (arg[0] == ':') { /* :user:group:perm: */ 163378356Sdwmalone char *user, *group, *perm; 163478356Sdwmalone struct passwd *pw; 163578356Sdwmalone struct group *gr; 163678356Sdwmalone user = arg+1; 163778356Sdwmalone if ((group = strchr(user, ':')) == NULL) { 163878356Sdwmalone syslog(LOG_ERR, "no group after user '%s'", user); 163978356Sdwmalone goto more; 164078356Sdwmalone } 164178356Sdwmalone *group++ = '\0'; 164278356Sdwmalone if ((perm = strchr(group, ':')) == NULL) { 164378356Sdwmalone syslog(LOG_ERR, "no mode after group '%s'", group); 164478356Sdwmalone goto more; 164578356Sdwmalone } 164678356Sdwmalone *perm++ = '\0'; 164778356Sdwmalone if ((pw = getpwnam(user)) == NULL) { 164878356Sdwmalone syslog(LOG_ERR, "no such user '%s'", user); 164978356Sdwmalone goto more; 165078356Sdwmalone } 165178356Sdwmalone sep->se_sockuid = pw->pw_uid; 165278356Sdwmalone if ((gr = getgrnam(group)) == NULL) { 165378356Sdwmalone syslog(LOG_ERR, "no such user '%s'", group); 165478356Sdwmalone goto more; 165578356Sdwmalone } 165678356Sdwmalone sep->se_sockgid = gr->gr_gid; 165778356Sdwmalone sep->se_sockmode = strtol(perm, &arg, 8); 165878356Sdwmalone if (*arg != ':') { 165978356Sdwmalone syslog(LOG_ERR, "bad mode '%s'", perm); 166078356Sdwmalone goto more; 166178356Sdwmalone } 166278356Sdwmalone *arg++ = '\0'; 166378356Sdwmalone } else { 166478356Sdwmalone sep->se_sockuid = euid; 166578356Sdwmalone sep->se_sockgid = egid; 166678356Sdwmalone sep->se_sockmode = 0200; 166778356Sdwmalone } 16681553Srgrimes if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { 16691553Srgrimes char *c = arg + MUX_LEN; 16701553Srgrimes if (*c == '+') { 16711553Srgrimes sep->se_type = MUXPLUS_TYPE; 16721553Srgrimes c++; 16731553Srgrimes } else 16741553Srgrimes sep->se_type = MUX_TYPE; 16751553Srgrimes sep->se_service = newstr(c); 16761553Srgrimes } else { 16771553Srgrimes sep->se_service = newstr(arg); 16781553Srgrimes sep->se_type = NORM_TYPE; 16791553Srgrimes } 16801553Srgrimes arg = sskip(&cp); 16811553Srgrimes if (strcmp(arg, "stream") == 0) 16821553Srgrimes sep->se_socktype = SOCK_STREAM; 16831553Srgrimes else if (strcmp(arg, "dgram") == 0) 16841553Srgrimes sep->se_socktype = SOCK_DGRAM; 16851553Srgrimes else if (strcmp(arg, "rdm") == 0) 16861553Srgrimes sep->se_socktype = SOCK_RDM; 16871553Srgrimes else if (strcmp(arg, "seqpacket") == 0) 16881553Srgrimes sep->se_socktype = SOCK_SEQPACKET; 16891553Srgrimes else if (strcmp(arg, "raw") == 0) 16901553Srgrimes sep->se_socktype = SOCK_RAW; 16911553Srgrimes else 16921553Srgrimes sep->se_socktype = -1; 169336042Sguido 169436042Sguido arg = sskip(&cp); 169556590Sshin if (strncmp(arg, "tcp", 3) == 0) { 169656590Sshin sep->se_proto = newstr(strsep(&arg, "/")); 169756590Sshin if (arg != NULL) { 169856590Sshin if (strcmp(arg, "ttcp") == 0) 169956590Sshin sep->se_type = TTCP_TYPE; 170056590Sshin else if (strcmp(arg, "faith") == 0) 170156590Sshin sep->se_type = FAITH_TYPE; 170256590Sshin } 170377518Sume } else { 170477518Sume if (sep->se_type == NORM_TYPE && 170577518Sume strncmp(arg, "faith/", 6) == 0) { 170677518Sume arg += 6; 170777518Sume sep->se_type = FAITH_TYPE; 170877518Sume } 170936042Sguido sep->se_proto = newstr(arg); 171077518Sume } 17112657Scsgr if (strncmp(sep->se_proto, "rpc/", 4) == 0) { 171219237Sjoerg memmove(sep->se_proto, sep->se_proto + 4, 171319237Sjoerg strlen(sep->se_proto) + 1 - 4); 17142657Scsgr sep->se_rpc = 1; 17152657Scsgr sep->se_rpc_prog = sep->se_rpc_lowvers = 17162657Scsgr sep->se_rpc_lowvers = 0; 171756590Sshin memcpy(&sep->se_ctrladdr4, bind_sa4, 171856590Sshin sizeof(sep->se_ctrladdr4)); 17192657Scsgr if ((versp = rindex(sep->se_service, '/'))) { 17202657Scsgr *versp++ = '\0'; 1721102859Sdwmalone switch (sscanf(versp, "%u-%u", 17222657Scsgr &sep->se_rpc_lowvers, 17232657Scsgr &sep->se_rpc_highvers)) { 17242657Scsgr case 2: 17252657Scsgr break; 17262657Scsgr case 1: 17272657Scsgr sep->se_rpc_highvers = 17282657Scsgr sep->se_rpc_lowvers; 17292657Scsgr break; 17302657Scsgr default: 17318857Srgrimes syslog(LOG_ERR, 173252219Scharnier "bad RPC version specifier; %s", 17332657Scsgr sep->se_service); 17342657Scsgr freeconfig(sep); 17352657Scsgr goto more; 17362657Scsgr } 17372657Scsgr } 17382657Scsgr else { 17392657Scsgr sep->se_rpc_lowvers = 17402657Scsgr sep->se_rpc_highvers = 1; 17412657Scsgr } 17422657Scsgr } 174356590Sshin sep->se_nomapped = 0; 174456590Sshin while (isdigit(sep->se_proto[strlen(sep->se_proto) - 1])) { 174556590Sshin#ifdef INET6 174656590Sshin if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') { 174756590Sshin sep->se_proto[strlen(sep->se_proto) - 1] = '\0'; 174856590Sshin v6bind = 1; 174956590Sshin continue; 175056590Sshin } 175156590Sshin#endif 175256590Sshin if (sep->se_proto[strlen(sep->se_proto) - 1] == '4') { 175356590Sshin sep->se_proto[strlen(sep->se_proto) - 1] = '\0'; 175456590Sshin v4bind = 1; 175556590Sshin continue; 175656590Sshin } 175756590Sshin /* illegal version num */ 175856590Sshin syslog(LOG_ERR, "bad IP version for %s", sep->se_proto); 175956590Sshin freeconfig(sep); 176056590Sshin goto more; 176156590Sshin } 176278356Sdwmalone if (strcmp(sep->se_proto, "unix") == 0) { 176378356Sdwmalone sep->se_family = AF_UNIX; 176478356Sdwmalone } else 176556590Sshin#ifdef INET6 1766100127Salfred if (v6bind != 0 && no_v6bind != 0) { 1767100127Salfred syslog(LOG_INFO, "IPv6 bind is ignored for %s", 1768100127Salfred sep->se_service); 1769100127Salfred if (v4bind && no_v4bind == 0) 1770100127Salfred v6bind = 0; 1771100127Salfred else { 1772100127Salfred freeconfig(sep); 1773100127Salfred goto more; 1774100127Salfred } 1775100127Salfred } 177656590Sshin if (v6bind != 0) { 177756590Sshin sep->se_family = AF_INET6; 177856590Sshin if (v4bind == 0 || no_v4bind != 0) 177956590Sshin sep->se_nomapped = 1; 178057906Sshin } else 178156590Sshin#endif 178257906Sshin { /* default to v4 bind if not v6 bind */ 178356590Sshin if (no_v4bind != 0) { 178497293Sjwd syslog(LOG_NOTICE, "IPv4 bind is ignored for %s", 178556590Sshin sep->se_service); 178656590Sshin freeconfig(sep); 178756590Sshin goto more; 178856590Sshin } 178956590Sshin sep->se_family = AF_INET; 179056590Sshin } 179156590Sshin /* init ctladdr */ 179256590Sshin switch(sep->se_family) { 179356590Sshin case AF_INET: 179456590Sshin memcpy(&sep->se_ctrladdr4, bind_sa4, 179556590Sshin sizeof(sep->se_ctrladdr4)); 179656590Sshin sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr4); 179756590Sshin break; 179856590Sshin#ifdef INET6 179956590Sshin case AF_INET6: 180056590Sshin memcpy(&sep->se_ctrladdr6, bind_sa6, 180156590Sshin sizeof(sep->se_ctrladdr6)); 180256590Sshin sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr6); 180356590Sshin break; 180456590Sshin#endif 180578356Sdwmalone case AF_UNIX: 180678356Sdwmalone if (strlen(sep->se_service) >= sizeof(sep->se_ctrladdr_un.sun_path)) { 180778356Sdwmalone syslog(LOG_ERR, 180878356Sdwmalone "domain socket pathname too long for service %s", 180978356Sdwmalone sep->se_service); 181078356Sdwmalone goto more; 181178356Sdwmalone } 181278356Sdwmalone memset(&sep->se_ctrladdr, 0, sizeof(sep->se_ctrladdr)); 181378356Sdwmalone sep->se_ctrladdr_un.sun_family = sep->se_family; 181478356Sdwmalone sep->se_ctrladdr_un.sun_len = strlen(sep->se_service); 181578356Sdwmalone strcpy(sep->se_ctrladdr_un.sun_path, sep->se_service); 181678356Sdwmalone sep->se_ctrladdr_size = SUN_LEN(&sep->se_ctrladdr_un); 181756590Sshin } 18181553Srgrimes arg = sskip(&cp); 181919618Sjulian if (!strncmp(arg, "wait", 4)) 182019618Sjulian sep->se_accept = 0; 182119618Sjulian else if (!strncmp(arg, "nowait", 6)) 182219618Sjulian sep->se_accept = 1; 182319618Sjulian else { 182419618Sjulian syslog(LOG_ERR, 182519618Sjulian "%s: bad wait/nowait for service %s", 182619618Sjulian CONFIG, sep->se_service); 182719618Sjulian goto more; 182819618Sjulian } 182948069Ssheldonh sep->se_maxchild = -1; 183048069Ssheldonh sep->se_maxcpm = -1; 1831101474Sume sep->se_maxperip = -1; 183219618Sjulian if ((s = strchr(arg, '/')) != NULL) { 183319618Sjulian char *eptr; 183419618Sjulian u_long val; 183519618Sjulian 183619618Sjulian val = strtoul(s + 1, &eptr, 10); 183730847Sdima if (eptr == s + 1 || val > MAX_MAXCHLD) { 183819618Sjulian syslog(LOG_ERR, 183919618Sjulian "%s: bad max-child for service %s", 184019618Sjulian CONFIG, sep->se_service); 184119618Sjulian goto more; 184219618Sjulian } 184348069Ssheldonh if (debug) 184448069Ssheldonh if (!sep->se_accept && val != 1) 184548069Ssheldonh warnx("maxchild=%lu for wait service %s" 184648069Ssheldonh " not recommended", val, sep->se_service); 184719618Sjulian sep->se_maxchild = val; 184830847Sdima if (*eptr == '/') 184930847Sdima sep->se_maxcpm = strtol(eptr + 1, &eptr, 10); 1850101474Sume if (*eptr == '/') 1851101474Sume sep->se_maxperip = strtol(eptr + 1, &eptr, 10); 185230847Sdima /* 185330847Sdima * explicitly do not check for \0 for future expansion / 185430847Sdima * backwards compatibility 185530847Sdima */ 185619618Sjulian } 18571553Srgrimes if (ISMUX(sep)) { 18581553Srgrimes /* 185919618Sjulian * Silently enforce "nowait" mode for TCPMUX services 186019618Sjulian * since they don't have an assigned port to listen on. 18611553Srgrimes */ 186219618Sjulian sep->se_accept = 1; 18631553Srgrimes if (strcmp(sep->se_proto, "tcp")) { 18648857Srgrimes syslog(LOG_ERR, 18651553Srgrimes "%s: bad protocol for tcpmux service %s", 18661553Srgrimes CONFIG, sep->se_service); 18671553Srgrimes goto more; 18681553Srgrimes } 18691553Srgrimes if (sep->se_socktype != SOCK_STREAM) { 18708857Srgrimes syslog(LOG_ERR, 18711553Srgrimes "%s: bad socket type for tcpmux service %s", 18721553Srgrimes CONFIG, sep->se_service); 18731553Srgrimes goto more; 18741553Srgrimes } 18751553Srgrimes } 18761553Srgrimes sep->se_user = newstr(sskip(&cp)); 187730792Sache#ifdef LOGIN_CAP 187830792Sache if ((s = strrchr(sep->se_user, '/')) != NULL) { 187930792Sache *s = '\0'; 188030792Sache sep->se_class = newstr(s + 1); 188130792Sache } else 188230792Sache sep->se_class = newstr(RESOURCE_RC); 188330792Sache#endif 188430807Sache if ((s = strrchr(sep->se_user, ':')) != NULL) { 188530807Sache *s = '\0'; 188630807Sache sep->se_group = newstr(s + 1); 188730807Sache } else 188830807Sache sep->se_group = NULL; 18891553Srgrimes sep->se_server = newstr(sskip(&cp)); 189045588Smarkm if ((sep->se_server_name = rindex(sep->se_server, '/'))) 189145588Smarkm sep->se_server_name++; 18921553Srgrimes if (strcmp(sep->se_server, "internal") == 0) { 18931553Srgrimes struct biltin *bi; 18941553Srgrimes 18951553Srgrimes for (bi = biltins; bi->bi_service; bi++) 189649026Sdes if (bi->bi_socktype == sep->se_socktype && 189748467Ssheldonh matchservent(bi->bi_service, sep->se_service, 189848467Ssheldonh sep->se_proto)) 18991553Srgrimes break; 19001553Srgrimes if (bi->bi_service == 0) { 19011553Srgrimes syslog(LOG_ERR, "internal service %s unknown", 19021553Srgrimes sep->se_service); 19031553Srgrimes goto more; 19041553Srgrimes } 190519618Sjulian sep->se_accept = 1; /* force accept mode for built-ins */ 19061553Srgrimes sep->se_bi = bi; 19071553Srgrimes } else 19081553Srgrimes sep->se_bi = NULL; 1909101474Sume if (sep->se_maxperip < 0) 1910101474Sume sep->se_maxperip = maxperip; 191148069Ssheldonh if (sep->se_maxcpm < 0) 191248069Ssheldonh sep->se_maxcpm = maxcpm; 191345588Smarkm if (sep->se_maxchild < 0) { /* apply default max-children */ 191448069Ssheldonh if (sep->se_bi && sep->se_bi->bi_maxchild >= 0) 191519618Sjulian sep->se_maxchild = sep->se_bi->bi_maxchild; 191648069Ssheldonh else if (sep->se_accept) 191748069Ssheldonh sep->se_maxchild = maxchild > 0 ? maxchild : 0; 191819618Sjulian else 191948069Ssheldonh sep->se_maxchild = 1; 192045588Smarkm } 192164197Sdwmalone if (sep->se_maxchild > 0) { 192219618Sjulian sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); 192319618Sjulian if (sep->se_pids == NULL) { 192449102Ssheldonh syslog(LOG_ERR, "malloc: %m"); 192519618Sjulian exit(EX_OSERR); 192619618Sjulian } 192719618Sjulian } 19281553Srgrimes argc = 0; 19291553Srgrimes for (arg = skip(&cp); cp; arg = skip(&cp)) 193019618Sjulian if (argc < MAXARGV) { 19311553Srgrimes sep->se_argv[argc++] = newstr(arg); 193219618Sjulian } else { 193319618Sjulian syslog(LOG_ERR, 193419618Sjulian "%s: too many arguments for service %s", 193519618Sjulian CONFIG, sep->se_service); 193619618Sjulian goto more; 193719618Sjulian } 19381553Srgrimes while (argc <= MAXARGV) 19391553Srgrimes sep->se_argv[argc++] = NULL; 1940101474Sume for (i = 0; i < PERIPSIZE; ++i) 1941101474Sume LIST_INIT(&sep->se_conn[i]); 194256590Sshin#ifdef IPSEC 194356590Sshin sep->se_policy = policy ? newstr(policy) : NULL; 194456590Sshin#endif 19451553Srgrimes return (sep); 19461553Srgrimes} 19471553Srgrimes 19481553Srgrimesvoid 194998558Sjmallettfreeconfig(struct servtab *cp) 19501553Srgrimes{ 19511553Srgrimes int i; 19521553Srgrimes 19531553Srgrimes if (cp->se_service) 19541553Srgrimes free(cp->se_service); 19551553Srgrimes if (cp->se_proto) 19561553Srgrimes free(cp->se_proto); 19571553Srgrimes if (cp->se_user) 19581553Srgrimes free(cp->se_user); 195930807Sache if (cp->se_group) 196030807Sache free(cp->se_group); 196130792Sache#ifdef LOGIN_CAP 196230792Sache if (cp->se_class) 196330792Sache free(cp->se_class); 196430792Sache#endif 19651553Srgrimes if (cp->se_server) 19661553Srgrimes free(cp->se_server); 196719618Sjulian if (cp->se_pids) 196819618Sjulian free(cp->se_pids); 19691553Srgrimes for (i = 0; i < MAXARGV; i++) 19701553Srgrimes if (cp->se_argv[i]) 19711553Srgrimes free(cp->se_argv[i]); 1972101474Sume free_connlist(cp); 197356590Sshin#ifdef IPSEC 197456590Sshin if (cp->se_policy) 197556590Sshin free(cp->se_policy); 197656590Sshin#endif 19771553Srgrimes} 19781553Srgrimes 19791553Srgrimes 19801553Srgrimes/* 19811553Srgrimes * Safe skip - if skip returns null, log a syntax error in the 19821553Srgrimes * configuration file and exit. 19831553Srgrimes */ 19841553Srgrimeschar * 198598558Sjmallettsskip(char **cpp) 19861553Srgrimes{ 19871553Srgrimes char *cp; 19881553Srgrimes 19891553Srgrimes cp = skip(cpp); 19901553Srgrimes if (cp == NULL) { 19911553Srgrimes syslog(LOG_ERR, "%s: syntax error", CONFIG); 199219617Sjulian exit(EX_DATAERR); 19931553Srgrimes } 19941553Srgrimes return (cp); 19951553Srgrimes} 19961553Srgrimes 19971553Srgrimeschar * 199898558Sjmallettskip(char **cpp) 19991553Srgrimes{ 20001553Srgrimes char *cp = *cpp; 20011553Srgrimes char *start; 200211933Sadam char quote = '\0'; 20031553Srgrimes 20041553Srgrimesagain: 20051553Srgrimes while (*cp == ' ' || *cp == '\t') 20061553Srgrimes cp++; 20071553Srgrimes if (*cp == '\0') { 20081553Srgrimes int c; 20091553Srgrimes 20101553Srgrimes c = getc(fconfig); 20111553Srgrimes (void) ungetc(c, fconfig); 20121553Srgrimes if (c == ' ' || c == '\t') 201319617Sjulian if ((cp = nextline(fconfig))) 20141553Srgrimes goto again; 20151553Srgrimes *cpp = (char *)0; 20161553Srgrimes return ((char *)0); 20171553Srgrimes } 201811933Sadam if (*cp == '"' || *cp == '\'') 201911933Sadam quote = *cp++; 20201553Srgrimes start = cp; 202111933Sadam if (quote) 202211933Sadam while (*cp && *cp != quote) 202311933Sadam cp++; 202411933Sadam else 202511933Sadam while (*cp && *cp != ' ' && *cp != '\t') 202611933Sadam cp++; 20271553Srgrimes if (*cp != '\0') 20281553Srgrimes *cp++ = '\0'; 20291553Srgrimes *cpp = cp; 20301553Srgrimes return (start); 20311553Srgrimes} 20321553Srgrimes 20331553Srgrimeschar * 203498558Sjmallettnextline(FILE *fd) 20351553Srgrimes{ 20361553Srgrimes char *cp; 20371553Srgrimes 20381553Srgrimes if (fgets(line, sizeof (line), fd) == NULL) 20391553Srgrimes return ((char *)0); 20401553Srgrimes cp = strchr(line, '\n'); 20411553Srgrimes if (cp) 20421553Srgrimes *cp = '\0'; 20431553Srgrimes return (line); 20441553Srgrimes} 20451553Srgrimes 20461553Srgrimeschar * 204798558Sjmallettnewstr(const char *cp) 20481553Srgrimes{ 204978694Sdwmalone char *cr; 205078694Sdwmalone 205178694Sdwmalone if ((cr = strdup(cp != NULL ? cp : ""))) 205278694Sdwmalone return (cr); 20531553Srgrimes syslog(LOG_ERR, "strdup: %m"); 205419617Sjulian exit(EX_OSERR); 20551553Srgrimes} 20561553Srgrimes 20571553Srgrimesvoid 205898558Sjmallettinetd_setproctitle(const char *a, int s) 20591553Srgrimes{ 206071399Sdwmalone socklen_t size; 206156590Sshin struct sockaddr_storage ss; 206256590Sshin char buf[80], pbuf[INET6_ADDRSTRLEN]; 20631553Srgrimes 206456590Sshin size = sizeof(ss); 206556590Sshin if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) { 206656590Sshin getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf), 206756590Sshin NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); 206856590Sshin (void) sprintf(buf, "%s [%s]", a, pbuf); 206956590Sshin } else 207013142Speter (void) sprintf(buf, "%s", a); 207113142Speter setproctitle("%s", buf); 207213142Speter} 207313142Speter 207478694Sdwmaloneint 207598558Sjmallettcheck_loop(const struct sockaddr *sa, const struct servtab *sep) 20765182Swollman{ 20775182Swollman struct servtab *se2; 207856590Sshin char pname[INET6_ADDRSTRLEN]; 20795182Swollman 20805182Swollman for (se2 = servtab; se2; se2 = se2->se_next) { 20815182Swollman if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM) 20825182Swollman continue; 20835182Swollman 208456590Sshin switch (se2->se_family) { 208556590Sshin case AF_INET: 208678694Sdwmalone if (((const struct sockaddr_in *)sa)->sin_port == 208756590Sshin se2->se_ctrladdr4.sin_port) 208856590Sshin goto isloop; 208956590Sshin continue; 209056590Sshin#ifdef INET6 209156590Sshin case AF_INET6: 209278694Sdwmalone if (((const struct sockaddr_in *)sa)->sin_port == 209356590Sshin se2->se_ctrladdr4.sin_port) 209456590Sshin goto isloop; 209556590Sshin continue; 209656590Sshin#endif 209756590Sshin default: 209856590Sshin continue; 20995182Swollman } 210056590Sshin isloop: 210156590Sshin getnameinfo(sa, sa->sa_len, pname, sizeof(pname), NULL, 0, 210256590Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 210356590Sshin syslog(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s", 210456590Sshin sep->se_service, sep->se_proto, 210556590Sshin se2->se_service, se2->se_proto, 210656590Sshin pname); 210756590Sshin return 1; 21085182Swollman } 21095182Swollman return 0; 21105182Swollman} 21115182Swollman 21121553Srgrimes/* 21131553Srgrimes * print_service: 21141553Srgrimes * Dump relevant information to stderr 21151553Srgrimes */ 21161553Srgrimesvoid 211798558Sjmallettprint_service(const char *action, const struct servtab *sep) 21181553Srgrimes{ 211919617Sjulian fprintf(stderr, 212056590Sshin "%s: %s proto=%s accept=%d max=%d user=%s group=%s" 212130792Sache#ifdef LOGIN_CAP 212256590Sshin "class=%s" 212330792Sache#endif 212456590Sshin " builtin=%p server=%s" 212556590Sshin#ifdef IPSEC 212656590Sshin " policy=\"%s\"" 212756590Sshin#endif 212856590Sshin "\n", 212919617Sjulian action, sep->se_service, sep->se_proto, 213030807Sache sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group, 213130792Sache#ifdef LOGIN_CAP 213230792Sache sep->se_class, 213330792Sache#endif 213456590Sshin (void *) sep->se_bi, sep->se_server 213556590Sshin#ifdef IPSEC 213656590Sshin , (sep->se_policy ? sep->se_policy : "") 213756590Sshin#endif 213856590Sshin ); 21391553Srgrimes} 21401553Srgrimes 214130847Sdima#define CPMHSIZE 256 214230847Sdima#define CPMHMASK (CPMHSIZE-1) 214330847Sdima#define CHTGRAN 10 214430847Sdima#define CHTSIZE 6 214530847Sdima 214630847Sdimatypedef struct CTime { 214730847Sdima unsigned long ct_Ticks; 214830847Sdima int ct_Count; 214930847Sdima} CTime; 215030847Sdima 215130847Sdimatypedef struct CHash { 215256590Sshin union { 215356590Sshin struct in_addr c4_Addr; 215456590Sshin struct in6_addr c6_Addr; 215556590Sshin } cu_Addr; 215656590Sshin#define ch_Addr4 cu_Addr.c4_Addr 215756590Sshin#define ch_Addr6 cu_Addr.c6_Addr 215856590Sshin int ch_Family; 215930847Sdima time_t ch_LTime; 216030847Sdima char *ch_Service; 216130847Sdima CTime ch_Times[CHTSIZE]; 216230847Sdima} CHash; 216330847Sdima 216430847SdimaCHash CHashAry[CPMHSIZE]; 216530847Sdima 216630847Sdimaint 216798558Sjmallettcpmip(const struct servtab *sep, int ctrl) 216830847Sdima{ 216956590Sshin struct sockaddr_storage rss; 217071399Sdwmalone socklen_t rssLen = sizeof(rss); 217130847Sdima int r = 0; 217230847Sdima 217330847Sdima /* 217430847Sdima * If getpeername() fails, just let it through (if logging is 217530847Sdima * enabled the condition is caught elsewhere) 217630847Sdima */ 217730847Sdima 217830847Sdima if (sep->se_maxcpm > 0 && 217956590Sshin getpeername(ctrl, (struct sockaddr *)&rss, &rssLen) == 0 ) { 218030847Sdima time_t t = time(NULL); 218130847Sdima int hv = 0xABC3D20F; 218230847Sdima int i; 218330847Sdima int cnt = 0; 218430847Sdima CHash *chBest = NULL; 218530847Sdima unsigned int ticks = t / CHTGRAN; 218678694Sdwmalone struct sockaddr_in *sin4; 218756590Sshin#ifdef INET6 218856590Sshin struct sockaddr_in6 *sin6; 218956590Sshin#endif 219030847Sdima 219178694Sdwmalone sin4 = (struct sockaddr_in *)&rss; 219256590Sshin#ifdef INET6 219356590Sshin sin6 = (struct sockaddr_in6 *)&rss; 219456590Sshin#endif 219530847Sdima { 219630847Sdima char *p; 219778694Sdwmalone int addrlen; 219830847Sdima 219956590Sshin switch (rss.ss_family) { 220056590Sshin case AF_INET: 220178694Sdwmalone p = (char *)&sin4->sin_addr; 220256590Sshin addrlen = sizeof(struct in_addr); 220356590Sshin break; 220456590Sshin#ifdef INET6 220556590Sshin case AF_INET6: 220656590Sshin p = (char *)&sin6->sin6_addr; 220756590Sshin addrlen = sizeof(struct in6_addr); 220856590Sshin break; 220956590Sshin#endif 221056590Sshin default: 221156590Sshin /* should not happen */ 221256590Sshin return -1; 221356590Sshin } 221456590Sshin 221556590Sshin for (i = 0; i < addrlen; ++i, ++p) { 221630847Sdima hv = (hv << 5) ^ (hv >> 23) ^ *p; 221730847Sdima } 221830847Sdima hv = (hv ^ (hv >> 16)); 221930847Sdima } 222030847Sdima for (i = 0; i < 5; ++i) { 222130847Sdima CHash *ch = &CHashAry[(hv + i) & CPMHMASK]; 222230847Sdima 222356590Sshin if (rss.ss_family == AF_INET && 222456590Sshin ch->ch_Family == AF_INET && 222578694Sdwmalone sin4->sin_addr.s_addr == ch->ch_Addr4.s_addr && 222630847Sdima ch->ch_Service && strcmp(sep->se_service, 222730847Sdima ch->ch_Service) == 0) { 222830847Sdima chBest = ch; 222930847Sdima break; 223030847Sdima } 223156590Sshin#ifdef INET6 223256590Sshin if (rss.ss_family == AF_INET6 && 223356590Sshin ch->ch_Family == AF_INET6 && 223456590Sshin IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 223556590Sshin &ch->ch_Addr6) != 0 && 223656590Sshin ch->ch_Service && strcmp(sep->se_service, 223756590Sshin ch->ch_Service) == 0) { 223856590Sshin chBest = ch; 223956590Sshin break; 224056590Sshin } 224156590Sshin#endif 224230847Sdima if (chBest == NULL || ch->ch_LTime == 0 || 224330847Sdima ch->ch_LTime < chBest->ch_LTime) { 224430847Sdima chBest = ch; 224530847Sdima } 224630847Sdima } 224756590Sshin if ((rss.ss_family == AF_INET && 224856590Sshin (chBest->ch_Family != AF_INET || 224978694Sdwmalone sin4->sin_addr.s_addr != chBest->ch_Addr4.s_addr)) || 225030847Sdima chBest->ch_Service == NULL || 225130847Sdima strcmp(sep->se_service, chBest->ch_Service) != 0) { 225278694Sdwmalone chBest->ch_Family = sin4->sin_family; 225378694Sdwmalone chBest->ch_Addr4 = sin4->sin_addr; 225430847Sdima if (chBest->ch_Service) 225530847Sdima free(chBest->ch_Service); 225630847Sdima chBest->ch_Service = strdup(sep->se_service); 225730847Sdima bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 225830847Sdima } 225956590Sshin#ifdef INET6 226056590Sshin if ((rss.ss_family == AF_INET6 && 226156590Sshin (chBest->ch_Family != AF_INET6 || 226256590Sshin IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 226356590Sshin &chBest->ch_Addr6) == 0)) || 226456590Sshin chBest->ch_Service == NULL || 226556590Sshin strcmp(sep->se_service, chBest->ch_Service) != 0) { 226656590Sshin chBest->ch_Family = sin6->sin6_family; 226756590Sshin chBest->ch_Addr6 = sin6->sin6_addr; 226856590Sshin if (chBest->ch_Service) 226956590Sshin free(chBest->ch_Service); 227056590Sshin chBest->ch_Service = strdup(sep->se_service); 227156590Sshin bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 227256590Sshin } 227356590Sshin#endif 227430847Sdima chBest->ch_LTime = t; 227530847Sdima { 227630847Sdima CTime *ct = &chBest->ch_Times[ticks % CHTSIZE]; 227730847Sdima if (ct->ct_Ticks != ticks) { 227830847Sdima ct->ct_Ticks = ticks; 227930847Sdima ct->ct_Count = 0; 228030847Sdima } 228130847Sdima ++ct->ct_Count; 228230847Sdima } 228330847Sdima for (i = 0; i < CHTSIZE; ++i) { 228430847Sdima CTime *ct = &chBest->ch_Times[i]; 228530847Sdima if (ct->ct_Ticks <= ticks && 228630847Sdima ct->ct_Ticks >= ticks - CHTSIZE) { 228730847Sdima cnt += ct->ct_Count; 228830847Sdima } 228930847Sdima } 229030847Sdima if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) { 229156590Sshin char pname[INET6_ADDRSTRLEN]; 229256590Sshin 229356590Sshin getnameinfo((struct sockaddr *)&rss, 229456590Sshin ((struct sockaddr *)&rss)->sa_len, 229556590Sshin pname, sizeof(pname), NULL, 0, 229656590Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 229730847Sdima r = -1; 229830847Sdima syslog(LOG_ERR, 229933794Spst "%s from %s exceeded counts/min (limit %d/min)", 230056590Sshin sep->se_service, pname, 230133794Spst sep->se_maxcpm); 230230847Sdima } 230330847Sdima } 230430847Sdima return(r); 230530847Sdima} 2306101474Sume 2307101474Sumestatic struct conninfo * 2308101474Sumesearch_conn(struct servtab *sep, int ctrl) 2309101474Sume{ 2310101474Sume struct sockaddr_storage ss; 2311101474Sume socklen_t sslen = sizeof(ss); 2312101474Sume struct conninfo *conn; 2313101474Sume int hv; 2314101474Sume char pname[NI_MAXHOST], pname2[NI_MAXHOST]; 2315101474Sume 2316101474Sume if (sep->se_maxperip <= 0) 2317101474Sume return NULL; 2318101474Sume 2319101474Sume /* 2320101474Sume * If getpeername() fails, just let it through (if logging is 2321101474Sume * enabled the condition is caught elsewhere) 2322101474Sume */ 2323101474Sume if (getpeername(ctrl, (struct sockaddr *)&ss, &sslen) != 0) 2324101474Sume return NULL; 2325101474Sume 2326101474Sume switch (ss.ss_family) { 2327101474Sume case AF_INET: 2328101474Sume hv = hashval((char *)&((struct sockaddr_in *)&ss)->sin_addr, 2329101474Sume sizeof(struct in_addr)); 2330101474Sume break; 2331101474Sume#ifdef INET6 2332101474Sume case AF_INET6: 2333101474Sume hv = hashval((char *)&((struct sockaddr_in6 *)&ss)->sin6_addr, 2334101474Sume sizeof(struct in6_addr)); 2335101474Sume break; 2336101474Sume#endif 2337101474Sume default: 2338101474Sume /* 2339101474Sume * Since we only support AF_INET and AF_INET6, just 2340101474Sume * let other than AF_INET and AF_INET6 through. 2341101474Sume */ 2342101474Sume return NULL; 2343101474Sume } 2344101474Sume 2345101474Sume if (getnameinfo((struct sockaddr *)&ss, sslen, pname, sizeof(pname), 2346101474Sume NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0) 2347101474Sume return NULL; 2348101474Sume 2349101474Sume LIST_FOREACH(conn, &sep->se_conn[hv], co_link) { 2350101474Sume if (getnameinfo((struct sockaddr *)&conn->co_addr, 2351101474Sume conn->co_addr.ss_len, pname2, sizeof(pname2), NULL, 0, 2352101474Sume NI_NUMERICHOST | NI_WITHSCOPEID) == 0 && 2353101474Sume strcmp(pname, pname2) == 0) 2354101474Sume break; 2355101474Sume } 2356101474Sume 2357101474Sume if (conn == NULL) { 2358101474Sume if ((conn = malloc(sizeof(struct conninfo))) == NULL) { 2359101474Sume syslog(LOG_ERR, "malloc: %m"); 2360101474Sume exit(EX_OSERR); 2361101474Sume } 2362101474Sume conn->co_proc = malloc(sep->se_maxperip * sizeof(*conn->co_proc)); 2363101474Sume if (conn->co_proc == NULL) { 2364101474Sume syslog(LOG_ERR, "malloc: %m"); 2365101474Sume exit(EX_OSERR); 2366101474Sume } 2367101474Sume memcpy(&conn->co_addr, (struct sockaddr *)&ss, sslen); 2368101474Sume conn->co_numchild = 0; 2369101474Sume LIST_INSERT_HEAD(&sep->se_conn[hv], conn, co_link); 2370101474Sume } 2371101474Sume 2372101474Sume /* 2373101474Sume * Since a child process is not invoked yet, we cannot 2374101474Sume * determine a pid of a child. So, co_proc and co_numchild 2375101474Sume * should be filled leter. 2376101474Sume */ 2377101474Sume 2378101474Sume return conn; 2379101474Sume} 2380101474Sume 2381101474Sumestatic int 2382101474Sumeroom_conn(struct servtab *sep, struct conninfo *conn) 2383101474Sume{ 2384101474Sume char pname[NI_MAXHOST]; 2385101474Sume 2386101474Sume if (conn->co_numchild >= sep->se_maxperip) { 2387101474Sume getnameinfo((struct sockaddr *)&conn->co_addr, 2388101474Sume conn->co_addr.ss_len, pname, sizeof(pname), NULL, 0, 2389101474Sume NI_NUMERICHOST | NI_WITHSCOPEID); 2390101474Sume syslog(LOG_ERR, "%s from %s exceeded counts (limit %d)", 2391101474Sume sep->se_service, pname, sep->se_maxperip); 2392101474Sume return 0; 2393101474Sume } 2394101474Sume return 1; 2395101474Sume} 2396101474Sume 2397101474Sumestatic void 2398101474Sumeaddchild_conn(struct conninfo *conn, pid_t pid) 2399101474Sume{ 2400101474Sume struct procinfo *proc; 2401101474Sume 2402101474Sume if (conn == NULL) 2403101474Sume return; 2404101474Sume 2405101474Sume if ((proc = search_proc(pid, 1)) != NULL) { 2406101474Sume if (proc->pr_conn != NULL) { 2407101474Sume syslog(LOG_ERR, 2408101474Sume "addchild_conn: child already on process list"); 2409101474Sume exit(EX_OSERR); 2410101474Sume } 2411101474Sume proc->pr_conn = conn; 2412101474Sume } 2413101474Sume 2414101474Sume conn->co_proc[conn->co_numchild++] = proc; 2415101474Sume} 2416101474Sume 2417101474Sumestatic void 2418101474Sumereapchild_conn(pid_t pid) 2419101474Sume{ 2420101474Sume struct procinfo *proc; 2421101474Sume struct conninfo *conn; 2422101474Sume int i; 2423101474Sume 2424101474Sume if ((proc = search_proc(pid, 0)) == NULL) 2425101474Sume return; 2426101474Sume if ((conn = proc->pr_conn) == NULL) 2427101474Sume return; 2428101474Sume for (i = 0; i < conn->co_numchild; ++i) 2429101474Sume if (conn->co_proc[i] == proc) { 2430101474Sume conn->co_proc[i] = conn->co_proc[--conn->co_numchild]; 2431101474Sume break; 2432101474Sume } 2433101474Sume free_proc(proc); 2434101474Sume free_conn(conn); 2435101474Sume} 2436101474Sume 2437101474Sumestatic void 2438102859Sdwmaloneresize_conn(struct servtab *sep, int maxpip) 2439101474Sume{ 2440101474Sume struct conninfo *conn; 2441101474Sume int i, j; 2442101474Sume 2443101474Sume if (sep->se_maxperip <= 0) 2444101474Sume return; 2445102859Sdwmalone if (maxpip <= 0) { 2446101474Sume free_connlist(sep); 2447101474Sume return; 2448101474Sume } 2449101474Sume for (i = 0; i < PERIPSIZE; ++i) { 2450101474Sume LIST_FOREACH(conn, &sep->se_conn[i], co_link) { 2451102859Sdwmalone for (j = maxpip; j < conn->co_numchild; ++j) 2452101474Sume free_proc(conn->co_proc[j]); 2453101474Sume conn->co_proc = realloc(conn->co_proc, 2454102859Sdwmalone maxpip * sizeof(*conn->co_proc)); 2455101474Sume if (conn->co_proc == NULL) { 2456101474Sume syslog(LOG_ERR, "realloc: %m"); 2457101474Sume exit(EX_OSERR); 2458101474Sume } 2459102859Sdwmalone if (conn->co_numchild > maxpip) 2460102859Sdwmalone conn->co_numchild = maxpip; 2461101474Sume } 2462101474Sume } 2463101474Sume} 2464101474Sume 2465101474Sumestatic void 2466101474Sumefree_connlist(struct servtab *sep) 2467101474Sume{ 2468101474Sume struct conninfo *conn; 2469101474Sume int i, j; 2470101474Sume 2471101474Sume for (i = 0; i < PERIPSIZE; ++i) { 2472101474Sume while ((conn = LIST_FIRST(&sep->se_conn[i])) != NULL) { 2473101474Sume for (j = 0; j < conn->co_numchild; ++j) 2474101474Sume free_proc(conn->co_proc[j]); 2475101474Sume conn->co_numchild = 0; 2476101474Sume free_conn(conn); 2477101474Sume } 2478101474Sume } 2479101474Sume} 2480101474Sume 2481101474Sumestatic void 2482101474Sumefree_conn(struct conninfo *conn) 2483101474Sume{ 2484101474Sume if (conn == NULL) 2485101474Sume return; 2486101474Sume if (conn->co_numchild <= 0) { 2487101474Sume LIST_REMOVE(conn, co_link); 2488101474Sume free(conn->co_proc); 2489101474Sume free(conn); 2490101474Sume } 2491101474Sume} 2492101474Sume 2493101474Sumestatic struct procinfo * 2494101474Sumesearch_proc(pid_t pid, int add) 2495101474Sume{ 2496101474Sume struct procinfo *proc; 2497101474Sume int hv; 2498101474Sume 2499101474Sume hv = hashval((char *)&pid, sizeof(pid)); 2500101474Sume LIST_FOREACH(proc, &proctable[hv], pr_link) { 2501101474Sume if (proc->pr_pid == pid) 2502101474Sume break; 2503101474Sume } 2504101474Sume if (proc == NULL && add) { 2505101474Sume if ((proc = malloc(sizeof(struct procinfo))) == NULL) { 2506101474Sume syslog(LOG_ERR, "malloc: %m"); 2507101474Sume exit(EX_OSERR); 2508101474Sume } 2509101474Sume proc->pr_pid = pid; 2510101474Sume proc->pr_conn = NULL; 2511101474Sume LIST_INSERT_HEAD(&proctable[hv], proc, pr_link); 2512101474Sume } 2513101474Sume return proc; 2514101474Sume} 2515101474Sume 2516101474Sumestatic void 2517101474Sumefree_proc(struct procinfo *proc) 2518101474Sume{ 2519101474Sume if (proc == NULL) 2520101474Sume return; 2521101474Sume LIST_REMOVE(proc, pr_link); 2522101474Sume free(proc); 2523101474Sume} 2524101474Sume 2525101474Sumestatic int 2526101474Sumehashval(char *p, int len) 2527101474Sume{ 2528101474Sume int i, hv = 0xABC3D20F; 2529101474Sume 2530101474Sume for (i = 0; i < len; ++i, ++p) 2531101474Sume hv = (hv << 5) ^ (hv >> 23) ^ *p; 2532101474Sume hv = (hv ^ (hv >> 16)) & (PERIPSIZE - 1); 2533101474Sume return hv; 2534101474Sume} 2535