inetd.c revision 101474
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 101474 2002-08-07 17:03:14Z ume $"); 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; 950101474Sume struct conninfo *conn; 95142122Sdes long omask; 952100127Salfred int new_nomapped; 9531553Srgrimes 9541553Srgrimes if (!setconfig()) { 9551553Srgrimes syslog(LOG_ERR, "%s: %m", CONFIG); 9561553Srgrimes return; 9571553Srgrimes } 9581553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 9591553Srgrimes sep->se_checked = 0; 96019618Sjulian while ((new = getconfigent())) { 96130807Sache if (getpwnam(new->se_user) == NULL) { 9621553Srgrimes syslog(LOG_ERR, 96356482Scharnier "%s/%s: no such user '%s', service ignored", 96419618Sjulian new->se_service, new->se_proto, new->se_user); 9651553Srgrimes continue; 9661553Srgrimes } 96730807Sache if (new->se_group && getgrnam(new->se_group) == NULL) { 96830807Sache syslog(LOG_ERR, 96956482Scharnier "%s/%s: no such group '%s', service ignored", 97030807Sache new->se_service, new->se_proto, new->se_group); 97130807Sache continue; 97230807Sache } 97330792Sache#ifdef LOGIN_CAP 97430792Sache if (login_getclass(new->se_class) == NULL) { 97530792Sache /* error syslogged by getclass */ 97630792Sache syslog(LOG_ERR, 97737850Sache "%s/%s: %s: login class error, service ignored", 97837850Sache new->se_service, new->se_proto, new->se_class); 97930792Sache continue; 98030792Sache } 98130792Sache#endif 982100127Salfred new_nomapped = new->se_nomapped; 9831553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 98419618Sjulian if (strcmp(sep->se_service, new->se_service) == 0 && 98556590Sshin strcmp(sep->se_proto, new->se_proto) == 0 && 986100127Salfred sep->se_rpc == new->se_rpc && 98778356Sdwmalone sep->se_socktype == new->se_socktype && 98856590Sshin sep->se_family == new->se_family) 9891553Srgrimes break; 9901553Srgrimes if (sep != 0) { 9911553Srgrimes int i; 9921553Srgrimes 99398611Sjmallett#define SWAP(t,a, b) { t c = a; a = b; b = c; } 99442122Sdes omask = sigblock(SIGBLOCK); 99556590Sshin if (sep->se_nomapped != new->se_nomapped) { 996100127Salfred /* for rpc keep old nommaped till unregister */ 997100127Salfred if (!sep->se_rpc) 998100127Salfred sep->se_nomapped = new->se_nomapped; 99956590Sshin sep->se_reset = 1; 100056590Sshin } 100119618Sjulian /* copy over outstanding child pids */ 100264197Sdwmalone if (sep->se_maxchild > 0 && new->se_maxchild > 0) { 100319618Sjulian new->se_numchild = sep->se_numchild; 100419618Sjulian if (new->se_numchild > new->se_maxchild) 100519618Sjulian new->se_numchild = new->se_maxchild; 100619618Sjulian memcpy(new->se_pids, sep->se_pids, 100719618Sjulian new->se_numchild * sizeof(*new->se_pids)); 100819618Sjulian } 100998611Sjmallett SWAP(pid_t *, sep->se_pids, new->se_pids); 101019618Sjulian sep->se_maxchild = new->se_maxchild; 101119618Sjulian sep->se_numchild = new->se_numchild; 101230847Sdima sep->se_maxcpm = new->se_maxcpm; 1013101474Sume resize_conn(sep, new->se_maxperip); 1014101474Sume sep->se_maxperip = new->se_maxperip; 101566544Sdwmalone sep->se_bi = new->se_bi; 101619618Sjulian /* might need to turn on or off service now */ 101719618Sjulian if (sep->se_fd >= 0) { 101864197Sdwmalone if (sep->se_maxchild > 0 101919618Sjulian && sep->se_numchild == sep->se_maxchild) { 102019618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 102119618Sjulian disable(sep); 102219618Sjulian } else { 102319618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) 102419618Sjulian enable(sep); 102519618Sjulian } 102619618Sjulian } 102719618Sjulian sep->se_accept = new->se_accept; 102898611Sjmallett SWAP(char *, sep->se_user, new->se_user); 102998611Sjmallett SWAP(char *, sep->se_group, new->se_group); 103030792Sache#ifdef LOGIN_CAP 103198611Sjmallett SWAP(char *, sep->se_class, new->se_class); 103230792Sache#endif 103398611Sjmallett SWAP(char *, sep->se_server, new->se_server); 103498611Sjmallett SWAP(char *, sep->se_server_name, new->se_server_name); 10351553Srgrimes for (i = 0; i < MAXARGV; i++) 103698611Sjmallett SWAP(char *, sep->se_argv[i], new->se_argv[i]); 103756590Sshin#ifdef IPSEC 103898611Sjmallett SWAP(char *, sep->se_policy, new->se_policy); 103956590Sshin ipsecsetup(sep); 104056590Sshin#endif 104142122Sdes sigsetmask(omask); 104219618Sjulian freeconfig(new); 10431553Srgrimes if (debug) 10441553Srgrimes print_service("REDO", sep); 10451553Srgrimes } else { 104619618Sjulian sep = enter(new); 10471553Srgrimes if (debug) 10481553Srgrimes print_service("ADD ", sep); 10491553Srgrimes } 10501553Srgrimes sep->se_checked = 1; 10511553Srgrimes if (ISMUX(sep)) { 10521553Srgrimes sep->se_fd = -1; 10531553Srgrimes continue; 10541553Srgrimes } 105556590Sshin switch (sep->se_family) { 105656590Sshin case AF_INET: 105756590Sshin if (no_v4bind != 0) { 105856590Sshin sep->se_fd = -1; 105956590Sshin continue; 106056590Sshin } 106156590Sshin break; 106256590Sshin#ifdef INET6 106356590Sshin case AF_INET6: 106456590Sshin if (no_v6bind != 0) { 106556590Sshin sep->se_fd = -1; 106656590Sshin continue; 106756590Sshin } 106856590Sshin break; 106956590Sshin#endif 107056590Sshin } 10712657Scsgr if (!sep->se_rpc) { 107278356Sdwmalone if (sep->se_family != AF_UNIX) { 107378356Sdwmalone sp = getservbyname(sep->se_service, sep->se_proto); 107478356Sdwmalone if (sp == 0) { 107578356Sdwmalone syslog(LOG_ERR, "%s/%s: unknown service", 107678356Sdwmalone sep->se_service, sep->se_proto); 107778356Sdwmalone sep->se_checked = 0; 107878356Sdwmalone continue; 107978356Sdwmalone } 10802657Scsgr } 108156590Sshin switch (sep->se_family) { 108256590Sshin case AF_INET: 108356590Sshin if (sp->s_port != sep->se_ctrladdr4.sin_port) { 108456590Sshin sep->se_ctrladdr4.sin_port = 108556590Sshin sp->s_port; 108656590Sshin sep->se_reset = 1; 108756590Sshin } 108856590Sshin break; 108956590Sshin#ifdef INET6 109056590Sshin case AF_INET6: 109156590Sshin if (sp->s_port != 109256590Sshin sep->se_ctrladdr6.sin6_port) { 109356590Sshin sep->se_ctrladdr6.sin6_port = 109456590Sshin sp->s_port; 109556590Sshin sep->se_reset = 1; 109656590Sshin } 109756590Sshin break; 109856590Sshin#endif 10992657Scsgr } 110056590Sshin if (sep->se_reset != 0 && sep->se_fd >= 0) 110156590Sshin close_sep(sep); 11022657Scsgr } else { 11032657Scsgr rpc = getrpcbyname(sep->se_service); 11042657Scsgr if (rpc == 0) { 110552219Scharnier syslog(LOG_ERR, "%s/%s unknown RPC service", 11062657Scsgr sep->se_service, sep->se_proto); 11072657Scsgr if (sep->se_fd != -1) 11082657Scsgr (void) close(sep->se_fd); 11092657Scsgr sep->se_fd = -1; 11102657Scsgr continue; 11112657Scsgr } 1112100127Salfred if (sep->se_reset != 0 || 1113100127Salfred rpc->r_number != sep->se_rpc_prog) { 11142657Scsgr if (sep->se_rpc_prog) 11152657Scsgr unregisterrpc(sep); 11162657Scsgr sep->se_rpc_prog = rpc->r_number; 11172657Scsgr if (sep->se_fd != -1) 11182657Scsgr (void) close(sep->se_fd); 11192657Scsgr sep->se_fd = -1; 11202657Scsgr } 1121100127Salfred sep->se_nomapped = new_nomapped; 11221553Srgrimes } 1123100127Salfred sep->se_reset = 0; 11241553Srgrimes if (sep->se_fd == -1) 11251553Srgrimes setup(sep); 11261553Srgrimes } 11271553Srgrimes endconfig(); 11281553Srgrimes /* 11291553Srgrimes * Purge anything not looked at above. 11301553Srgrimes */ 113142122Sdes omask = sigblock(SIGBLOCK); 11321553Srgrimes sepp = &servtab; 113319617Sjulian while ((sep = *sepp)) { 11341553Srgrimes if (sep->se_checked) { 11351553Srgrimes sepp = &sep->se_next; 11361553Srgrimes continue; 11371553Srgrimes } 11381553Srgrimes *sepp = sep->se_next; 11391553Srgrimes if (sep->se_fd >= 0) 11401553Srgrimes close_sep(sep); 11411553Srgrimes if (debug) 11421553Srgrimes print_service("FREE", sep); 11432657Scsgr if (sep->se_rpc && sep->se_rpc_prog > 0) 11442657Scsgr unregisterrpc(sep); 11451553Srgrimes freeconfig(sep); 114671399Sdwmalone free(sep); 11471553Srgrimes } 114842122Sdes (void) sigsetmask(omask); 11491553Srgrimes} 11501553Srgrimes 11511553Srgrimesvoid 115298558Sjmallettunregisterrpc(struct servtab *sep) 11532657Scsgr{ 115478694Sdwmalone u_int i; 11552657Scsgr struct servtab *sepp; 115642122Sdes long omask; 1157100127Salfred struct netconfig *netid4, *netid6; 11582657Scsgr 115942122Sdes omask = sigblock(SIGBLOCK); 1160100127Salfred netid4 = sep->se_socktype == SOCK_DGRAM ? udpconf : tcpconf; 1161100127Salfred netid6 = sep->se_socktype == SOCK_DGRAM ? udp6conf : tcp6conf; 1162100127Salfred if (sep->se_family == AF_INET) 1163100127Salfred netid6 = NULL; 1164100127Salfred else if (sep->se_nomapped) 1165100127Salfred netid4 = NULL; 1166100127Salfred /* 1167100127Salfred * Conflict if same prog and protocol - In that case one should look 1168100127Salfred * to versions, but it is not interesting: having separate servers for 1169100127Salfred * different versions does not work well. 1170100127Salfred * Therefore one do not unregister if there is a conflict. 1171100127Salfred * There is also transport conflict if destroying INET when INET46 1172100127Salfred * exists, or destroying INET46 when INET exists 1173100127Salfred */ 11742657Scsgr for (sepp = servtab; sepp; sepp = sepp->se_next) { 11752657Scsgr if (sepp == sep) 11762657Scsgr continue; 1177100127Salfred if (sepp->se_checked == 0 || 11782657Scsgr !sepp->se_rpc || 1179100127Salfred strcmp(sep->se_proto, sepp->se_proto) != 0 || 11802657Scsgr sep->se_rpc_prog != sepp->se_rpc_prog) 11812657Scsgr continue; 1182100127Salfred if (sepp->se_family == AF_INET) 1183100127Salfred netid4 = NULL; 1184100127Salfred if (sepp->se_family == AF_INET6) { 1185100127Salfred netid6 = NULL; 1186100127Salfred if (!sep->se_nomapped) 1187100127Salfred netid4 = NULL; 1188100127Salfred } 1189100127Salfred if (netid4 == NULL && netid6 == NULL) 1190100127Salfred return; 11912657Scsgr } 11922657Scsgr if (debug) 11932657Scsgr print_service("UNREG", sep); 1194100127Salfred for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { 1195100127Salfred if (netid4) 1196100127Salfred rpcb_unset(sep->se_rpc_prog, i, netid4); 1197100127Salfred if (netid6) 1198100127Salfred rpcb_unset(sep->se_rpc_prog, i, netid6); 1199100127Salfred } 12002657Scsgr if (sep->se_fd != -1) 12012657Scsgr (void) close(sep->se_fd); 12022657Scsgr sep->se_fd = -1; 120342122Sdes (void) sigsetmask(omask); 12042657Scsgr} 12052657Scsgr 12062657Scsgrvoid 120798561Sjmallettflag_retry(int signo __unused) 12081553Srgrimes{ 120942250Sdes flag_signal('A'); 121042122Sdes} 121142122Sdes 121242122Sdesvoid 121398558Sjmallettretry(void) 121442122Sdes{ 12151553Srgrimes struct servtab *sep; 12161553Srgrimes 12171553Srgrimes timingout = 0; 12181553Srgrimes for (sep = servtab; sep; sep = sep->se_next) 121919617Sjulian if (sep->se_fd == -1 && !ISMUX(sep)) 12201553Srgrimes setup(sep); 12211553Srgrimes} 12221553Srgrimes 12231553Srgrimesvoid 122498558Sjmallettsetup(struct servtab *sep) 12251553Srgrimes{ 12261553Srgrimes int on = 1; 12271553Srgrimes 122856590Sshin if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { 12291553Srgrimes if (debug) 123029602Scharnier warn("socket failed on %s/%s", 123129602Scharnier sep->se_service, sep->se_proto); 12321553Srgrimes syslog(LOG_ERR, "%s/%s: socket: %m", 12331553Srgrimes sep->se_service, sep->se_proto); 12341553Srgrimes return; 12351553Srgrimes } 12361553Srgrimes#define turnon(fd, opt) \ 12371553Srgrimessetsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) 12381553Srgrimes if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && 12391553Srgrimes turnon(sep->se_fd, SO_DEBUG) < 0) 12401553Srgrimes syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 12411553Srgrimes if (turnon(sep->se_fd, SO_REUSEADDR) < 0) 12421553Srgrimes syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); 124325253Swollman#ifdef SO_PRIVSTATE 124413956Swollman if (turnon(sep->se_fd, SO_PRIVSTATE) < 0) 124513956Swollman syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m"); 124625253Swollman#endif 124756590Sshin /* tftpd opens a new connection then needs more infos */ 124856590Sshin if ((sep->se_family == AF_INET6) && 124956590Sshin (strcmp(sep->se_proto, "udp") == 0) && 125056590Sshin (sep->se_accept == 0) && 125156590Sshin (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_PKTINFO, 125256590Sshin (char *)&on, sizeof (on)) < 0)) 125356590Sshin syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m"); 125458935Sume if (sep->se_family == AF_INET6) { 125558935Sume int flag = sep->se_nomapped ? 1 : 0; 1256100505Sume if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_V6ONLY, 125758935Sume (char *)&flag, sizeof (flag)) < 0) 1258100505Sume syslog(LOG_ERR, "setsockopt (IPV6_V6ONLY): %m"); 125958935Sume } 12601553Srgrimes#undef turnon 126136042Sguido if (sep->se_type == TTCP_TYPE) 126236042Sguido if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH, 126336042Sguido (char *)&on, sizeof (on)) < 0) 126436042Sguido syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m"); 126556590Sshin#ifdef IPV6_FAITH 126656590Sshin if (sep->se_type == FAITH_TYPE) { 126756590Sshin if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH, &on, 126856590Sshin sizeof(on)) < 0) { 126956590Sshin syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m"); 127056590Sshin } 127156590Sshin } 127256590Sshin#endif 127356590Sshin#ifdef IPSEC 127456590Sshin ipsecsetup(sep); 127556590Sshin#endif 127678356Sdwmalone if (sep->se_family == AF_UNIX) { 127778356Sdwmalone (void) unlink(sep->se_ctrladdr_un.sun_path); 127878356Sdwmalone umask(0777); /* Make socket with conservative permissions */ 127978356Sdwmalone } 12801553Srgrimes if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr, 128156590Sshin sep->se_ctrladdr_size) < 0) { 12821553Srgrimes if (debug) 128329602Scharnier warn("bind failed on %s/%s", 128429602Scharnier sep->se_service, sep->se_proto); 12851553Srgrimes syslog(LOG_ERR, "%s/%s: bind: %m", 12861553Srgrimes sep->se_service, sep->se_proto); 12871553Srgrimes (void) close(sep->se_fd); 12881553Srgrimes sep->se_fd = -1; 12891553Srgrimes if (!timingout) { 12901553Srgrimes timingout = 1; 12911553Srgrimes alarm(RETRYTIME); 12921553Srgrimes } 129378356Sdwmalone if (sep->se_family == AF_UNIX) 129478356Sdwmalone umask(mask); 12951553Srgrimes return; 12961553Srgrimes } 129778356Sdwmalone if (sep->se_family == AF_UNIX) { 129878356Sdwmalone /* Ick - fch{own,mod} don't work on Unix domain sockets */ 129978356Sdwmalone if (chown(sep->se_service, sep->se_sockuid, sep->se_sockgid) < 0) 130078356Sdwmalone syslog(LOG_ERR, "chown socket: %m"); 130178356Sdwmalone if (chmod(sep->se_service, sep->se_sockmode) < 0) 130278356Sdwmalone syslog(LOG_ERR, "chmod socket: %m"); 130378356Sdwmalone umask(mask); 130478356Sdwmalone } 13052657Scsgr if (sep->se_rpc) { 130678694Sdwmalone u_int i; 130771399Sdwmalone socklen_t len = sep->se_ctrladdr_size; 1308100127Salfred struct netconfig *netid, *netid2 = NULL; 1309100127Salfred struct sockaddr_in sock; 1310100127Salfred struct netbuf nbuf, nbuf2; 13112657Scsgr 13128857Srgrimes if (getsockname(sep->se_fd, 13132657Scsgr (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){ 13142657Scsgr syslog(LOG_ERR, "%s/%s: getsockname: %m", 13152657Scsgr sep->se_service, sep->se_proto); 13162657Scsgr (void) close(sep->se_fd); 13172657Scsgr sep->se_fd = -1; 13188857Srgrimes return; 13192657Scsgr } 1320100127Salfred nbuf.buf = &sep->se_ctrladdr; 1321100127Salfred nbuf.len = sep->se_ctrladdr.sa_len; 1322100127Salfred if (sep->se_family == AF_INET) 1323100127Salfred netid = sep->se_socktype==SOCK_DGRAM? udpconf:tcpconf; 1324100127Salfred else { 1325100127Salfred netid = sep->se_socktype==SOCK_DGRAM? udp6conf:tcp6conf; 1326100127Salfred if (!sep->se_nomapped) { /* INET and INET6 */ 1327100127Salfred netid2 = netid==udp6conf? udpconf:tcpconf; 1328100127Salfred memset(&sock, 0, sizeof sock); /* ADDR_ANY */ 1329100127Salfred nbuf2.buf = &sock; 1330100127Salfred nbuf2.len = sock.sin_len = sizeof sock; 1331100127Salfred sock.sin_family = AF_INET; 1332100127Salfred sock.sin_port = sep->se_ctrladdr6.sin6_port; 1333100127Salfred } 1334100127Salfred } 13352657Scsgr if (debug) 13362657Scsgr print_service("REG ", sep); 13372657Scsgr for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) { 1338100127Salfred rpcb_unset(sep->se_rpc_prog, i, netid); 1339100127Salfred rpcb_set(sep->se_rpc_prog, i, netid, &nbuf); 1340100127Salfred if (netid2) { 1341100127Salfred rpcb_unset(sep->se_rpc_prog, i, netid2); 1342100127Salfred rpcb_set(sep->se_rpc_prog, i, netid2, &nbuf2); 1343100127Salfred } 13442657Scsgr } 13452657Scsgr } 13461553Srgrimes if (sep->se_socktype == SOCK_STREAM) 134717197Sdg listen(sep->se_fd, 64); 134819618Sjulian enable(sep); 13491553Srgrimes if (debug) { 135029602Scharnier warnx("registered %s on %d", 13511553Srgrimes sep->se_server, sep->se_fd); 13521553Srgrimes } 13531553Srgrimes} 13541553Srgrimes 135556590Sshin#ifdef IPSEC 135656590Sshinvoid 135756590Sshinipsecsetup(sep) 135856590Sshin struct servtab *sep; 135956590Sshin{ 136056590Sshin char *buf; 136156590Sshin char *policy_in = NULL; 136256590Sshin char *policy_out = NULL; 136356590Sshin int level; 136456590Sshin int opt; 136556590Sshin 136656590Sshin switch (sep->se_family) { 136756590Sshin case AF_INET: 136856590Sshin level = IPPROTO_IP; 136956590Sshin opt = IP_IPSEC_POLICY; 137056590Sshin break; 137156590Sshin#ifdef INET6 137256590Sshin case AF_INET6: 137356590Sshin level = IPPROTO_IPV6; 137456590Sshin opt = IPV6_IPSEC_POLICY; 137556590Sshin break; 137656590Sshin#endif 137756590Sshin default: 137856590Sshin return; 137956590Sshin } 138056590Sshin 138156590Sshin if (!sep->se_policy || sep->se_policy[0] == '\0') { 138278694Sdwmalone static char def_in[] = "in entrust", def_out[] = "out entrust"; 138378694Sdwmalone policy_in = def_in; 138478694Sdwmalone policy_out = def_out; 138556590Sshin } else { 138656590Sshin if (!strncmp("in", sep->se_policy, 2)) 138756590Sshin policy_in = sep->se_policy; 138856590Sshin else if (!strncmp("out", sep->se_policy, 3)) 138956590Sshin policy_out = sep->se_policy; 139056590Sshin else { 139156590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 139256590Sshin sep->se_policy); 139356590Sshin return; 139456590Sshin } 139556590Sshin } 139656590Sshin 139756590Sshin if (policy_in != NULL) { 139856590Sshin buf = ipsec_set_policy(policy_in, strlen(policy_in)); 139956590Sshin if (buf != NULL) { 140056590Sshin if (setsockopt(sep->se_fd, level, opt, 140156675Sshin buf, ipsec_get_policylen(buf)) < 0 && 140256759Sshin debug != 0) 140356759Sshin warnx("%s/%s: ipsec initialization failed; %s", 140456759Sshin sep->se_service, sep->se_proto, 140556759Sshin policy_in); 140656590Sshin free(buf); 140756590Sshin } else 140856590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 140956590Sshin policy_in); 141056590Sshin } 141156590Sshin if (policy_out != NULL) { 141256590Sshin buf = ipsec_set_policy(policy_out, strlen(policy_out)); 141356590Sshin if (buf != NULL) { 141456590Sshin if (setsockopt(sep->se_fd, level, opt, 141556675Sshin buf, ipsec_get_policylen(buf)) < 0 && 141656759Sshin debug != 0) 141756759Sshin warnx("%s/%s: ipsec initialization failed; %s", 141856759Sshin sep->se_service, sep->se_proto, 141956759Sshin policy_out); 142056590Sshin free(buf); 142156590Sshin } else 142256590Sshin syslog(LOG_ERR, "invalid security policy \"%s\"", 142356590Sshin policy_out); 142456590Sshin } 142556590Sshin} 142656590Sshin#endif 142756590Sshin 14281553Srgrimes/* 14291553Srgrimes * Finish with a service and its socket. 14301553Srgrimes */ 14311553Srgrimesvoid 143298558Sjmallettclose_sep(struct servtab *sep) 14331553Srgrimes{ 14341553Srgrimes if (sep->se_fd >= 0) { 143519618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) 143619618Sjulian disable(sep); 14371553Srgrimes (void) close(sep->se_fd); 14381553Srgrimes sep->se_fd = -1; 14391553Srgrimes } 14401553Srgrimes sep->se_count = 0; 144119618Sjulian sep->se_numchild = 0; /* forget about any existing children */ 14421553Srgrimes} 14431553Srgrimes 144448467Ssheldonhint 144598558Sjmallettmatchservent(const char *name1, const char *name2, const char *proto) 144648467Ssheldonh{ 144778356Sdwmalone char **alias, *p; 144848467Ssheldonh struct servent *se; 144948467Ssheldonh 145078356Sdwmalone if (strcmp(proto, "unix") == 0) { 145178356Sdwmalone if ((p = strrchr(name1, '/')) != NULL) 145278356Sdwmalone name1 = p + 1; 145378356Sdwmalone if ((p = strrchr(name2, '/')) != NULL) 145478356Sdwmalone name2 = p + 1; 145578356Sdwmalone } 145649026Sdes if (strcmp(name1, name2) == 0) 145749026Sdes return(1); 145848467Ssheldonh if ((se = getservbyname(name1, proto)) != NULL) { 145948467Ssheldonh if (strcmp(name2, se->s_name) == 0) 146048467Ssheldonh return(1); 146148467Ssheldonh for (alias = se->s_aliases; *alias; alias++) 146248467Ssheldonh if (strcmp(name2, *alias) == 0) 146348467Ssheldonh return(1); 146448467Ssheldonh } 146548467Ssheldonh return(0); 146648467Ssheldonh} 146748467Ssheldonh 14681553Srgrimesstruct servtab * 146998558Sjmallettenter(struct servtab *cp) 14701553Srgrimes{ 14711553Srgrimes struct servtab *sep; 147242122Sdes long omask; 14731553Srgrimes 14741553Srgrimes sep = (struct servtab *)malloc(sizeof (*sep)); 14751553Srgrimes if (sep == (struct servtab *)0) { 147649102Ssheldonh syslog(LOG_ERR, "malloc: %m"); 147719617Sjulian exit(EX_OSERR); 14781553Srgrimes } 14791553Srgrimes *sep = *cp; 14801553Srgrimes sep->se_fd = -1; 148142122Sdes omask = sigblock(SIGBLOCK); 14821553Srgrimes sep->se_next = servtab; 14831553Srgrimes servtab = sep; 148442122Sdes sigsetmask(omask); 14851553Srgrimes return (sep); 14861553Srgrimes} 14871553Srgrimes 148819618Sjulianvoid 148998558Sjmallettenable(struct servtab *sep) 149019618Sjulian{ 149119618Sjulian if (debug) 149229602Scharnier warnx( 149319618Sjulian "enabling %s, fd %d", sep->se_service, sep->se_fd); 149419618Sjulian#ifdef SANITY_CHECK 149519618Sjulian if (sep->se_fd < 0) { 149619618Sjulian syslog(LOG_ERR, 149719618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 149819618Sjulian exit(EX_SOFTWARE); 149919618Sjulian } 150019618Sjulian if (ISMUX(sep)) { 150119618Sjulian syslog(LOG_ERR, 150219618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 150319618Sjulian exit(EX_SOFTWARE); 150419618Sjulian } 150519618Sjulian if (FD_ISSET(sep->se_fd, &allsock)) { 150619618Sjulian syslog(LOG_ERR, 150719618Sjulian "%s: %s: not off", __FUNCTION__, sep->se_service); 150819618Sjulian exit(EX_SOFTWARE); 150919618Sjulian } 151048991Ssheldonh nsock++; 151119618Sjulian#endif 151219618Sjulian FD_SET(sep->se_fd, &allsock); 151319618Sjulian if (sep->se_fd > maxsock) 151419618Sjulian maxsock = sep->se_fd; 151519618Sjulian} 151619618Sjulian 151719618Sjulianvoid 151898558Sjmallettdisable(struct servtab *sep) 151919618Sjulian{ 152019618Sjulian if (debug) 152129602Scharnier warnx( 152219618Sjulian "disabling %s, fd %d", sep->se_service, sep->se_fd); 152319618Sjulian#ifdef SANITY_CHECK 152419618Sjulian if (sep->se_fd < 0) { 152519618Sjulian syslog(LOG_ERR, 152619618Sjulian "%s: %s: bad fd", __FUNCTION__, sep->se_service); 152719618Sjulian exit(EX_SOFTWARE); 152819618Sjulian } 152919618Sjulian if (ISMUX(sep)) { 153019618Sjulian syslog(LOG_ERR, 153119618Sjulian "%s: %s: is mux", __FUNCTION__, sep->se_service); 153219618Sjulian exit(EX_SOFTWARE); 153319618Sjulian } 153419618Sjulian if (!FD_ISSET(sep->se_fd, &allsock)) { 153519618Sjulian syslog(LOG_ERR, 153619618Sjulian "%s: %s: not on", __FUNCTION__, sep->se_service); 153719618Sjulian exit(EX_SOFTWARE); 153819618Sjulian } 153919618Sjulian if (nsock == 0) { 154019618Sjulian syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); 154119618Sjulian exit(EX_SOFTWARE); 154219618Sjulian } 154348991Ssheldonh nsock--; 154419618Sjulian#endif 154519618Sjulian FD_CLR(sep->se_fd, &allsock); 154619618Sjulian if (sep->se_fd == maxsock) 154719618Sjulian maxsock--; 154819618Sjulian} 154919618Sjulian 15501553SrgrimesFILE *fconfig = NULL; 15511553Srgrimesstruct servtab serv; 15521553Srgrimeschar line[LINE_MAX]; 15531553Srgrimes 15541553Srgrimesint 155598558Sjmallettsetconfig(void) 15561553Srgrimes{ 15571553Srgrimes 15581553Srgrimes if (fconfig != NULL) { 15591553Srgrimes fseek(fconfig, 0L, SEEK_SET); 15601553Srgrimes return (1); 15611553Srgrimes } 15621553Srgrimes fconfig = fopen(CONFIG, "r"); 15631553Srgrimes return (fconfig != NULL); 15641553Srgrimes} 15651553Srgrimes 15661553Srgrimesvoid 156798558Sjmallettendconfig(void) 15681553Srgrimes{ 15691553Srgrimes if (fconfig) { 15701553Srgrimes (void) fclose(fconfig); 15711553Srgrimes fconfig = NULL; 15721553Srgrimes } 15731553Srgrimes} 15741553Srgrimes 15751553Srgrimesstruct servtab * 157698558Sjmallettgetconfigent(void) 15771553Srgrimes{ 15781553Srgrimes struct servtab *sep = &serv; 15791553Srgrimes int argc; 158019618Sjulian char *cp, *arg, *s; 15812657Scsgr char *versp; 15821553Srgrimes static char TCPMUX_TOKEN[] = "tcpmux/"; 15831553Srgrimes#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1) 158456590Sshin#ifdef IPSEC 158556590Sshin char *policy = NULL; 158656590Sshin#endif 158756590Sshin int v4bind = 0; 158856590Sshin#ifdef INET6 158956590Sshin int v6bind = 0; 159056590Sshin#endif 1591101474Sume int i; 15921553Srgrimes 15931553Srgrimesmore: 159456590Sshin while ((cp = nextline(fconfig)) != NULL) { 159556590Sshin#ifdef IPSEC 159656590Sshin /* lines starting with #@ is not a comment, but the policy */ 159756590Sshin if (cp[0] == '#' && cp[1] == '@') { 159856590Sshin char *p; 159956590Sshin for (p = cp + 2; p && *p && isspace(*p); p++) 160056590Sshin ; 160156590Sshin if (*p == '\0') { 160256590Sshin if (policy) 160356590Sshin free(policy); 160456590Sshin policy = NULL; 160556590Sshin } else if (ipsec_get_policylen(p) >= 0) { 160656590Sshin if (policy) 160756590Sshin free(policy); 160856590Sshin policy = newstr(p); 160956590Sshin } else { 161056590Sshin syslog(LOG_ERR, 161156590Sshin "%s: invalid ipsec policy \"%s\"", 161256590Sshin CONFIG, p); 161356590Sshin exit(EX_CONFIG); 161456590Sshin } 161556590Sshin } 161656590Sshin#endif 161756590Sshin if (*cp == '#' || *cp == '\0') 161856590Sshin continue; 161956590Sshin break; 162056590Sshin } 16211553Srgrimes if (cp == NULL) 16221553Srgrimes return ((struct servtab *)0); 16231553Srgrimes /* 16241553Srgrimes * clear the static buffer, since some fields (se_ctrladdr, 16251553Srgrimes * for example) don't get initialized here. 16261553Srgrimes */ 162771399Sdwmalone memset(sep, 0, sizeof *sep); 16281553Srgrimes arg = skip(&cp); 16291553Srgrimes if (cp == NULL) { 16301553Srgrimes /* got an empty line containing just blanks/tabs. */ 16311553Srgrimes goto more; 16321553Srgrimes } 163378356Sdwmalone if (arg[0] == ':') { /* :user:group:perm: */ 163478356Sdwmalone char *user, *group, *perm; 163578356Sdwmalone struct passwd *pw; 163678356Sdwmalone struct group *gr; 163778356Sdwmalone user = arg+1; 163878356Sdwmalone if ((group = strchr(user, ':')) == NULL) { 163978356Sdwmalone syslog(LOG_ERR, "no group after user '%s'", user); 164078356Sdwmalone goto more; 164178356Sdwmalone } 164278356Sdwmalone *group++ = '\0'; 164378356Sdwmalone if ((perm = strchr(group, ':')) == NULL) { 164478356Sdwmalone syslog(LOG_ERR, "no mode after group '%s'", group); 164578356Sdwmalone goto more; 164678356Sdwmalone } 164778356Sdwmalone *perm++ = '\0'; 164878356Sdwmalone if ((pw = getpwnam(user)) == NULL) { 164978356Sdwmalone syslog(LOG_ERR, "no such user '%s'", user); 165078356Sdwmalone goto more; 165178356Sdwmalone } 165278356Sdwmalone sep->se_sockuid = pw->pw_uid; 165378356Sdwmalone if ((gr = getgrnam(group)) == NULL) { 165478356Sdwmalone syslog(LOG_ERR, "no such user '%s'", group); 165578356Sdwmalone goto more; 165678356Sdwmalone } 165778356Sdwmalone sep->se_sockgid = gr->gr_gid; 165878356Sdwmalone sep->se_sockmode = strtol(perm, &arg, 8); 165978356Sdwmalone if (*arg != ':') { 166078356Sdwmalone syslog(LOG_ERR, "bad mode '%s'", perm); 166178356Sdwmalone goto more; 166278356Sdwmalone } 166378356Sdwmalone *arg++ = '\0'; 166478356Sdwmalone } else { 166578356Sdwmalone sep->se_sockuid = euid; 166678356Sdwmalone sep->se_sockgid = egid; 166778356Sdwmalone sep->se_sockmode = 0200; 166878356Sdwmalone } 16691553Srgrimes if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) { 16701553Srgrimes char *c = arg + MUX_LEN; 16711553Srgrimes if (*c == '+') { 16721553Srgrimes sep->se_type = MUXPLUS_TYPE; 16731553Srgrimes c++; 16741553Srgrimes } else 16751553Srgrimes sep->se_type = MUX_TYPE; 16761553Srgrimes sep->se_service = newstr(c); 16771553Srgrimes } else { 16781553Srgrimes sep->se_service = newstr(arg); 16791553Srgrimes sep->se_type = NORM_TYPE; 16801553Srgrimes } 16811553Srgrimes arg = sskip(&cp); 16821553Srgrimes if (strcmp(arg, "stream") == 0) 16831553Srgrimes sep->se_socktype = SOCK_STREAM; 16841553Srgrimes else if (strcmp(arg, "dgram") == 0) 16851553Srgrimes sep->se_socktype = SOCK_DGRAM; 16861553Srgrimes else if (strcmp(arg, "rdm") == 0) 16871553Srgrimes sep->se_socktype = SOCK_RDM; 16881553Srgrimes else if (strcmp(arg, "seqpacket") == 0) 16891553Srgrimes sep->se_socktype = SOCK_SEQPACKET; 16901553Srgrimes else if (strcmp(arg, "raw") == 0) 16911553Srgrimes sep->se_socktype = SOCK_RAW; 16921553Srgrimes else 16931553Srgrimes sep->se_socktype = -1; 169436042Sguido 169536042Sguido arg = sskip(&cp); 169656590Sshin if (strncmp(arg, "tcp", 3) == 0) { 169756590Sshin sep->se_proto = newstr(strsep(&arg, "/")); 169856590Sshin if (arg != NULL) { 169956590Sshin if (strcmp(arg, "ttcp") == 0) 170056590Sshin sep->se_type = TTCP_TYPE; 170156590Sshin else if (strcmp(arg, "faith") == 0) 170256590Sshin sep->se_type = FAITH_TYPE; 170356590Sshin } 170477518Sume } else { 170577518Sume if (sep->se_type == NORM_TYPE && 170677518Sume strncmp(arg, "faith/", 6) == 0) { 170777518Sume arg += 6; 170877518Sume sep->se_type = FAITH_TYPE; 170977518Sume } 171036042Sguido sep->se_proto = newstr(arg); 171177518Sume } 17122657Scsgr if (strncmp(sep->se_proto, "rpc/", 4) == 0) { 171319237Sjoerg memmove(sep->se_proto, sep->se_proto + 4, 171419237Sjoerg strlen(sep->se_proto) + 1 - 4); 17152657Scsgr sep->se_rpc = 1; 17162657Scsgr sep->se_rpc_prog = sep->se_rpc_lowvers = 17172657Scsgr sep->se_rpc_lowvers = 0; 171856590Sshin memcpy(&sep->se_ctrladdr4, bind_sa4, 171956590Sshin sizeof(sep->se_ctrladdr4)); 17202657Scsgr if ((versp = rindex(sep->se_service, '/'))) { 17212657Scsgr *versp++ = '\0'; 17222657Scsgr switch (sscanf(versp, "%d-%d", 17232657Scsgr &sep->se_rpc_lowvers, 17242657Scsgr &sep->se_rpc_highvers)) { 17252657Scsgr case 2: 17262657Scsgr break; 17272657Scsgr case 1: 17282657Scsgr sep->se_rpc_highvers = 17292657Scsgr sep->se_rpc_lowvers; 17302657Scsgr break; 17312657Scsgr default: 17328857Srgrimes syslog(LOG_ERR, 173352219Scharnier "bad RPC version specifier; %s", 17342657Scsgr sep->se_service); 17352657Scsgr freeconfig(sep); 17362657Scsgr goto more; 17372657Scsgr } 17382657Scsgr } 17392657Scsgr else { 17402657Scsgr sep->se_rpc_lowvers = 17412657Scsgr sep->se_rpc_highvers = 1; 17422657Scsgr } 17432657Scsgr } 174456590Sshin sep->se_nomapped = 0; 174556590Sshin while (isdigit(sep->se_proto[strlen(sep->se_proto) - 1])) { 174656590Sshin#ifdef INET6 174756590Sshin if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') { 174856590Sshin sep->se_proto[strlen(sep->se_proto) - 1] = '\0'; 174956590Sshin v6bind = 1; 175056590Sshin continue; 175156590Sshin } 175256590Sshin#endif 175356590Sshin if (sep->se_proto[strlen(sep->se_proto) - 1] == '4') { 175456590Sshin sep->se_proto[strlen(sep->se_proto) - 1] = '\0'; 175556590Sshin v4bind = 1; 175656590Sshin continue; 175756590Sshin } 175856590Sshin /* illegal version num */ 175956590Sshin syslog(LOG_ERR, "bad IP version for %s", sep->se_proto); 176056590Sshin freeconfig(sep); 176156590Sshin goto more; 176256590Sshin } 176378356Sdwmalone if (strcmp(sep->se_proto, "unix") == 0) { 176478356Sdwmalone sep->se_family = AF_UNIX; 176578356Sdwmalone } else 176656590Sshin#ifdef INET6 1767100127Salfred if (v6bind != 0 && no_v6bind != 0) { 1768100127Salfred syslog(LOG_INFO, "IPv6 bind is ignored for %s", 1769100127Salfred sep->se_service); 1770100127Salfred if (v4bind && no_v4bind == 0) 1771100127Salfred v6bind = 0; 1772100127Salfred else { 1773100127Salfred freeconfig(sep); 1774100127Salfred goto more; 1775100127Salfred } 1776100127Salfred } 177756590Sshin if (v6bind != 0) { 177856590Sshin sep->se_family = AF_INET6; 177956590Sshin if (v4bind == 0 || no_v4bind != 0) 178056590Sshin sep->se_nomapped = 1; 178157906Sshin } else 178256590Sshin#endif 178357906Sshin { /* default to v4 bind if not v6 bind */ 178456590Sshin if (no_v4bind != 0) { 178597293Sjwd syslog(LOG_NOTICE, "IPv4 bind is ignored for %s", 178656590Sshin sep->se_service); 178756590Sshin freeconfig(sep); 178856590Sshin goto more; 178956590Sshin } 179056590Sshin sep->se_family = AF_INET; 179156590Sshin } 179256590Sshin /* init ctladdr */ 179356590Sshin switch(sep->se_family) { 179456590Sshin case AF_INET: 179556590Sshin memcpy(&sep->se_ctrladdr4, bind_sa4, 179656590Sshin sizeof(sep->se_ctrladdr4)); 179756590Sshin sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr4); 179856590Sshin break; 179956590Sshin#ifdef INET6 180056590Sshin case AF_INET6: 180156590Sshin memcpy(&sep->se_ctrladdr6, bind_sa6, 180256590Sshin sizeof(sep->se_ctrladdr6)); 180356590Sshin sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr6); 180456590Sshin break; 180556590Sshin#endif 180678356Sdwmalone case AF_UNIX: 180778356Sdwmalone if (strlen(sep->se_service) >= sizeof(sep->se_ctrladdr_un.sun_path)) { 180878356Sdwmalone syslog(LOG_ERR, 180978356Sdwmalone "domain socket pathname too long for service %s", 181078356Sdwmalone sep->se_service); 181178356Sdwmalone goto more; 181278356Sdwmalone } 181378356Sdwmalone memset(&sep->se_ctrladdr, 0, sizeof(sep->se_ctrladdr)); 181478356Sdwmalone sep->se_ctrladdr_un.sun_family = sep->se_family; 181578356Sdwmalone sep->se_ctrladdr_un.sun_len = strlen(sep->se_service); 181678356Sdwmalone strcpy(sep->se_ctrladdr_un.sun_path, sep->se_service); 181778356Sdwmalone sep->se_ctrladdr_size = SUN_LEN(&sep->se_ctrladdr_un); 181856590Sshin } 18191553Srgrimes arg = sskip(&cp); 182019618Sjulian if (!strncmp(arg, "wait", 4)) 182119618Sjulian sep->se_accept = 0; 182219618Sjulian else if (!strncmp(arg, "nowait", 6)) 182319618Sjulian sep->se_accept = 1; 182419618Sjulian else { 182519618Sjulian syslog(LOG_ERR, 182619618Sjulian "%s: bad wait/nowait for service %s", 182719618Sjulian CONFIG, sep->se_service); 182819618Sjulian goto more; 182919618Sjulian } 183048069Ssheldonh sep->se_maxchild = -1; 183148069Ssheldonh sep->se_maxcpm = -1; 1832101474Sume sep->se_maxperip = -1; 183319618Sjulian if ((s = strchr(arg, '/')) != NULL) { 183419618Sjulian char *eptr; 183519618Sjulian u_long val; 183619618Sjulian 183719618Sjulian val = strtoul(s + 1, &eptr, 10); 183830847Sdima if (eptr == s + 1 || val > MAX_MAXCHLD) { 183919618Sjulian syslog(LOG_ERR, 184019618Sjulian "%s: bad max-child for service %s", 184119618Sjulian CONFIG, sep->se_service); 184219618Sjulian goto more; 184319618Sjulian } 184448069Ssheldonh if (debug) 184548069Ssheldonh if (!sep->se_accept && val != 1) 184648069Ssheldonh warnx("maxchild=%lu for wait service %s" 184748069Ssheldonh " not recommended", val, sep->se_service); 184819618Sjulian sep->se_maxchild = val; 184930847Sdima if (*eptr == '/') 185030847Sdima sep->se_maxcpm = strtol(eptr + 1, &eptr, 10); 1851101474Sume if (*eptr == '/') 1852101474Sume sep->se_maxperip = strtol(eptr + 1, &eptr, 10); 185330847Sdima /* 185430847Sdima * explicitly do not check for \0 for future expansion / 185530847Sdima * backwards compatibility 185630847Sdima */ 185719618Sjulian } 18581553Srgrimes if (ISMUX(sep)) { 18591553Srgrimes /* 186019618Sjulian * Silently enforce "nowait" mode for TCPMUX services 186119618Sjulian * since they don't have an assigned port to listen on. 18621553Srgrimes */ 186319618Sjulian sep->se_accept = 1; 18641553Srgrimes if (strcmp(sep->se_proto, "tcp")) { 18658857Srgrimes syslog(LOG_ERR, 18661553Srgrimes "%s: bad protocol for tcpmux service %s", 18671553Srgrimes CONFIG, sep->se_service); 18681553Srgrimes goto more; 18691553Srgrimes } 18701553Srgrimes if (sep->se_socktype != SOCK_STREAM) { 18718857Srgrimes syslog(LOG_ERR, 18721553Srgrimes "%s: bad socket type for tcpmux service %s", 18731553Srgrimes CONFIG, sep->se_service); 18741553Srgrimes goto more; 18751553Srgrimes } 18761553Srgrimes } 18771553Srgrimes sep->se_user = newstr(sskip(&cp)); 187830792Sache#ifdef LOGIN_CAP 187930792Sache if ((s = strrchr(sep->se_user, '/')) != NULL) { 188030792Sache *s = '\0'; 188130792Sache sep->se_class = newstr(s + 1); 188230792Sache } else 188330792Sache sep->se_class = newstr(RESOURCE_RC); 188430792Sache#endif 188530807Sache if ((s = strrchr(sep->se_user, ':')) != NULL) { 188630807Sache *s = '\0'; 188730807Sache sep->se_group = newstr(s + 1); 188830807Sache } else 188930807Sache sep->se_group = NULL; 18901553Srgrimes sep->se_server = newstr(sskip(&cp)); 189145588Smarkm if ((sep->se_server_name = rindex(sep->se_server, '/'))) 189245588Smarkm sep->se_server_name++; 18931553Srgrimes if (strcmp(sep->se_server, "internal") == 0) { 18941553Srgrimes struct biltin *bi; 18951553Srgrimes 18961553Srgrimes for (bi = biltins; bi->bi_service; bi++) 189749026Sdes if (bi->bi_socktype == sep->se_socktype && 189848467Ssheldonh matchservent(bi->bi_service, sep->se_service, 189948467Ssheldonh sep->se_proto)) 19001553Srgrimes break; 19011553Srgrimes if (bi->bi_service == 0) { 19021553Srgrimes syslog(LOG_ERR, "internal service %s unknown", 19031553Srgrimes sep->se_service); 19041553Srgrimes goto more; 19051553Srgrimes } 190619618Sjulian sep->se_accept = 1; /* force accept mode for built-ins */ 19071553Srgrimes sep->se_bi = bi; 19081553Srgrimes } else 19091553Srgrimes sep->se_bi = NULL; 1910101474Sume if (sep->se_maxperip < 0) 1911101474Sume sep->se_maxperip = maxperip; 191248069Ssheldonh if (sep->se_maxcpm < 0) 191348069Ssheldonh sep->se_maxcpm = maxcpm; 191445588Smarkm if (sep->se_maxchild < 0) { /* apply default max-children */ 191548069Ssheldonh if (sep->se_bi && sep->se_bi->bi_maxchild >= 0) 191619618Sjulian sep->se_maxchild = sep->se_bi->bi_maxchild; 191748069Ssheldonh else if (sep->se_accept) 191848069Ssheldonh sep->se_maxchild = maxchild > 0 ? maxchild : 0; 191919618Sjulian else 192048069Ssheldonh sep->se_maxchild = 1; 192145588Smarkm } 192264197Sdwmalone if (sep->se_maxchild > 0) { 192319618Sjulian sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); 192419618Sjulian if (sep->se_pids == NULL) { 192549102Ssheldonh syslog(LOG_ERR, "malloc: %m"); 192619618Sjulian exit(EX_OSERR); 192719618Sjulian } 192819618Sjulian } 19291553Srgrimes argc = 0; 19301553Srgrimes for (arg = skip(&cp); cp; arg = skip(&cp)) 193119618Sjulian if (argc < MAXARGV) { 19321553Srgrimes sep->se_argv[argc++] = newstr(arg); 193319618Sjulian } else { 193419618Sjulian syslog(LOG_ERR, 193519618Sjulian "%s: too many arguments for service %s", 193619618Sjulian CONFIG, sep->se_service); 193719618Sjulian goto more; 193819618Sjulian } 19391553Srgrimes while (argc <= MAXARGV) 19401553Srgrimes sep->se_argv[argc++] = NULL; 1941101474Sume for (i = 0; i < PERIPSIZE; ++i) 1942101474Sume LIST_INIT(&sep->se_conn[i]); 194356590Sshin#ifdef IPSEC 194456590Sshin sep->se_policy = policy ? newstr(policy) : NULL; 194556590Sshin#endif 19461553Srgrimes return (sep); 19471553Srgrimes} 19481553Srgrimes 19491553Srgrimesvoid 195098558Sjmallettfreeconfig(struct servtab *cp) 19511553Srgrimes{ 19521553Srgrimes int i; 19531553Srgrimes 19541553Srgrimes if (cp->se_service) 19551553Srgrimes free(cp->se_service); 19561553Srgrimes if (cp->se_proto) 19571553Srgrimes free(cp->se_proto); 19581553Srgrimes if (cp->se_user) 19591553Srgrimes free(cp->se_user); 196030807Sache if (cp->se_group) 196130807Sache free(cp->se_group); 196230792Sache#ifdef LOGIN_CAP 196330792Sache if (cp->se_class) 196430792Sache free(cp->se_class); 196530792Sache#endif 19661553Srgrimes if (cp->se_server) 19671553Srgrimes free(cp->se_server); 196819618Sjulian if (cp->se_pids) 196919618Sjulian free(cp->se_pids); 19701553Srgrimes for (i = 0; i < MAXARGV; i++) 19711553Srgrimes if (cp->se_argv[i]) 19721553Srgrimes free(cp->se_argv[i]); 1973101474Sume free_connlist(cp); 197456590Sshin#ifdef IPSEC 197556590Sshin if (cp->se_policy) 197656590Sshin free(cp->se_policy); 197756590Sshin#endif 19781553Srgrimes} 19791553Srgrimes 19801553Srgrimes 19811553Srgrimes/* 19821553Srgrimes * Safe skip - if skip returns null, log a syntax error in the 19831553Srgrimes * configuration file and exit. 19841553Srgrimes */ 19851553Srgrimeschar * 198698558Sjmallettsskip(char **cpp) 19871553Srgrimes{ 19881553Srgrimes char *cp; 19891553Srgrimes 19901553Srgrimes cp = skip(cpp); 19911553Srgrimes if (cp == NULL) { 19921553Srgrimes syslog(LOG_ERR, "%s: syntax error", CONFIG); 199319617Sjulian exit(EX_DATAERR); 19941553Srgrimes } 19951553Srgrimes return (cp); 19961553Srgrimes} 19971553Srgrimes 19981553Srgrimeschar * 199998558Sjmallettskip(char **cpp) 20001553Srgrimes{ 20011553Srgrimes char *cp = *cpp; 20021553Srgrimes char *start; 200311933Sadam char quote = '\0'; 20041553Srgrimes 20051553Srgrimesagain: 20061553Srgrimes while (*cp == ' ' || *cp == '\t') 20071553Srgrimes cp++; 20081553Srgrimes if (*cp == '\0') { 20091553Srgrimes int c; 20101553Srgrimes 20111553Srgrimes c = getc(fconfig); 20121553Srgrimes (void) ungetc(c, fconfig); 20131553Srgrimes if (c == ' ' || c == '\t') 201419617Sjulian if ((cp = nextline(fconfig))) 20151553Srgrimes goto again; 20161553Srgrimes *cpp = (char *)0; 20171553Srgrimes return ((char *)0); 20181553Srgrimes } 201911933Sadam if (*cp == '"' || *cp == '\'') 202011933Sadam quote = *cp++; 20211553Srgrimes start = cp; 202211933Sadam if (quote) 202311933Sadam while (*cp && *cp != quote) 202411933Sadam cp++; 202511933Sadam else 202611933Sadam while (*cp && *cp != ' ' && *cp != '\t') 202711933Sadam cp++; 20281553Srgrimes if (*cp != '\0') 20291553Srgrimes *cp++ = '\0'; 20301553Srgrimes *cpp = cp; 20311553Srgrimes return (start); 20321553Srgrimes} 20331553Srgrimes 20341553Srgrimeschar * 203598558Sjmallettnextline(FILE *fd) 20361553Srgrimes{ 20371553Srgrimes char *cp; 20381553Srgrimes 20391553Srgrimes if (fgets(line, sizeof (line), fd) == NULL) 20401553Srgrimes return ((char *)0); 20411553Srgrimes cp = strchr(line, '\n'); 20421553Srgrimes if (cp) 20431553Srgrimes *cp = '\0'; 20441553Srgrimes return (line); 20451553Srgrimes} 20461553Srgrimes 20471553Srgrimeschar * 204898558Sjmallettnewstr(const char *cp) 20491553Srgrimes{ 205078694Sdwmalone char *cr; 205178694Sdwmalone 205278694Sdwmalone if ((cr = strdup(cp != NULL ? cp : ""))) 205378694Sdwmalone return (cr); 20541553Srgrimes syslog(LOG_ERR, "strdup: %m"); 205519617Sjulian exit(EX_OSERR); 20561553Srgrimes} 20571553Srgrimes 20581553Srgrimesvoid 205998558Sjmallettinetd_setproctitle(const char *a, int s) 20601553Srgrimes{ 206171399Sdwmalone socklen_t size; 206256590Sshin struct sockaddr_storage ss; 206356590Sshin char buf[80], pbuf[INET6_ADDRSTRLEN]; 20641553Srgrimes 206556590Sshin size = sizeof(ss); 206656590Sshin if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) { 206756590Sshin getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf), 206856590Sshin NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); 206956590Sshin (void) sprintf(buf, "%s [%s]", a, pbuf); 207056590Sshin } else 207113142Speter (void) sprintf(buf, "%s", a); 207213142Speter setproctitle("%s", buf); 207313142Speter} 207413142Speter 207578694Sdwmaloneint 207698558Sjmallettcheck_loop(const struct sockaddr *sa, const struct servtab *sep) 20775182Swollman{ 20785182Swollman struct servtab *se2; 207956590Sshin char pname[INET6_ADDRSTRLEN]; 20805182Swollman 20815182Swollman for (se2 = servtab; se2; se2 = se2->se_next) { 20825182Swollman if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM) 20835182Swollman continue; 20845182Swollman 208556590Sshin switch (se2->se_family) { 208656590Sshin case AF_INET: 208778694Sdwmalone if (((const struct sockaddr_in *)sa)->sin_port == 208856590Sshin se2->se_ctrladdr4.sin_port) 208956590Sshin goto isloop; 209056590Sshin continue; 209156590Sshin#ifdef INET6 209256590Sshin case AF_INET6: 209378694Sdwmalone if (((const struct sockaddr_in *)sa)->sin_port == 209456590Sshin se2->se_ctrladdr4.sin_port) 209556590Sshin goto isloop; 209656590Sshin continue; 209756590Sshin#endif 209856590Sshin default: 209956590Sshin continue; 21005182Swollman } 210156590Sshin isloop: 210256590Sshin getnameinfo(sa, sa->sa_len, pname, sizeof(pname), NULL, 0, 210356590Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 210456590Sshin syslog(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s", 210556590Sshin sep->se_service, sep->se_proto, 210656590Sshin se2->se_service, se2->se_proto, 210756590Sshin pname); 210856590Sshin return 1; 21095182Swollman } 21105182Swollman return 0; 21115182Swollman} 21125182Swollman 21131553Srgrimes/* 21141553Srgrimes * print_service: 21151553Srgrimes * Dump relevant information to stderr 21161553Srgrimes */ 21171553Srgrimesvoid 211898558Sjmallettprint_service(const char *action, const struct servtab *sep) 21191553Srgrimes{ 212019617Sjulian fprintf(stderr, 212156590Sshin "%s: %s proto=%s accept=%d max=%d user=%s group=%s" 212230792Sache#ifdef LOGIN_CAP 212356590Sshin "class=%s" 212430792Sache#endif 212556590Sshin " builtin=%p server=%s" 212656590Sshin#ifdef IPSEC 212756590Sshin " policy=\"%s\"" 212856590Sshin#endif 212956590Sshin "\n", 213019617Sjulian action, sep->se_service, sep->se_proto, 213130807Sache sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group, 213230792Sache#ifdef LOGIN_CAP 213330792Sache sep->se_class, 213430792Sache#endif 213556590Sshin (void *) sep->se_bi, sep->se_server 213656590Sshin#ifdef IPSEC 213756590Sshin , (sep->se_policy ? sep->se_policy : "") 213856590Sshin#endif 213956590Sshin ); 21401553Srgrimes} 21411553Srgrimes 214230847Sdima#define CPMHSIZE 256 214330847Sdima#define CPMHMASK (CPMHSIZE-1) 214430847Sdima#define CHTGRAN 10 214530847Sdima#define CHTSIZE 6 214630847Sdima 214730847Sdimatypedef struct CTime { 214830847Sdima unsigned long ct_Ticks; 214930847Sdima int ct_Count; 215030847Sdima} CTime; 215130847Sdima 215230847Sdimatypedef struct CHash { 215356590Sshin union { 215456590Sshin struct in_addr c4_Addr; 215556590Sshin struct in6_addr c6_Addr; 215656590Sshin } cu_Addr; 215756590Sshin#define ch_Addr4 cu_Addr.c4_Addr 215856590Sshin#define ch_Addr6 cu_Addr.c6_Addr 215956590Sshin int ch_Family; 216030847Sdima time_t ch_LTime; 216130847Sdima char *ch_Service; 216230847Sdima CTime ch_Times[CHTSIZE]; 216330847Sdima} CHash; 216430847Sdima 216530847SdimaCHash CHashAry[CPMHSIZE]; 216630847Sdima 216730847Sdimaint 216898558Sjmallettcpmip(const struct servtab *sep, int ctrl) 216930847Sdima{ 217056590Sshin struct sockaddr_storage rss; 217171399Sdwmalone socklen_t rssLen = sizeof(rss); 217230847Sdima int r = 0; 217330847Sdima 217430847Sdima /* 217530847Sdima * If getpeername() fails, just let it through (if logging is 217630847Sdima * enabled the condition is caught elsewhere) 217730847Sdima */ 217830847Sdima 217930847Sdima if (sep->se_maxcpm > 0 && 218056590Sshin getpeername(ctrl, (struct sockaddr *)&rss, &rssLen) == 0 ) { 218130847Sdima time_t t = time(NULL); 218230847Sdima int hv = 0xABC3D20F; 218330847Sdima int i; 218430847Sdima int cnt = 0; 218530847Sdima CHash *chBest = NULL; 218630847Sdima unsigned int ticks = t / CHTGRAN; 218778694Sdwmalone struct sockaddr_in *sin4; 218856590Sshin#ifdef INET6 218956590Sshin struct sockaddr_in6 *sin6; 219056590Sshin#endif 219130847Sdima 219278694Sdwmalone sin4 = (struct sockaddr_in *)&rss; 219356590Sshin#ifdef INET6 219456590Sshin sin6 = (struct sockaddr_in6 *)&rss; 219556590Sshin#endif 219630847Sdima { 219730847Sdima char *p; 219878694Sdwmalone int addrlen; 219930847Sdima 220056590Sshin switch (rss.ss_family) { 220156590Sshin case AF_INET: 220278694Sdwmalone p = (char *)&sin4->sin_addr; 220356590Sshin addrlen = sizeof(struct in_addr); 220456590Sshin break; 220556590Sshin#ifdef INET6 220656590Sshin case AF_INET6: 220756590Sshin p = (char *)&sin6->sin6_addr; 220856590Sshin addrlen = sizeof(struct in6_addr); 220956590Sshin break; 221056590Sshin#endif 221156590Sshin default: 221256590Sshin /* should not happen */ 221356590Sshin return -1; 221456590Sshin } 221556590Sshin 221656590Sshin for (i = 0; i < addrlen; ++i, ++p) { 221730847Sdima hv = (hv << 5) ^ (hv >> 23) ^ *p; 221830847Sdima } 221930847Sdima hv = (hv ^ (hv >> 16)); 222030847Sdima } 222130847Sdima for (i = 0; i < 5; ++i) { 222230847Sdima CHash *ch = &CHashAry[(hv + i) & CPMHMASK]; 222330847Sdima 222456590Sshin if (rss.ss_family == AF_INET && 222556590Sshin ch->ch_Family == AF_INET && 222678694Sdwmalone sin4->sin_addr.s_addr == ch->ch_Addr4.s_addr && 222730847Sdima ch->ch_Service && strcmp(sep->se_service, 222830847Sdima ch->ch_Service) == 0) { 222930847Sdima chBest = ch; 223030847Sdima break; 223130847Sdima } 223256590Sshin#ifdef INET6 223356590Sshin if (rss.ss_family == AF_INET6 && 223456590Sshin ch->ch_Family == AF_INET6 && 223556590Sshin IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 223656590Sshin &ch->ch_Addr6) != 0 && 223756590Sshin ch->ch_Service && strcmp(sep->se_service, 223856590Sshin ch->ch_Service) == 0) { 223956590Sshin chBest = ch; 224056590Sshin break; 224156590Sshin } 224256590Sshin#endif 224330847Sdima if (chBest == NULL || ch->ch_LTime == 0 || 224430847Sdima ch->ch_LTime < chBest->ch_LTime) { 224530847Sdima chBest = ch; 224630847Sdima } 224730847Sdima } 224856590Sshin if ((rss.ss_family == AF_INET && 224956590Sshin (chBest->ch_Family != AF_INET || 225078694Sdwmalone sin4->sin_addr.s_addr != chBest->ch_Addr4.s_addr)) || 225130847Sdima chBest->ch_Service == NULL || 225230847Sdima strcmp(sep->se_service, chBest->ch_Service) != 0) { 225378694Sdwmalone chBest->ch_Family = sin4->sin_family; 225478694Sdwmalone chBest->ch_Addr4 = sin4->sin_addr; 225530847Sdima if (chBest->ch_Service) 225630847Sdima free(chBest->ch_Service); 225730847Sdima chBest->ch_Service = strdup(sep->se_service); 225830847Sdima bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 225930847Sdima } 226056590Sshin#ifdef INET6 226156590Sshin if ((rss.ss_family == AF_INET6 && 226256590Sshin (chBest->ch_Family != AF_INET6 || 226356590Sshin IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, 226456590Sshin &chBest->ch_Addr6) == 0)) || 226556590Sshin chBest->ch_Service == NULL || 226656590Sshin strcmp(sep->se_service, chBest->ch_Service) != 0) { 226756590Sshin chBest->ch_Family = sin6->sin6_family; 226856590Sshin chBest->ch_Addr6 = sin6->sin6_addr; 226956590Sshin if (chBest->ch_Service) 227056590Sshin free(chBest->ch_Service); 227156590Sshin chBest->ch_Service = strdup(sep->se_service); 227256590Sshin bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); 227356590Sshin } 227456590Sshin#endif 227530847Sdima chBest->ch_LTime = t; 227630847Sdima { 227730847Sdima CTime *ct = &chBest->ch_Times[ticks % CHTSIZE]; 227830847Sdima if (ct->ct_Ticks != ticks) { 227930847Sdima ct->ct_Ticks = ticks; 228030847Sdima ct->ct_Count = 0; 228130847Sdima } 228230847Sdima ++ct->ct_Count; 228330847Sdima } 228430847Sdima for (i = 0; i < CHTSIZE; ++i) { 228530847Sdima CTime *ct = &chBest->ch_Times[i]; 228630847Sdima if (ct->ct_Ticks <= ticks && 228730847Sdima ct->ct_Ticks >= ticks - CHTSIZE) { 228830847Sdima cnt += ct->ct_Count; 228930847Sdima } 229030847Sdima } 229130847Sdima if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) { 229256590Sshin char pname[INET6_ADDRSTRLEN]; 229356590Sshin 229456590Sshin getnameinfo((struct sockaddr *)&rss, 229556590Sshin ((struct sockaddr *)&rss)->sa_len, 229656590Sshin pname, sizeof(pname), NULL, 0, 229756590Sshin NI_NUMERICHOST|NI_WITHSCOPEID); 229830847Sdima r = -1; 229930847Sdima syslog(LOG_ERR, 230033794Spst "%s from %s exceeded counts/min (limit %d/min)", 230156590Sshin sep->se_service, pname, 230233794Spst sep->se_maxcpm); 230330847Sdima } 230430847Sdima } 230530847Sdima return(r); 230630847Sdima} 2307101474Sume 2308101474Sumestatic struct conninfo * 2309101474Sumesearch_conn(struct servtab *sep, int ctrl) 2310101474Sume{ 2311101474Sume struct sockaddr_storage ss; 2312101474Sume socklen_t sslen = sizeof(ss); 2313101474Sume struct conninfo *conn; 2314101474Sume int hv; 2315101474Sume char pname[NI_MAXHOST], pname2[NI_MAXHOST]; 2316101474Sume 2317101474Sume if (sep->se_maxperip <= 0) 2318101474Sume return NULL; 2319101474Sume 2320101474Sume /* 2321101474Sume * If getpeername() fails, just let it through (if logging is 2322101474Sume * enabled the condition is caught elsewhere) 2323101474Sume */ 2324101474Sume if (getpeername(ctrl, (struct sockaddr *)&ss, &sslen) != 0) 2325101474Sume return NULL; 2326101474Sume 2327101474Sume switch (ss.ss_family) { 2328101474Sume case AF_INET: 2329101474Sume hv = hashval((char *)&((struct sockaddr_in *)&ss)->sin_addr, 2330101474Sume sizeof(struct in_addr)); 2331101474Sume break; 2332101474Sume#ifdef INET6 2333101474Sume case AF_INET6: 2334101474Sume hv = hashval((char *)&((struct sockaddr_in6 *)&ss)->sin6_addr, 2335101474Sume sizeof(struct in6_addr)); 2336101474Sume break; 2337101474Sume#endif 2338101474Sume default: 2339101474Sume /* 2340101474Sume * Since we only support AF_INET and AF_INET6, just 2341101474Sume * let other than AF_INET and AF_INET6 through. 2342101474Sume */ 2343101474Sume return NULL; 2344101474Sume } 2345101474Sume 2346101474Sume if (getnameinfo((struct sockaddr *)&ss, sslen, pname, sizeof(pname), 2347101474Sume NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0) 2348101474Sume return NULL; 2349101474Sume 2350101474Sume LIST_FOREACH(conn, &sep->se_conn[hv], co_link) { 2351101474Sume if (getnameinfo((struct sockaddr *)&conn->co_addr, 2352101474Sume conn->co_addr.ss_len, pname2, sizeof(pname2), NULL, 0, 2353101474Sume NI_NUMERICHOST | NI_WITHSCOPEID) == 0 && 2354101474Sume strcmp(pname, pname2) == 0) 2355101474Sume break; 2356101474Sume } 2357101474Sume 2358101474Sume if (conn == NULL) { 2359101474Sume if ((conn = malloc(sizeof(struct conninfo))) == NULL) { 2360101474Sume syslog(LOG_ERR, "malloc: %m"); 2361101474Sume exit(EX_OSERR); 2362101474Sume } 2363101474Sume conn->co_proc = malloc(sep->se_maxperip * sizeof(*conn->co_proc)); 2364101474Sume if (conn->co_proc == NULL) { 2365101474Sume syslog(LOG_ERR, "malloc: %m"); 2366101474Sume exit(EX_OSERR); 2367101474Sume } 2368101474Sume memcpy(&conn->co_addr, (struct sockaddr *)&ss, sslen); 2369101474Sume conn->co_numchild = 0; 2370101474Sume LIST_INSERT_HEAD(&sep->se_conn[hv], conn, co_link); 2371101474Sume } 2372101474Sume 2373101474Sume /* 2374101474Sume * Since a child process is not invoked yet, we cannot 2375101474Sume * determine a pid of a child. So, co_proc and co_numchild 2376101474Sume * should be filled leter. 2377101474Sume */ 2378101474Sume 2379101474Sume return conn; 2380101474Sume} 2381101474Sume 2382101474Sumestatic int 2383101474Sumeroom_conn(struct servtab *sep, struct conninfo *conn) 2384101474Sume{ 2385101474Sume char pname[NI_MAXHOST]; 2386101474Sume 2387101474Sume if (conn->co_numchild >= sep->se_maxperip) { 2388101474Sume getnameinfo((struct sockaddr *)&conn->co_addr, 2389101474Sume conn->co_addr.ss_len, pname, sizeof(pname), NULL, 0, 2390101474Sume NI_NUMERICHOST | NI_WITHSCOPEID); 2391101474Sume syslog(LOG_ERR, "%s from %s exceeded counts (limit %d)", 2392101474Sume sep->se_service, pname, sep->se_maxperip); 2393101474Sume return 0; 2394101474Sume } 2395101474Sume return 1; 2396101474Sume} 2397101474Sume 2398101474Sumestatic void 2399101474Sumeaddchild_conn(struct conninfo *conn, pid_t pid) 2400101474Sume{ 2401101474Sume struct procinfo *proc; 2402101474Sume 2403101474Sume if (conn == NULL) 2404101474Sume return; 2405101474Sume 2406101474Sume if ((proc = search_proc(pid, 1)) != NULL) { 2407101474Sume if (proc->pr_conn != NULL) { 2408101474Sume syslog(LOG_ERR, 2409101474Sume "addchild_conn: child already on process list"); 2410101474Sume exit(EX_OSERR); 2411101474Sume } 2412101474Sume proc->pr_conn = conn; 2413101474Sume } 2414101474Sume 2415101474Sume conn->co_proc[conn->co_numchild++] = proc; 2416101474Sume} 2417101474Sume 2418101474Sumestatic void 2419101474Sumereapchild_conn(pid_t pid) 2420101474Sume{ 2421101474Sume struct procinfo *proc; 2422101474Sume struct conninfo *conn; 2423101474Sume int i; 2424101474Sume 2425101474Sume if ((proc = search_proc(pid, 0)) == NULL) 2426101474Sume return; 2427101474Sume if ((conn = proc->pr_conn) == NULL) 2428101474Sume return; 2429101474Sume for (i = 0; i < conn->co_numchild; ++i) 2430101474Sume if (conn->co_proc[i] == proc) { 2431101474Sume conn->co_proc[i] = conn->co_proc[--conn->co_numchild]; 2432101474Sume break; 2433101474Sume } 2434101474Sume free_proc(proc); 2435101474Sume free_conn(conn); 2436101474Sume} 2437101474Sume 2438101474Sumestatic void 2439101474Sumeresize_conn(struct servtab *sep, int maxperip) 2440101474Sume{ 2441101474Sume struct conninfo *conn; 2442101474Sume int i, j; 2443101474Sume 2444101474Sume if (sep->se_maxperip <= 0) 2445101474Sume return; 2446101474Sume if (maxperip <= 0) { 2447101474Sume free_connlist(sep); 2448101474Sume return; 2449101474Sume } 2450101474Sume for (i = 0; i < PERIPSIZE; ++i) { 2451101474Sume LIST_FOREACH(conn, &sep->se_conn[i], co_link) { 2452101474Sume for (j = maxperip; j < conn->co_numchild; ++j) 2453101474Sume free_proc(conn->co_proc[j]); 2454101474Sume conn->co_proc = realloc(conn->co_proc, 2455101474Sume maxperip * sizeof(*conn->co_proc)); 2456101474Sume if (conn->co_proc == NULL) { 2457101474Sume syslog(LOG_ERR, "realloc: %m"); 2458101474Sume exit(EX_OSERR); 2459101474Sume } 2460101474Sume if (conn->co_numchild > maxperip) 2461101474Sume conn->co_numchild = maxperip; 2462101474Sume } 2463101474Sume } 2464101474Sume} 2465101474Sume 2466101474Sumestatic void 2467101474Sumefree_connlist(struct servtab *sep) 2468101474Sume{ 2469101474Sume struct conninfo *conn; 2470101474Sume int i, j; 2471101474Sume 2472101474Sume for (i = 0; i < PERIPSIZE; ++i) { 2473101474Sume while ((conn = LIST_FIRST(&sep->se_conn[i])) != NULL) { 2474101474Sume for (j = 0; j < conn->co_numchild; ++j) 2475101474Sume free_proc(conn->co_proc[j]); 2476101474Sume conn->co_numchild = 0; 2477101474Sume free_conn(conn); 2478101474Sume } 2479101474Sume } 2480101474Sume} 2481101474Sume 2482101474Sumestatic void 2483101474Sumefree_conn(struct conninfo *conn) 2484101474Sume{ 2485101474Sume if (conn == NULL) 2486101474Sume return; 2487101474Sume if (conn->co_numchild <= 0) { 2488101474Sume LIST_REMOVE(conn, co_link); 2489101474Sume free(conn->co_proc); 2490101474Sume free(conn); 2491101474Sume } 2492101474Sume} 2493101474Sume 2494101474Sumestatic struct procinfo * 2495101474Sumesearch_proc(pid_t pid, int add) 2496101474Sume{ 2497101474Sume struct procinfo *proc; 2498101474Sume int hv; 2499101474Sume 2500101474Sume hv = hashval((char *)&pid, sizeof(pid)); 2501101474Sume LIST_FOREACH(proc, &proctable[hv], pr_link) { 2502101474Sume if (proc->pr_pid == pid) 2503101474Sume break; 2504101474Sume } 2505101474Sume if (proc == NULL && add) { 2506101474Sume if ((proc = malloc(sizeof(struct procinfo))) == NULL) { 2507101474Sume syslog(LOG_ERR, "malloc: %m"); 2508101474Sume exit(EX_OSERR); 2509101474Sume } 2510101474Sume proc->pr_pid = pid; 2511101474Sume proc->pr_conn = NULL; 2512101474Sume LIST_INSERT_HEAD(&proctable[hv], proc, pr_link); 2513101474Sume } 2514101474Sume return proc; 2515101474Sume} 2516101474Sume 2517101474Sumestatic void 2518101474Sumefree_proc(struct procinfo *proc) 2519101474Sume{ 2520101474Sume if (proc == NULL) 2521101474Sume return; 2522101474Sume LIST_REMOVE(proc, pr_link); 2523101474Sume free(proc); 2524101474Sume} 2525101474Sume 2526101474Sumestatic int 2527101474Sumehashval(char *p, int len) 2528101474Sume{ 2529101474Sume int i, hv = 0xABC3D20F; 2530101474Sume 2531101474Sume for (i = 0; i < len; ++i, ++p) 2532101474Sume hv = (hv << 5) ^ (hv >> 23) ^ *p; 2533101474Sume hv = (hv ^ (hv >> 16)) & (PERIPSIZE - 1); 2534101474Sume return hv; 2535101474Sume} 2536