From 1a2f4d9af7b8ec08db75f245e1b513a47c5535d8 Mon Sep 17 00:00:00 2001 From: "\"Vladimir N. Oleynik\"" Date: Mon, 3 Oct 2005 08:08:58 +0000 Subject: [PATCH] sync inetd with slackware-10.2 inetd (OpenBSD-1.79), have destroy bug 455 --- networking/Config.in | 7 + networking/inetd.c | 2397 +++++++++++++++++++++++++++++++------------------- 2 files changed, 1494 insertions(+), 910 deletions(-) diff --git a/networking/Config.in b/networking/Config.in index 544ee61..be863d3 100644 --- a/networking/Config.in +++ b/networking/Config.in @@ -305,6 +305,13 @@ config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN help Familiar character generator internal inetd service +config CONFIG_FEATURE_INETD_RPC + bool " Support RPC services" + default n + depends on CONFIG_INETD + help + Suuport Sun-RPC based services + config CONFIG_IP bool "ip" diff --git a/networking/inetd.c b/networking/inetd.c index 0604b14..4c16ff3 100644 --- a/networking/inetd.c +++ b/networking/inetd.c @@ -1,26 +1,38 @@ +/* $Slackware: inetd.c 1.79s 2001/02/06 13:18:00 volkerdi Exp $ */ +/* $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $ */ +/* $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $ */ +/* Busybox port by Vladimir Oleynik (C) 2001-2005 */ /* * Copyright (c) 1983,1991 The Regents of the University of California. * All rights reserved. * - * This code is derived from software contributed to Berkeley by - * David A. Holland. - * - * Busybox port by Vladimir Oleynik (C) 2001-2003 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ /* @@ -45,70 +57,88 @@ * Inetd uses a configuration file which is read at startup * and, possibly, at some later time in response to a hangup signal. * The configuration file is ``free format'' with fields given in the - * order shown below. Continuation lines for an entry must being with + * order shown below. Continuation lines for an entry must begin with * a space or tab. All fields must be present in each entry. * * service name must be in /etc/services * socket type stream/dgram/raw/rdm/seqpacket * protocol must be in /etc/protocols * wait/nowait[.max] single-threaded/multi-threaded, max # - * user[.group] user/group to run daemon as + * user[.group] or user[:group] user/group to run daemon as + * server program full path name + * server program arguments maximum of MAXARGS (20) + * + * For RPC services + * service name/version must be in /etc/rpc + * socket type stream/dgram/raw/rdm/seqpacket + * protocol must be in /etc/protocols + * wait/nowait[.max] single-threaded/multi-threaded + * user[.group] or user[:group] user to run daemon as * server program full path name * server program arguments maximum of MAXARGS (20) * - * RPC services unsupported + * For non-RPC services, the "service name" can be of the form + * hostaddress:servicename, in which case the hostaddress is used + * as the host portion of the address to listen on. If hostaddress + * consists of a single `*' character, INADDR_ANY is used. + * + * A line can also consist of just + * hostaddress: + * where hostaddress is as in the preceding paragraph. Such a line must + * have no further fields; the specified hostaddress is remembered and + * used for all further lines that have no hostaddress specified, + * until the next such line (or EOF). (This is why * is provided to + * allow explicit specification of INADDR_ANY.) A line + * *: + * is implicitly in effect at the beginning of the file. + * + * The hostaddress specifier may (and often will) contain dots; + * the service name must not. + * + * For RPC services, host-address specifiers are accepted and will + * work to some extent; however, because of limitations in the + * portmapper interface, it will not work to try to give more than + * one line for any given RPC service, even if the host-address + * specifiers are different. * * Comment lines are indicated by a `#' in column 1. */ /* - * Here's the scoop concerning the user.group feature: + * Here's the scoop concerning the user[.:]group feature: * - * 1) No group listed. + * 1) set-group-option off. * - * a) for root: NO setuid() or setgid() is done + * a) user = root: NO setuid() or setgid() is done * - * b) nonroot: setuid() - * setgid(primary group as found in passwd) + * b) other: setgid(primary group as found in passwd) * initgroups(name, primary group) + * setuid() * * 2) set-group-option on. * - * a) for root: NO setuid() - * setgid(specified group) - * setgroups(1, specified group) + * a) user = root: setgid(specified group) + * NO initgroups() + * NO setuid() * - * b) nonroot: setuid() - * setgid(specified group) + * b) other: setgid(specified group) * initgroups(name, specified group) + * setuid() * - * All supplementary groups are discarded at startup in case inetd was - * run manually. */ -#define __USE_BSD_SIGNAL - -#include "busybox.h" - - -#ifndef __linux__ -#ifndef RLIMIT_NOFILE -#define RLIMIT_NOFILE RLIMIT_OFILE -#endif -#endif - -#include -#include #include -#include -#include #include -#include +#include +#include #include +#include #include +#include +#include + #include -#include #include #include @@ -117,30 +147,51 @@ #include #include #include -#include -#include #include -#include +#include +#include #include -#ifndef OPEN_MAX -#define OPEN_MAX 64 +#include "busybox.h" + +//#define CONFIG_FEATURE_INETD_RPC +//#define CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO +//#define CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD +//#define CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME +//#define CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME +//#define CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN +//#define CONFIG_FEATURE_IPV6 + +#ifdef CONFIG_FEATURE_INETD_RPC +#include +#include +#include #endif #define _PATH_INETDCONF "/etc/inetd.conf" #define _PATH_INETDPID "/var/run/inetd.pid" -#define TOOMANY 40 /* don't start more than TOOMANY */ -#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ -#define RETRYTIME (60*10) /* retry after bind or server fail */ -#define MAXARGV 20 -#define se_ctrladdr se_un.se_un_ctrladdr -#define se_ctrladdr_in se_un.se_un_ctrladdr_in -#define se_ctrladdr_un se_un.se_un_ctrladdr_un +#define TOOMANY 0 /* don't start more than TOOMANY */ + +#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ +#define RETRYTIME (60*10) /* retry after bind or server fail */ + +#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) + +#ifndef RLIMIT_NOFILE +#define RLIMIT_NOFILE RLIMIT_OFILE +#endif + +#ifndef OPEN_MAX +#define OPEN_MAX 64 +#endif /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */ -#define FD_MARGIN (8) +#define FD_MARGIN (8) +static rlim_t rlim_ofile_cur = OPEN_MAX; +static struct rlimit rlim_ofile; + /* Check unsupporting builtin */ #if defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO || \ @@ -151,988 +202,1504 @@ # define INETD_FEATURE_ENABLED #endif -typedef struct servtab_s { - char *se_service; /* name of service */ - int se_socktype; /* type of socket to use */ - int se_family; /* address family */ - char *se_proto; /* protocol used */ - short se_wait; /* single threaded server */ - short se_checked; /* looked at during merge */ - char *se_user; /* user name to run as */ - char *se_group; /* group name to run as */ +#if defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO || \ + defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD || \ + defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN +# define INETD_SETPROCTITLE +#endif + +typedef struct servtab +{ + char *se_hostaddr; /* host address to listen on */ + char *se_service; /* name of service */ + int se_socktype; /* type of socket to use */ + int se_family; /* address family */ + char *se_proto; /* protocol used */ +#ifdef CONFIG_FEATURE_INETD_RPC + int se_rpcprog; /* rpc program number */ + int se_rpcversl; /* rpc program lowest version */ + int se_rpcversh; /* rpc program highest version */ +#define isrpcservice(sep) ((sep)->se_rpcversl != 0) +#else +#define isrpcservice(sep) 0 +#endif + pid_t se_wait; /* single threaded server */ + short se_checked; /* looked at during merge */ + char *se_user; /* user name to run as */ + char *se_group; /* group name to run as */ #ifdef INETD_FEATURE_ENABLED - const struct biltin *se_bi; /* if built-in, description */ + const struct biltin *se_bi; /* if built-in, description */ #endif - char *se_server; /* server program */ - char *se_argv[MAXARGV+1]; /* program arguments */ - int se_fd; /* open descriptor */ - union { - struct sockaddr se_un_ctrladdr; - struct sockaddr_in se_un_ctrladdr_in; - struct sockaddr_un se_un_ctrladdr_un; - } se_un; /* bound address */ - int se_ctrladdr_size; - int se_max; /* max # of instances of this service */ - int se_count; /* number started since se_time */ - struct timeval se_time; /* start of se_count */ - struct servtab_s *se_next; + char *se_server; /* server program */ +#define MAXARGV 20 + char *se_argv[MAXARGV + 1]; /* program arguments */ + int se_fd; /* open descriptor */ + union + { + struct sockaddr se_un_ctrladdr; + struct sockaddr_in se_un_ctrladdr_in; +#ifdef CONFIG_FEATURE_IPV6 + struct sockaddr_in6 se_un_ctrladdr_in6; +#endif + struct sockaddr_un se_un_ctrladdr_un; + } se_un; /* bound address */ +#define se_ctrladdr se_un.se_un_ctrladdr +#define se_ctrladdr_in se_un.se_un_ctrladdr_in +#define se_ctrladdr_in6 se_un.se_un_ctrladdr_in6 +#define se_ctrladdr_un se_un.se_un_ctrladdr_un + int se_ctrladdr_size; + int se_max; /* max # of instances of this service */ + int se_count; /* number started since se_time */ + struct timeval se_time; /* start of se_count */ + struct servtab *se_next; } servtab_t; static servtab_t *servtab; #ifdef INETD_FEATURE_ENABLED -struct biltin { - const char *bi_service; /* internally provided service name */ - int bi_socktype; /* type of socket supported */ - short bi_fork; /* 1 if should fork before call */ - short bi_wait; /* 1 if should wait for child */ - void (*bi_fn)(int, servtab_t *); /* fn which performs it */ +struct biltin +{ + const char *bi_service; /* internally provided service name */ + int bi_socktype; /* type of socket supported */ + short bi_fork; /* 1 if should fork before call */ + short bi_wait; /* 1 if should wait for child */ + void (*bi_fn) (int, servtab_t *); }; - /* Echo received data */ + /* Echo received data */ #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO -static void echo_stream(int, servtab_t *); -static void echo_dg(int, servtab_t *); +static void echo_stream (int, servtab_t *); +static void echo_dg (int, servtab_t *); #endif - /* Internet /dev/null */ + /* Internet /dev/null */ #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD -static void discard_stream(int, servtab_t *); -static void discard_dg(int, servtab_t *); +static void discard_stream (int, servtab_t *); +static void discard_dg (int, servtab_t *); #endif /* Return 32 bit time since 1900 */ #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME -static void machtime_stream(int, servtab_t *); -static void machtime_dg(int, servtab_t *); +static void machtime_stream (int, servtab_t *); +static void machtime_dg (int, servtab_t *); #endif /* Return human-readable time */ #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME -static void daytime_stream(int, servtab_t *); -static void daytime_dg(int, servtab_t *); +static void daytime_stream (int, servtab_t *); +static void daytime_dg (int, servtab_t *); #endif /* Familiar character generator */ #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN -static void chargen_stream(int, servtab_t *); -static void chargen_dg(int, servtab_t *); +static void chargen_stream (int, servtab_t *); +static void chargen_dg (int, servtab_t *); #endif static const struct biltin biltins[] = { #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO - /* Echo received data */ - { "echo", SOCK_STREAM, 1, 0, echo_stream, }, - { "echo", SOCK_DGRAM, 0, 0, echo_dg, }, + /* Echo received data */ + {"echo", SOCK_STREAM, 1, 0, echo_stream,}, + {"echo", SOCK_DGRAM, 0, 0, echo_dg,}, #endif #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD - /* Internet /dev/null */ - { "discard", SOCK_STREAM, 1, 0, discard_stream, }, - { "discard", SOCK_DGRAM, 0, 0, discard_dg, }, + /* Internet /dev/null */ + {"discard", SOCK_STREAM, 1, 0, discard_stream,}, + {"discard", SOCK_DGRAM, 0, 0, discard_dg,}, #endif #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME - /* Return 32 bit time since 1900 */ - { "time", SOCK_STREAM, 0, 0, machtime_stream, }, - { "time", SOCK_DGRAM, 0, 0, machtime_dg, }, + /* Return 32 bit time since 1900 */ + {"time", SOCK_STREAM, 0, 0, machtime_stream,}, + {"time", SOCK_DGRAM, 0, 0, machtime_dg,}, #endif #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME - /* Return human-readable time */ - { "daytime", SOCK_STREAM, 0, 0, daytime_stream, }, - { "daytime", SOCK_DGRAM, 0, 0, daytime_dg, }, + /* Return human-readable time */ + {"daytime", SOCK_STREAM, 0, 0, daytime_stream,}, + {"daytime", SOCK_DGRAM, 0, 0, daytime_dg,}, #endif #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN - /* Familiar character generator */ - { "chargen", SOCK_STREAM, 1, 0, chargen_stream, }, - { "chargen", SOCK_DGRAM, 0, 0, chargen_dg, }, + /* Familiar character generator */ + {"chargen", SOCK_STREAM, 1, 0, chargen_stream,}, + {"chargen", SOCK_DGRAM, 0, 0, chargen_dg,}, #endif - { NULL, 0, 0, 0, NULL } + {NULL, 0, 0, 0, NULL} }; -#endif /* INETD_FEATURE_ENABLED */ +#endif /* INETD_FEATURE_ENABLED */ -#ifdef RLIMIT_NOFILE -static struct rlimit rlim_ofile; -#endif +static int global_queuelen = 128; +static int nsock, maxsock; +static fd_set allsock; +static int toomany = TOOMANY; +static int timingout; +static struct servent *sp; +static uid_t uid; -/* Length of socket listen queue. Should be per-service probably. */ -static int global_queuelen = 128; +static char *CONFIG = _PATH_INETDCONF; static FILE *fconfig; -static sigset_t blockmask; -static sigset_t emptymask; -static fd_set allsock; -static int nsock; -static int maxsock; -static int timingout; -static int rlim_ofile_cur = OPEN_MAX; -static const char *CONFIG = _PATH_INETDCONF; +static char line[1024]; +static char *defhost; -static void -syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) - __attribute__ ((noreturn, format (printf, 2, 3))); +static char *newstr (char *cp) +{ + if ((cp = strdup (cp ? cp : ""))) + return (cp); + syslog (LOG_ERR, "strdup: %m"); + exit (1); +} -static void -syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) +static int setconfig (void) { - char buf[50]; - va_list p; - - va_start(p, msg); - vsyslog(LOG_ERR, msg, p); - if (se_socktype != SOCK_STREAM) - recv(0, buf, sizeof (buf), 0); - _exit(1); + free (defhost); + defhost = newstr ("*"); + if (fconfig != NULL) { + fseek (fconfig, 0L, SEEK_SET); + return (1); + } + fconfig = fopen (CONFIG, "r"); + return (fconfig != NULL); } -static char * inetd_strdup(const char *s) +static void endconfig (void) { - char *ms = strdup(s); + if (fconfig) { + (void) fclose (fconfig); + fconfig = NULL; + } + free (defhost); + defhost = 0; +} - if(ms == NULL) - syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m"); - return ms; +#ifdef CONFIG_FEATURE_INETD_RPC +static void register_rpc (servtab_t *sep) +{ + int n; + struct sockaddr_in ir_sin; + struct protoent *pp; + + if ((pp = getprotobyname (sep->se_proto + 4)) == NULL) { + syslog (LOG_ERR, "%s: getproto: %m", sep->se_proto); + return; + } + n = sizeof ir_sin; + if (getsockname (sep->se_fd, (struct sockaddr *) &ir_sin, &n) < 0) { + syslog (LOG_ERR, "%s/%s: getsockname: %m", + sep->se_service, sep->se_proto); + return; + } + + for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) { + (void) pmap_unset (sep->se_rpcprog, n); + if (!pmap_set (sep->se_rpcprog, n, pp->p_proto, ntohs (ir_sin.sin_port))) + syslog (LOG_ERR, "%s %s: pmap_set: %u %u %u %u: %m", + sep->se_service, sep->se_proto, + sep->se_rpcprog, n, pp->p_proto, ntohs (ir_sin.sin_port)); + } } +static void unregister_rpc (servtab_t *sep) +{ + int n; + + for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) { + if (!pmap_unset (sep->se_rpcprog, n)) + syslog (LOG_ERR, "pmap_unset(%u, %u)", sep->se_rpcprog, n); + } +} +#endif /* CONFIG_FEATURE_INETD_RPC */ -static servtab_t *getconfigent(void) +static void freeconfig (servtab_t *cp) { - static servtab_t serv; - servtab_t *sep = &serv; - int argc; - char *cp = NULL; - char *cp_ptr; - char *cp_ptr_ptr = NULL; + int i; + + free (cp->se_hostaddr); + free (cp->se_service); + free (cp->se_proto); + free (cp->se_user); + free (cp->se_group); + free (cp->se_server); + for (i = 0; i < MAXARGV; i++) + if (cp->se_argv[i]) + free (cp->se_argv[i]); +} -more: - free(cp); - cp = bb_get_chomped_line_from_file(fconfig); - if (feof(fconfig)) { - free(cp); - return (NULL); - } - if ((cp == NULL) || (*cp == '#')) { - goto more; - } - /* make bind 0.0.0.0 and other zero default */ - memset((char *)sep, 0, sizeof *sep); +static int bump_nofile (void) +{ +#define FD_CHUNK 32 + + struct rlimit rl; + + if (getrlimit (RLIMIT_NOFILE, &rl) < 0) { + syslog (LOG_ERR, "getrlimit: %m"); + return -1; + } + rl.rlim_cur = MIN (rl.rlim_max, rl.rlim_cur + FD_CHUNK); + rl.rlim_cur = MIN (FD_SETSIZE, rl.rlim_cur + FD_CHUNK); + if (rl.rlim_cur <= rlim_ofile_cur) { + syslog (LOG_ERR, "bump_nofile: cannot extend file limit, max = %d", + (int) rl.rlim_cur); + return -1; + } + + if (setrlimit (RLIMIT_NOFILE, &rl) < 0) { + syslog (LOG_ERR, "setrlimit: %m"); + return -1; + } + + rlim_ofile_cur = rl.rlim_cur; + return 0; +} - cp_ptr = strtok_r(cp, " \t", &cp_ptr_ptr); - if (cp_ptr == NULL) { - /* Error */ - goto more; - } - sep->se_service = inetd_strdup(cp_ptr); +static void setup (servtab_t *sep) +{ + int on = 1; + int r; + + if ((sep->se_fd = socket (sep->se_family, sep->se_socktype, 0)) < 0) { + syslog (LOG_ERR, "%s/%s: socket: %m", sep->se_service, sep->se_proto); + return; + } +#define turnon(fd, opt) \ +setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) + if (turnon (sep->se_fd, SO_REUSEADDR) < 0) + syslog (LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); +#undef turnon + +#ifdef CONFIG_FEATURE_INETD_RPC + if (isrpcservice (sep)) { + struct passwd *pwd; - cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); - if (cp_ptr == NULL) { - /* Error */ - goto more; - } - if (strcmp(cp_ptr, "stream") == 0) - sep->se_socktype = SOCK_STREAM; - else if (strcmp(cp_ptr, "dgram") == 0) - sep->se_socktype = SOCK_DGRAM; - else if (strcmp(cp_ptr, "rdm") == 0) - sep->se_socktype = SOCK_RDM; - else if (strcmp(cp_ptr, "seqpacket") == 0) - sep->se_socktype = SOCK_SEQPACKET; - else if (strcmp(cp_ptr, "raw") == 0) - sep->se_socktype = SOCK_RAW; - else - sep->se_socktype = -1; - - cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); - if (cp_ptr == NULL) { - /* error */ - goto more; + /* + * for RPC services, attempt to use a reserved port + * if they are going to be running as root. + * + * Also, zero out the port for all RPC services; let bind() + * find one. + */ + sep->se_ctrladdr_in.sin_port = 0; + if (sep->se_user && (pwd = getpwnam (sep->se_user)) && + pwd->pw_uid == 0 && uid == 0) + r = bindresvport (sep->se_fd, &sep->se_ctrladdr_in); + else { + r = bind (sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size); + if (r == 0) { + int len = sep->se_ctrladdr_size; + int saveerrno = errno; + + /* update se_ctrladdr_in.sin_port */ + r = getsockname (sep->se_fd, &sep->se_ctrladdr, &len); + if (r <= 0) + errno = saveerrno; + } } - if (strcmp(cp_ptr, "unix") == 0) { - sep->se_family = AF_UNIX; - } else { - if (strncmp(cp_ptr, "rpc/", 4) == 0) { - syslog(LOG_ERR, "%s: rpc services not supported", - sep->se_service); - goto more; - } - sep->se_family = AF_INET; + } else +#endif + r = bind (sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size); + if (r < 0) { + syslog (LOG_ERR, "%s/%s (%d): bind: %m", + sep->se_service, sep->se_proto, sep->se_ctrladdr.sa_family); + close (sep->se_fd); + sep->se_fd = -1; + if (!timingout) { + timingout = 1; + alarm (RETRYTIME); } - sep->se_proto = inetd_strdup(cp_ptr); + return; + } + if (sep->se_socktype == SOCK_STREAM) + listen (sep->se_fd, global_queuelen); + + FD_SET (sep->se_fd, &allsock); + nsock++; + if (sep->se_fd > maxsock) { + maxsock = sep->se_fd; + if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN) + bump_nofile (); + } +} - cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); - if (cp_ptr == NULL) { - /* error */ - goto more; - } - { - char *s = strchr(cp_ptr, '.'); - if (s) { - *s++ = '\0'; - sep->se_max = atoi(s); - } else - sep->se_max = TOOMANY; - } - sep->se_wait = strcmp(cp_ptr, "wait") == 0; +static char *nextline (void) +{ + char *cp; + FILE *fd = fconfig; + + if (fgets (line, sizeof (line), fd) == NULL) + return (NULL); + cp = strchr (line, '\n'); + if (cp) + *cp = '\0'; + return (line); +} - cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); - if (cp_ptr == NULL) { - /* error */ - goto more; - } +static char *skip (char **cpp) /* int report; */ +{ + char *cp = *cpp; + char *start; + +/* erp: */ + if (*cpp == NULL) { + /* if (report) */ + /* syslog(LOG_ERR, "syntax error in inetd config file"); */ + return (NULL); + } + +again: + while (*cp == ' ' || *cp == '\t') + cp++; + if (*cp == '\0') { + int c; + + c = getc (fconfig); + (void) ungetc (c, fconfig); + if (c == ' ' || c == '\t') + if ((cp = nextline ())) + goto again; + *cpp = NULL; + /* goto erp; */ + return (NULL); + } + start = cp; + while (*cp && *cp != ' ' && *cp != '\t') + cp++; + if (*cp != '\0') + *cp++ = '\0'; + /* if ((*cpp = cp) == NULL) */ + /* goto erp; */ + + *cpp = cp; + return (start); +} - sep->se_user = inetd_strdup(cp_ptr); - { - char *cp_ptr2 = strchr(sep->se_user, '.'); +static servtab_t *new_servtab(void) +{ + servtab_t *sep; + + sep = (servtab_t *) malloc (sizeof (servtab_t)); + if (sep == NULL) { + syslog (LOG_ERR, bb_msg_memory_exhausted); + exit (1); + } + return sep; +} - if (cp_ptr2) { - *cp_ptr2++ = '\0'; - } - sep->se_group = cp_ptr2; - } +static servtab_t *dupconfig (servtab_t *sep) +{ + servtab_t *newtab; + int argc; + + newtab = new_servtab(); + memset (newtab, 0, sizeof (servtab_t)); + newtab->se_service = sep->se_service ? newstr (sep->se_service) : NULL; + newtab->se_socktype = sep->se_socktype; + newtab->se_family = sep->se_family; + newtab->se_proto = sep->se_proto ? newstr (sep->se_proto) : NULL; +#ifdef CONFIG_FEATURE_INETD_RPC + newtab->se_rpcprog = sep->se_rpcprog; + newtab->se_rpcversl = sep->se_rpcversl; + newtab->se_rpcversh = sep->se_rpcversh; +#endif + newtab->se_wait = sep->se_wait; + newtab->se_user = sep->se_user ? newstr (sep->se_user) : NULL; + newtab->se_group = sep->se_group ? newstr (sep->se_group) : NULL; +#ifdef INETD_FEATURE_ENABLED + newtab->se_bi = sep->se_bi; +#endif + newtab->se_server = sep->se_server ? newstr (sep->se_server) : 0; + + for (argc = 0; argc <= MAXARGV; argc++) + newtab->se_argv[argc] = sep->se_argv[argc] ? + newstr (sep->se_argv[argc]) : NULL; + newtab->se_max = sep->se_max; - cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); - if (cp_ptr == NULL) { - /* error */ + return (newtab); +} + +static servtab_t *getconfigent (void) +{ + servtab_t *sep; + int argc; + char *cp, *arg; + char *hostdelim; + servtab_t *nsep; + servtab_t *psep; + + sep = new_servtab(); + + /* memset(sep, 0, sizeof *sep); */ +more: + /* freeconfig(sep); */ + + while ((cp = nextline ()) && *cp == '#'); + if (cp == NULL) { + /* free(sep); */ + return (NULL); + } + + memset ((char *) sep, 0, sizeof *sep); + arg = skip (&cp); + if (arg == NULL) { + /* A blank line. */ + goto more; + } + + /* Check for a host name. */ + hostdelim = strrchr (arg, ':'); + if (hostdelim) { + *hostdelim = '\0'; + sep->se_hostaddr = newstr (arg); + arg = hostdelim + 1; + /* + * If the line is of the form `host:', then just change the + * default host for the following lines. + */ + if (*arg == '\0') { + arg = skip (&cp); + if (cp == NULL) { + free (defhost); + defhost = sep->se_hostaddr; goto more; + } } - if (strcmp(cp_ptr, "internal") == 0) { -#ifdef INETD_FEATURE_ENABLED - const struct biltin *bi; - - for (bi = biltins; bi->bi_service; bi++) { - if ((bi->bi_socktype == sep->se_socktype) && - (strcmp(bi->bi_service, sep->se_service) == 0)) { - break; - } - } - if (bi->bi_service == 0) { - syslog(LOG_ERR, "internal service %s unknown", sep->se_service); - goto more; - } - sep->se_bi = bi; - sep->se_wait = bi->bi_wait; + } else + sep->se_hostaddr = newstr (defhost); + + sep->se_service = newstr (arg); + arg = skip (&cp); + + if (strcmp (arg, "stream") == 0) + sep->se_socktype = SOCK_STREAM; + else if (strcmp (arg, "dgram") == 0) + sep->se_socktype = SOCK_DGRAM; + else if (strcmp (arg, "rdm") == 0) + sep->se_socktype = SOCK_RDM; + else if (strcmp (arg, "seqpacket") == 0) + sep->se_socktype = SOCK_SEQPACKET; + else if (strcmp (arg, "raw") == 0) + sep->se_socktype = SOCK_RAW; + else + sep->se_socktype = -1; + + sep->se_proto = newstr (skip (&cp)); + + if (strcmp (sep->se_proto, "unix") == 0) { + sep->se_family = AF_UNIX; + } else { + sep->se_family = AF_INET; + if (sep->se_proto[strlen (sep->se_proto) - 1] == '6') +#ifdef CONFIG_FEATURE_IPV6 + sep->se_family = AF_INET6; #else - syslog(LOG_ERR, "internal service %s unknown", cp_ptr); + syslog (LOG_ERR, "%s: IPV6 not supported", sep->se_proto); +#endif + if (strncmp (sep->se_proto, "rpc/", 4) == 0) { +#ifdef CONFIG_FEATURE_INETD_RPC + char *p, *ccp; + long l; + + p = strchr (sep->se_service, '/'); + if (p == 0) { + syslog (LOG_ERR, "%s: no rpc version", sep->se_service); + goto more; + } + *p++ = '\0'; + l = strtol (p, &ccp, 0); + if (ccp == p || l < 0 || l > INT_MAX) { + badafterall: + syslog (LOG_ERR, "%s/%s: bad rpc version", sep->se_service, p); goto more; + } + sep->se_rpcversl = sep->se_rpcversh = l; + if (*ccp == '-') { + p = ccp + 1; + l = strtol (p, &ccp, 0); + if (ccp == p || l < 0 || l > INT_MAX || l < sep->se_rpcversl || *ccp) + goto badafterall; + sep->se_rpcversh = l; + } else if (*ccp != '\0') + goto badafterall; +#else + syslog (LOG_ERR, "%s: rpc services not supported", sep->se_service); #endif } + } + arg = skip (&cp); + if (arg == NULL) + goto more; + + { + char *s = strchr (arg, '.'); + if (s) { + *s++ = '\0'; + sep->se_max = atoi (s); + } else + sep->se_max = toomany; + } + sep->se_wait = strcmp (arg, "wait") == 0; + /* if ((arg = skip(&cp, 1)) == NULL) */ + /* goto more; */ + sep->se_user = newstr (skip (&cp)); + arg = strchr (sep->se_user, '.'); + if (arg == NULL) + arg = strchr (sep->se_user, ':'); + if (arg) { + *arg++ = '\0'; + sep->se_group = newstr (arg); + } + /* if ((arg = skip(&cp, 1)) == NULL) */ + /* goto more; */ + + sep->se_server = newstr (skip (&cp)); + if (strcmp (sep->se_server, "internal") == 0) { #ifdef INETD_FEATURE_ENABLED - else { - sep->se_bi = NULL; + const struct biltin *bi; + + for (bi = biltins; bi->bi_service; bi++) + if (bi->bi_socktype == sep->se_socktype && + strcmp (bi->bi_service, sep->se_service) == 0) + break; + if (bi->bi_service == 0) { + syslog (LOG_ERR, "internal service %s unknown", sep->se_service); + goto more; } + sep->se_bi = bi; + sep->se_wait = bi->bi_wait; +#else + syslog (LOG_ERR, "internal service %s unknown", sep->se_service); + goto more; +#endif + } +#ifdef INETD_FEATURE_ENABLED + else + sep->se_bi = NULL; #endif - sep->se_server = inetd_strdup(cp_ptr); + argc = 0; + for (arg = skip (&cp); cp; arg = skip (&cp)) { + if (argc < MAXARGV) + sep->se_argv[argc++] = newstr (arg); + } + while (argc <= MAXARGV) + sep->se_argv[argc++] = NULL; + + /* + * Now that we've processed the entire line, check if the hostname + * specifier was a comma separated list of hostnames. If so + * we'll make new entries for each address. + */ + while ((hostdelim = strrchr (sep->se_hostaddr, ',')) != NULL) { + nsep = dupconfig (sep); - argc = 0; - while ((cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr)) != NULL) { - if (argc < MAXARGV) { - sep->se_argv[argc++] = inetd_strdup(cp_ptr); + /* + * NULL terminate the hostname field of the existing entry, + * and make a dup for the new entry. + */ + *hostdelim++ = '\0'; + nsep->se_hostaddr = newstr (hostdelim); + + nsep->se_next = sep->se_next; + sep->se_next = nsep; + } + + nsep = sep; + while (nsep != NULL) { + nsep->se_checked = 1; + if (nsep->se_family == AF_INET) { + if (!strcmp (nsep->se_hostaddr, "*")) + nsep->se_ctrladdr_in.sin_addr.s_addr = INADDR_ANY; + else if (!inet_aton (nsep->se_hostaddr, &nsep->se_ctrladdr_in.sin_addr)) { + struct hostent *hp; + + hp = gethostbyname (nsep->se_hostaddr); + if (hp == 0) { + syslog (LOG_ERR, "%s: unknown host", nsep->se_hostaddr); + nsep->se_checked = 0; + goto skip; + } else if (hp->h_addrtype != AF_INET) { + syslog (LOG_ERR, + "%s: address isn't an Internet " + "address", nsep->se_hostaddr); + nsep->se_checked = 0; + goto skip; + } else { + int i = 1; + + memmove (&nsep->se_ctrladdr_in.sin_addr, + hp->h_addr_list[0], sizeof (struct in_addr)); + while (hp->h_addr_list[i] != NULL) { + psep = dupconfig (nsep); + psep->se_hostaddr = newstr (nsep->se_hostaddr); + psep->se_checked = 1; + memmove (&psep->se_ctrladdr_in.sin_addr, + hp->h_addr_list[i], sizeof (struct in_addr)); + psep->se_ctrladdr_size = sizeof (psep->se_ctrladdr_in); + i++; + /* Prepend to list, don't want to look up its */ + /* hostname again. */ + psep->se_next = sep; + sep = psep; + } } + } + } +/* XXX BUG?: is this skip: label supposed to remain? */ + skip: + nsep = nsep->se_next; + } + + /* + * Finally, free any entries which failed the gethostbyname + * check. + */ + psep = NULL; + nsep = sep; + while (nsep != NULL) { + servtab_t *tsep; + + if (nsep->se_checked == 0) { + tsep = nsep; + if (psep == NULL) { + sep = nsep->se_next; + nsep = sep; + } else { + nsep = nsep->se_next; + psep->se_next = nsep; + } + freeconfig (tsep); + } else { + nsep->se_checked = 0; + psep = nsep; + nsep = nsep->se_next; } - free(cp); + } - return (sep); + return (sep); } -static void freeconfig(servtab_t *cp) +static servtab_t *enter (servtab_t *cp) { - int i; - - free(cp->se_service); - free(cp->se_proto); - free(cp->se_user); - /* Note: se_group is part of the newstr'ed se_user */ - free(cp->se_server); - for (i = 0; i < MAXARGV; i++) - free(cp->se_argv[i]); + servtab_t *sep; + int omask; + + sep = new_servtab(); + *sep = *cp; + sep->se_fd = -1; +#ifdef CONFIG_FEATURE_INETD_RPC + sep->se_rpcprog = -1; +#endif + omask = sigblock (SIGBLOCK); + sep->se_next = servtab; + servtab = sep; + sigsetmask (omask); + return (sep); } -#ifdef INETD_FEATURE_ENABLED -static char **Argv; -static char *LastArg; - -static void setproctitle(char *a, int s) +static int matchconf (servtab_t *old, servtab_t *new) { - socklen_t size; - char *cp; - struct sockaddr_in sn; - char buf[80]; - - cp = Argv[0]; - size = sizeof(sn); - if (getpeername(s, (struct sockaddr *)&sn, &size) == 0) - (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr)); - else - (void) sprintf(buf, "-%s", a); - strncpy(cp, buf, LastArg - cp); - cp += strlen(cp); - while (cp < LastArg) - *cp++ = ' '; + if (strcmp (old->se_service, new->se_service) != 0) + return (0); + + if (strcmp (old->se_hostaddr, new->se_hostaddr) != 0) + return (0); + + if (strcmp (old->se_proto, new->se_proto) != 0) + return (0); + + /* + * If the new servtab is bound to a specific address, check that the + * old servtab is bound to the same entry. If the new service is not + * bound to a specific address then the check of se_hostaddr above + * is sufficient. + */ + + if (old->se_family == AF_INET && new->se_family == AF_INET && + memcmp (&old->se_ctrladdr_in.sin_addr, + &new->se_ctrladdr_in.sin_addr, + sizeof (new->se_ctrladdr_in.sin_addr)) != 0) + return (0); + +#ifdef CONFIG_FEATURE_IPV6 + if (old->se_family == AF_INET6 && new->se_family == AF_INET6 && + memcmp (&old->se_ctrladdr_in6.sin6_addr, + &new->se_ctrladdr_in6.sin6_addr, + sizeof (new->se_ctrladdr_in6.sin6_addr)) != 0) + return (0); +#endif + return (1); } -#endif /* INETD_FEATURE_ENABLED */ - -static void setup(servtab_t *sep) +static void config (int sig __attribute__((unused))) { - int on = 1; + servtab_t *sep, *cp, **sepp; + int omask; + int add; + size_t n; + char protoname[10]; + + if (!setconfig ()) { + syslog (LOG_ERR, "%s: %m", CONFIG); + return; + } + for (sep = servtab; sep; sep = sep->se_next) + sep->se_checked = 0; + cp = getconfigent (); + while (cp != NULL) { + for (sep = servtab; sep; sep = sep->se_next) + if (matchconf (sep, cp)) + break; + add = 0; + if (sep != 0) { + int i; + +#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;} + + omask = sigblock (SIGBLOCK); + /* + * sep->se_wait may be holding the pid of a daemon + * that we're waiting for. If so, don't overwrite + * it unless the config file explicitly says don't + * wait. + */ + if ( +#ifdef INETD_FEATURE_ENABLED + cp->se_bi == 0 && +#endif + (sep->se_wait == 1 || cp->se_wait == 0)) + sep->se_wait = cp->se_wait; + SWAP (int, cp->se_max, sep->se_max); + SWAP (char *, sep->se_user, cp->se_user); + SWAP (char *, sep->se_group, cp->se_group); + SWAP (char *, sep->se_server, cp->se_server); + for (i = 0; i < MAXARGV; i++) + SWAP (char *, sep->se_argv[i], cp->se_argv[i]); +#undef SWAP - if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { - syslog(LOG_ERR, "%s/%s: socket: %m", - sep->se_service, sep->se_proto); - return; +#ifdef CONFIG_FEATURE_INETD_RPC + if (isrpcservice (sep)) + unregister_rpc (sep); + sep->se_rpcversl = cp->se_rpcversl; + sep->se_rpcversh = cp->se_rpcversh; +#endif + sigsetmask (omask); + freeconfig (cp); + add = 1; + } else { + sep = enter (cp); } - if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, - sizeof(on)) < 0) - syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); - if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) { - syslog(LOG_ERR, "%s/%s: bind: %m", - sep->se_service, sep->se_proto); - (void) close(sep->se_fd); - sep->se_fd = -1; - if (!timingout) { - timingout = 1; - alarm(RETRYTIME); + sep->se_checked = 1; + + switch (sep->se_family) { + case AF_UNIX: + if (sep->se_fd != -1) + break; + (void) unlink (sep->se_service); + n = strlen (sep->se_service); + if (n > sizeof sep->se_ctrladdr_un.sun_path - 1) + n = sizeof sep->se_ctrladdr_un.sun_path - 1; + safe_strncpy (sep->se_ctrladdr_un.sun_path, sep->se_service, n + 1); + sep->se_ctrladdr_un.sun_family = AF_UNIX; + sep->se_ctrladdr_size = n + sizeof sep->se_ctrladdr_un.sun_family; + setup (sep); + break; + case AF_INET: + sep->se_ctrladdr_in.sin_family = AF_INET; + /* se_ctrladdr_in was set in getconfigent */ + sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in; + +#ifdef CONFIG_FEATURE_INETD_RPC + if (isrpcservice (sep)) { + struct rpcent *rp; + + sep->se_rpcprog = atoi (sep->se_service); + if (sep->se_rpcprog == 0) { + rp = getrpcbyname (sep->se_service); + if (rp == 0) { + syslog (LOG_ERR, "%s: unknown rpc service", sep->se_service); + goto serv_unknown; + } + sep->se_rpcprog = rp->r_number; } - return; - } - if (sep->se_socktype == SOCK_STREAM) - listen(sep->se_fd, global_queuelen); - - FD_SET(sep->se_fd, &allsock); - nsock++; - if (sep->se_fd > maxsock) { - maxsock = sep->se_fd; - if (maxsock > rlim_ofile_cur - FD_MARGIN) { -#ifdef RLIMIT_NOFILE -# define FD_CHUNK 32 - struct rlimit rl; - - if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { - syslog(LOG_ERR, "getrlimit: %m"); - return; - } - rl.rlim_cur = rl.rlim_max < (rl.rlim_cur + FD_CHUNK) ? rl.rlim_max : (rl.rlim_cur + FD_CHUNK); - if (rl.rlim_cur <= rlim_ofile_cur) { - syslog(LOG_ERR, -# if _FILE_OFFSET_BITS == 64 - "bump_nofile: cannot extend file limit, max = %lld", -# else - "bump_nofile: cannot extend file limit, max = %ld", -# endif - rl.rlim_cur); - return; - } - - if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { - syslog(LOG_ERR, "setrlimit: %m"); - return; - } - - rlim_ofile_cur = rl.rlim_cur; - return; -#else - syslog(LOG_ERR, "bump_nofile: cannot extend file limit"); - return; -#endif /* RLIMIT_NOFILE */ + if (sep->se_fd == -1) + setup (sep); + if (sep->se_fd != -1) + register_rpc (sep); + } else +#endif + { + u_short port = htons (atoi (sep->se_service)); + + if (!port) { + /*XXX*/ strncpy (protoname, sep->se_proto, sizeof (protoname)); + if (isdigit (protoname[strlen (protoname) - 1])) + protoname[strlen (protoname) - 1] = '\0'; + sp = getservbyname (sep->se_service, protoname); + if (sp == 0) { + syslog (LOG_ERR, + "%s/%s: unknown service", sep->se_service, sep->se_proto); + goto serv_unknown; + } + port = sp->s_port; } - } -} - -static void config(int signum) -{ - servtab_t *sep, *cp, **sepp; - sigset_t oldmask; - unsigned n; - - (void)signum; - - if (fconfig != NULL) { - fseek(fconfig, 0L, L_SET); - } else { - fconfig = fopen(CONFIG, "r"); - if (fconfig == NULL) { - syslog(LOG_ERR, "%s: %m", CONFIG); - return; + if (port != sep->se_ctrladdr_in.sin_port) { + sep->se_ctrladdr_in.sin_port = port; + if (sep->se_fd != -1) { + FD_CLR (sep->se_fd, &allsock); + nsock--; + (void) close (sep->se_fd); + } + sep->se_fd = -1; } - } - - for (sep = servtab; sep; sep = sep->se_next) - sep->se_checked = 0; - while ((cp = getconfigent()) != NULL) { - for (sep = servtab; sep; sep = sep->se_next) - if (strcmp(sep->se_service, cp->se_service) == 0 && - strcmp(sep->se_proto, cp->se_proto) == 0) - break; - if (sep != 0) { - int i; - -#define SWAP(type, a, b) {type c=(type)(a); (a)=(type)(b); (b)=(type)c;} - - sigprocmask(SIG_BLOCK, &emptymask, &oldmask); - /* - * sep->se_wait may be holding the pid of a daemon - * that we're waiting for. If so, don't overwrite - * it unless the config file explicitly says don't - * wait. - */ - if ( -#ifdef INETD_FEATURE_ENABLED - cp->se_bi == 0 && + if (sep->se_fd == -1) + setup (sep); + } + break; +#ifdef CONFIG_FEATURE_IPV6 + case AF_INET6: + sep->se_ctrladdr_in6.sin6_family = AF_INET6; + /* se_ctrladdr_in was set in getconfigent */ + sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in6; + +#ifdef CONFIG_FEATURE_INETD_RPC + if (isrpcservice (sep)) { + struct rpcent *rp; + + sep->se_rpcprog = atoi (sep->se_service); + if (sep->se_rpcprog == 0) { + rp = getrpcbyname (sep->se_service); + if (rp == 0) { + syslog (LOG_ERR, "%s: unknown rpc service", sep->se_service); + goto serv_unknown; + } + sep->se_rpcprog = rp->r_number; + } + if (sep->se_fd == -1) + setup (sep); + if (sep->se_fd != -1) + register_rpc (sep); + } else #endif - (sep->se_wait == 1 || cp->se_wait == 0)) - sep->se_wait = cp->se_wait; - if (cp->se_max != sep->se_max) - SWAP(int, cp->se_max, sep->se_max); - if (cp->se_user) - SWAP(char *, sep->se_user, cp->se_user); - if (cp->se_group) - SWAP(char *, sep->se_group, cp->se_group); - if (cp->se_server) - SWAP(char *, sep->se_server, cp->se_server); - for (i = 0; i < MAXARGV; i++) - SWAP(char *, sep->se_argv[i], cp->se_argv[i]); -#undef SWAP - sigprocmask(SIG_SETMASK, &oldmask, NULL); - // This freeconfig() is probably a bug, since it will try and free() - // each of the argv[] values, which are really just pointers - // into the middle of a single line buffer for the config file. - //freeconfig(cp); // BUG? - } else { - sep = (servtab_t *)xmalloc(sizeof (*sep)); - *sep = *cp; - sep->se_fd = -1; - sigprocmask(SIG_BLOCK, &blockmask, &oldmask); - sep->se_next = servtab; - servtab = sep; - sigprocmask(SIG_SETMASK, &oldmask, NULL); + { + u_short port = htons (atoi (sep->se_service)); + + if (!port) { + /*XXX*/ strncpy (protoname, sep->se_proto, sizeof (protoname)); + if (isdigit (protoname[strlen (protoname) - 1])) + protoname[strlen (protoname) - 1] = '\0'; + sp = getservbyname (sep->se_service, protoname); + if (sp == 0) { + syslog (LOG_ERR, + "%s/%s: unknown service", sep->se_service, sep->se_proto); + goto serv_unknown; + } + port = sp->s_port; } - sep->se_checked = 1; - - switch (sep->se_family) { - case AF_UNIX: - if (sep->se_fd != -1) - break; - (void)unlink(sep->se_service); - n = strlen(sep->se_service); - if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1) - n = sizeof(sep->se_ctrladdr_un.sun_path) - 1; - strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n); - sep->se_ctrladdr_un.sun_family = AF_UNIX; - sep->se_ctrladdr_size = n + - sizeof sep->se_ctrladdr_un.sun_family; - setup(sep); - break; - case AF_INET: - sep->se_ctrladdr_in.sin_family = AF_INET; - sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in; - { - u_short port = bb_lookup_port(sep->se_service, sep->se_proto, 0); - - if (port == 0) { - syslog(LOG_ERR, - "%s/%s: unknown service", - sep->se_service, sep->se_proto); - continue; - } - if (port != sep->se_ctrladdr_in.sin_port) { - sep->se_ctrladdr_in.sin_port = port; - if (sep->se_fd != -1) { - FD_CLR(sep->se_fd, &allsock); - nsock--; - (void) close(sep->se_fd); - } - sep->se_fd = -1; - } - if (sep->se_fd == -1) - setup(sep); - } + if (port != sep->se_ctrladdr_in6.sin6_port) { + sep->se_ctrladdr_in6.sin6_port = port; + if (sep->se_fd != -1) { + FD_CLR (sep->se_fd, &allsock); + nsock--; + (void) close (sep->se_fd); + } + sep->se_fd = -1; } + if (sep->se_fd == -1) + setup (sep); + } + break; +#endif /* CONFIG_FEATURE_IPV6 */ } - if (fconfig) { - (void) fclose(fconfig); - fconfig = NULL; + serv_unknown: + if (cp->se_next != NULL) { + servtab_t *tmp = cp; + + cp = cp->se_next; + free (tmp); + } else { + free (cp); + cp = getconfigent (); } - /* - * Purge anything not looked at above. - */ - sigprocmask(SIG_SETMASK, &blockmask, &oldmask); - sepp = &servtab; - while ((sep = *sepp) != NULL) { - if (sep->se_checked) { - sepp = &sep->se_next; - continue; - } - *sepp = sep->se_next; - if (sep->se_fd != -1) { - FD_CLR(sep->se_fd, &allsock); - nsock--; - (void) close(sep->se_fd); - } - if (sep->se_family == AF_UNIX) - (void)unlink(sep->se_service); - freeconfig(sep); - free((char *)sep); + } + endconfig (); + /* + * Purge anything not looked at above. + */ + omask = sigblock (SIGBLOCK); + sepp = &servtab; + while ((sep = *sepp)) { + if (sep->se_checked) { + sepp = &sep->se_next; + continue; + } + *sepp = sep->se_next; + if (sep->se_fd != -1) { + FD_CLR (sep->se_fd, &allsock); + nsock--; + (void) close (sep->se_fd); } - sigprocmask(SIG_SETMASK, &oldmask, NULL); +#ifdef CONFIG_FEATURE_INETD_RPC + if (isrpcservice (sep)) + unregister_rpc (sep); +#endif + if (sep->se_family == AF_UNIX) + (void) unlink (sep->se_service); + freeconfig (sep); + free (sep); + } + (void) sigsetmask (omask); } - -static void reapchild(int signum) +static void reapchild (int sig __attribute__((unused))) { - int status; - int pid; - servtab_t *sep; - - (void)signum; - for (;;) { - pid = wait3(&status, WNOHANG, (struct rusage *)0); - if (pid <= 0) - break; - for (sep = servtab; sep; sep = sep->se_next) - if (sep->se_wait == pid) { - if (WIFEXITED(status) && WEXITSTATUS(status)) - syslog(LOG_WARNING, - "%s: exit status 0x%x", - sep->se_server, WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - syslog(LOG_WARNING, - "%s: exit signal 0x%x", - sep->se_server, WTERMSIG(status)); - sep->se_wait = 1; - FD_SET(sep->se_fd, &allsock); - nsock++; - } - } + pid_t pid; + int save_errno = errno, status; + servtab_t *sep; + + for (;;) { + pid = wait3 (&status, WNOHANG, NULL); + if (pid <= 0) + break; + for (sep = servtab; sep; sep = sep->se_next) + if (sep->se_wait == pid) { + if (WIFEXITED (status) && WEXITSTATUS (status)) + syslog (LOG_WARNING, + "%s: exit status 0x%x", + sep->se_server, WEXITSTATUS (status)); + else if (WIFSIGNALED (status)) + syslog (LOG_WARNING, + "%s: exit signal 0x%x", sep->se_server, WTERMSIG (status)); + sep->se_wait = 1; + FD_SET (sep->se_fd, &allsock); + nsock++; + } + } + errno = save_errno; } -static void retry(int signum) +static void retry (int sig __attribute__((unused))) { - servtab_t *sep; - - (void)signum; - timingout = 0; - for (sep = servtab; sep; sep = sep->se_next) { - if (sep->se_fd == -1) { - switch (sep->se_family) { - case AF_UNIX: - case AF_INET: - setup(sep); - break; - } - } + servtab_t *sep; + + timingout = 0; + for (sep = servtab; sep; sep = sep->se_next) { + if (sep->se_fd == -1) { + switch (sep->se_family) { + case AF_UNIX: + case AF_INET: +#ifdef CONFIG_FEATURE_IPV6 + case AF_INET6: +#endif + setup (sep); +#ifdef CONFIG_FEATURE_INETD_RPC + if (sep->se_fd != -1 && isrpcservice (sep)) + register_rpc (sep); +#endif + break; + } } + } } -static void goaway(int signum) +static void goaway (int sig __attribute__((unused))) { - servtab_t *sep; - - (void)signum; - for (sep = servtab; sep; sep = sep->se_next) - if (sep->se_fd != -1 && sep->se_family == AF_UNIX) - (void)unlink(sep->se_service); - (void)unlink(_PATH_INETDPID); - exit(0); + servtab_t *sep; + + /* XXX signal race walking sep list */ + for (sep = servtab; sep; sep = sep->se_next) { + if (sep->se_fd == -1) + continue; + + switch (sep->se_family) { + case AF_UNIX: + (void) unlink (sep->se_service); + break; + case AF_INET: +#ifdef CONFIG_FEATURE_IPV6 + case AF_INET6: +#endif +#ifdef CONFIG_FEATURE_INETD_RPC + if (sep->se_wait == 1 && isrpcservice (sep)) + unregister_rpc (sep); /* XXX signal race */ +#endif + break; + } + (void) close (sep->se_fd); + } + (void) unlink (_PATH_INETDPID); + exit (0); } +#ifdef INETD_SETPROCTITLE +static char **Argv; +static char *LastArg; -extern int inetd_main(int argc, char *argv[]) +static void +inetd_setproctitle (char *a, int s) { - servtab_t *sep; - struct group *grp = NULL; - struct sigaction sa; - int pid; - unsigned long opt; - char *sq; - gid_t gid; - -#ifdef INETD_FEATURE_ENABLED - extern char **environ; + int size; + char *cp; + struct sockaddr_in prt_sin; + char buf[80]; + + cp = Argv[0]; + size = sizeof (prt_sin); + (void) snprintf (buf, sizeof buf, "-%s", a); + if (getpeername (s, (struct sockaddr *) &prt_sin, &size) == 0) { + char *sa = inet_ntoa (prt_sin.sin_addr); + + buf[sizeof (buf) - 1 - strlen (sa) - 3] = '\0'; + strcat (buf, " ["); + strcat (buf, sa); + strcat (buf, "]"); + } + strncpy (cp, buf, LastArg - cp); + cp += strlen (cp); + while (cp < LastArg) + *cp++ = ' '; +} #endif - gid = getgid(); - setgroups(1, &gid); -#ifdef INETD_FEATURE_ENABLED - Argv = argv; - if (environ == 0 || *environ == 0) - environ = argv; - while (*environ) - environ++; - LastArg = environ[-1] + strlen(environ[-1]); +int +inetd_main (int argc, char *argv[]) +{ + servtab_t *sep; + struct passwd *pwd; + struct group *grp = NULL; + int tmpint; + struct sigaction sa, sapipe; + int opt; + pid_t pid; + char buf[50]; + char *stoomany; + +#ifdef INETD_SETPROCTITLE + extern char **environ; + char **envp = environ; + + Argv = argv; + if (envp == 0 || *envp == 0) + envp = argv; + while (*envp) + envp++; + LastArg = envp[-1] + strlen (envp[-1]); #endif - opt = bb_getopt_ulflags(argc, argv, "q:f", &sq); - if (!(opt & 2)) { - daemon(0, 0); + openlog (bb_applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + + opt = bb_getopt_ulflags (argc, argv, "R:f", &stoomany); + if(opt & 1) { + char *e; + + toomany = strtoul (stoomany, &e, 0); + if (!(toomany >= 0 && *e == '\0')) { + toomany = TOOMANY; + syslog (LOG_ERR, "-R %s: bad value for service invocation rate", stoomany); + } + } + argc -= optind; + argv += optind; + + uid = getuid (); + if (uid != 0) + CONFIG = NULL; + if (argc > 0) + CONFIG = argv[0]; + if (CONFIG == NULL) + bb_error_msg_and_die ("non-root must specify a config file"); + + if (!(opt & 2)) { #if defined(__uClinux__) - /* reexec for vfork() do continue parent */ - vfork_daemon_rexec(argc, argv, "-f"); + /* reexec for vfork() do continue parent */ + vfork_daemon_rexec (0, 0, argc, argv, "-f"); +#else + daemon (0, 0); #endif /* uClinux */ - } + } else { + setsid (); + } - if(opt & 1) { - global_queuelen = atoi(sq); - if (global_queuelen < 8) global_queuelen=8; - } - argc -= optind; - argv += optind; + if (uid == 0) { + gid_t gid = getgid (); - if (argc > 0) - CONFIG = argv[0]; + /* If run by hand, ensure groups vector gets trashed */ + setgroups (1, &gid); + } - openlog(bb_applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON); - { - FILE *fp; + { + FILE *fp; - if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) { - fprintf(fp, "%u\n", getpid()); - (void)fclose(fp); - } + if ((fp = fopen (_PATH_INETDPID, "w")) != NULL) { + fprintf (fp, "%u\n", getpid ()); + (void) fclose (fp); } + } + + if (getrlimit (RLIMIT_NOFILE, &rlim_ofile) < 0) { + syslog (LOG_ERR, "getrlimit: %m"); + } else { + rlim_ofile_cur = rlim_ofile.rlim_cur; + if (rlim_ofile_cur == RLIM_INFINITY) /* ! */ + rlim_ofile_cur = OPEN_MAX; + } + + memset ((char *) &sa, 0, sizeof (sa)); + sigemptyset (&sa.sa_mask); + sigaddset (&sa.sa_mask, SIGALRM); + sigaddset (&sa.sa_mask, SIGCHLD); + sigaddset (&sa.sa_mask, SIGHUP); + sa.sa_handler = retry; + sigaction (SIGALRM, &sa, NULL); + /* doconfig(); */ + config (SIGHUP); + sa.sa_handler = config; + sigaction (SIGHUP, &sa, NULL); + sa.sa_handler = reapchild; + sigaction (SIGCHLD, &sa, NULL); + sa.sa_handler = goaway; + sigaction (SIGTERM, &sa, NULL); + sa.sa_handler = goaway; + sigaction (SIGINT, &sa, NULL); + sa.sa_handler = SIG_IGN; + sigaction (SIGPIPE, &sa, &sapipe); + + { + /* space for daemons to overwrite environment for ps */ +#define DUMMYSIZE 100 + char dummy[DUMMYSIZE]; -#ifdef RLIMIT_NOFILE - if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { - syslog(LOG_ERR, "getrlimit: %m"); - } else { - rlim_ofile_cur = rlim_ofile.rlim_cur; - if (rlim_ofile_cur == RLIM_INFINITY) /* ! */ - rlim_ofile_cur = OPEN_MAX; - } -#endif + (void) memset (dummy, 'x', DUMMYSIZE - 1); + dummy[DUMMYSIZE - 1] = '\0'; - config(0); - - sigemptyset(&emptymask); - sigemptyset(&blockmask); - sigaddset(&blockmask, SIGCHLD); - sigaddset(&blockmask, SIGHUP); - sigaddset(&blockmask, SIGALRM); - - memset(&sa, 0, sizeof(sa)); - sa.sa_mask = blockmask; - sa.sa_handler = retry; - sigaction(SIGALRM, &sa, NULL); - sa.sa_handler = config; - sigaction(SIGHUP, &sa, NULL); - sa.sa_handler = reapchild; - sigaction(SIGCHLD, &sa, NULL); - sa.sa_handler = goaway; - sigaction(SIGTERM, &sa, NULL); - sa.sa_handler = goaway; - sigaction(SIGINT, &sa, NULL); - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, NULL); - - { - /* space for daemons to overwrite environment for ps */ -#define DUMMYSIZE 100 - char dummy[DUMMYSIZE]; + (void) setenv ("inetd_dummy", dummy, 1); + } - (void)memset(dummy, 'x', DUMMYSIZE - 1); - dummy[DUMMYSIZE - 1] = '\0'; + for (;;) { + int n, ctrl = -1; + fd_set readable; - (void)setenv("inetd_dummy", dummy, 1); + if (nsock == 0) { + (void) sigblock (SIGBLOCK); + while (nsock == 0) + sigpause (0L); + (void) sigsetmask (0L); } - for (;;) { - fd_set readable; - int ctrl; - int n; - - if (nsock == 0) { - sigprocmask(SIG_BLOCK, &blockmask, NULL); - while (nsock == 0) { - sigsuspend(&emptymask); + readable = allsock; + if ((n = select (maxsock + 1, &readable, NULL, NULL, NULL)) <= 0) { + if (n < 0 && errno != EINTR) { + syslog (LOG_WARNING, "select: %m"); + sleep (1); + } + continue; + } + for (sep = servtab; n && sep; sep = sep->se_next) + if (sep->se_fd != -1 && FD_ISSET (sep->se_fd, &readable)) { + n--; + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { + ctrl = accept (sep->se_fd, NULL, NULL); + if (ctrl < 0) { + if (errno == EINTR) + continue; + syslog (LOG_WARNING, "accept (for %s): %m", sep->se_service); + continue; + } + if (sep->se_family == AF_INET && sep->se_socktype == SOCK_STREAM) { + struct sockaddr_in peer; + int plen = sizeof (peer); + + if (getpeername (ctrl, (struct sockaddr *) &peer, &plen) < 0) { + syslog (LOG_WARNING, "could not getpeername"); + close (ctrl); + continue; } - sigprocmask(SIG_SETMASK, &emptymask, NULL); - } - readable = allsock; - n = select(maxsock + 1, &readable, (fd_set *)0, (fd_set *)0, (struct timeval *)0); - if (n <= 0) { - if (n < 0 && errno != EINTR) { - syslog(LOG_WARNING, "select: %m"); + if (ntohs (peer.sin_port) == 20) { + /* XXX ftp bounce */ + close (ctrl); + continue; } - sleep(1); - continue; - } - for (sep = servtab; n && sep; sep = sep->se_next) { - if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { - n--; - if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { - /* Fixed AGC */ - fcntl(sep->se_fd, F_SETFL, O_NDELAY); - /* --------- */ - ctrl = accept(sep->se_fd, NULL, NULL); - fcntl(sep->se_fd, F_SETFL, 0); - if (ctrl < 0) { - if (errno == EINTR || errno == EWOULDBLOCK) { - continue; - } - syslog(LOG_WARNING, "accept (for %s): %m", - sep->se_service); - continue; - } - } else { - ctrl = sep->se_fd; - } - sigprocmask(SIG_BLOCK, &blockmask, NULL); - pid = 0; + } + } else + ctrl = sep->se_fd; + (void) sigblock (SIGBLOCK); + pid = 0; #ifdef INETD_FEATURE_ENABLED - if (sep->se_bi == 0 || sep->se_bi->bi_fork) + if (sep->se_bi == 0 || sep->se_bi->bi_fork) #endif - { - if (sep->se_count++ == 0) { - gettimeofday(&sep->se_time, (struct timezone *)0); - } - else if (sep->se_count >= sep->se_max) { - struct timeval now; - - gettimeofday(&now, (struct timezone *)0); - if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) { - sep->se_time = now; - sep->se_count = 1; - } else { - syslog(LOG_ERR, - "%s/%s server failing (looping), service terminated", - sep->se_service, sep->se_proto); - FD_CLR(sep->se_fd, &allsock); - close(sep->se_fd); - sep->se_fd = -1; - sep->se_count = 0; - nsock--; - sigprocmask(SIG_SETMASK, &emptymask, NULL); - if (!timingout) { - timingout = 1; - alarm(RETRYTIME); - } - continue; - } - } - pid = fork(); - if (pid < 0) { - syslog(LOG_ERR, "fork: %m"); - if (sep->se_socktype == SOCK_STREAM) { - close(ctrl); - } - sigprocmask(SIG_SETMASK, &emptymask, NULL); - sleep(1); - continue; - } - if (pid && sep->se_wait) { - sep->se_wait = pid; - FD_CLR(sep->se_fd, &allsock); - nsock--; - } - } - sigprocmask(SIG_SETMASK, &emptymask, NULL); - if (pid == 0) { + { + if (sep->se_count++ == 0) + (void) gettimeofday (&sep->se_time, NULL); + else if (toomany > 0 && sep->se_count >= sep->se_max) { + struct timeval now; + + (void) gettimeofday (&now, NULL); + if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) { + sep->se_time = now; + sep->se_count = 1; + } else { + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) + close (ctrl); + if (sep->se_family == AF_INET && + ntohs (sep->se_ctrladdr_in.sin_port) >= IPPORT_RESERVED) { + /* + * Cannot close it -- there are + * thieves on the system. + * Simply ignore the connection. + */ + --sep->se_count; + continue; + } + syslog (LOG_ERR, + "%s/%s server failing (looping), service terminated", + sep->se_service, sep->se_proto); + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) + close (ctrl); + FD_CLR (sep->se_fd, &allsock); + (void) close (sep->se_fd); + sep->se_fd = -1; + sep->se_count = 0; + nsock--; + sigsetmask (0L); + if (!timingout) { + timingout = 1; + alarm (RETRYTIME); + } + continue; + } + } + pid = fork (); + } + if (pid < 0) { + syslog (LOG_ERR, "fork: %m"); + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) + close (ctrl); + sigsetmask (0L); + sleep (1); + continue; + } + if (pid && sep->se_wait) { + sep->se_wait = pid; + FD_CLR (sep->se_fd, &allsock); + nsock--; + } + sigsetmask (0L); + if (pid == 0) { #ifdef INETD_FEATURE_ENABLED - if (sep->se_bi) { - (*sep->se_bi->bi_fn)(ctrl, sep); - } else + if (sep->se_bi) { + (*sep->se_bi->bi_fn) (ctrl, sep); + } else #endif - { - struct passwd *pwd = getpwnam(sep->se_user); - if (pwd == NULL) { - syslog_err_and_discard_dg( - sep->se_socktype, - "getpwnam: %s: No such user", - sep->se_user); - } - if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) { - syslog_err_and_discard_dg(sep->se_socktype, - "getgrnam: %s: No such group", sep->se_group); - } - /* - * Ok. There are four cases here: - * 1. nonroot user, no group specified - * 2. nonroot user, some group specified - * 3. root user, no group specified - * 4. root user, some group specified - * In cases 2 and 4 we setgid to the specified - * group. In cases 1 and 2 we run initgroups - * to run with the groups of the given user. - * In case 4 we do setgroups to run with the - * given group. In case 3 we do nothing. - */ - if (pwd->pw_uid) { - if (sep->se_group) { - pwd->pw_gid = grp->gr_gid; - } - setgid((gid_t)pwd->pw_gid); - initgroups(pwd->pw_name, pwd->pw_gid); - setuid((uid_t)pwd->pw_uid); - } else if (sep->se_group) { - setgid((gid_t)grp->gr_gid); - setgroups(1, &grp->gr_gid); - } - dup2(ctrl, 0); - close(ctrl); - dup2(0, 1); - dup2(0, 2); -#ifdef RLIMIT_NOFILE - if (rlim_ofile.rlim_cur != rlim_ofile_cur) { - if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { - syslog(LOG_ERR,"setrlimit: %m"); - } - } -#endif - for (ctrl = rlim_ofile_cur-1; --ctrl > 2; ) { - (void)close(ctrl); - } - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - sigaction(SIGPIPE, &sa, NULL); - - execv(sep->se_server, sep->se_argv); - syslog_err_and_discard_dg(sep->se_socktype, "execv %s: %m", sep->se_server); - } - } - if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { - close(ctrl); - } + { + if ((pwd = getpwnam (sep->se_user)) == NULL) { + syslog (LOG_ERR, "getpwnam: %s: No such user", sep->se_user); + if (sep->se_socktype != SOCK_STREAM) + recv (0, buf, sizeof (buf), 0); + _exit (1); + } + if (setsid () < 0) + syslog (LOG_ERR, "%s: setsid: %m", sep->se_service); + if (sep->se_group && (grp = getgrnam (sep->se_group)) == NULL) { + syslog (LOG_ERR, "getgrnam: %s: No such group", sep->se_group); + if (sep->se_socktype != SOCK_STREAM) + recv (0, buf, sizeof (buf), 0); + _exit (1); + } + if (uid != 0) { + /* a user running private inetd */ + if (uid != pwd->pw_uid) + _exit (1); + } else if (pwd->pw_uid) { + if (sep->se_group) { + pwd->pw_gid = grp->gr_gid; + } + setgid ((gid_t) pwd->pw_gid); + initgroups (pwd->pw_name, pwd->pw_gid); + setuid ((uid_t) pwd->pw_uid); + } else if (sep->se_group) { + setgid (grp->gr_gid); + setgroups (1, &grp->gr_gid); } + dup2 (ctrl, 0); + close (ctrl); + dup2 (0, 1); + dup2 (0, 2); + if (rlim_ofile.rlim_cur != rlim_ofile_cur) + if (setrlimit (RLIMIT_NOFILE, &rlim_ofile) < 0) + syslog (LOG_ERR, "setrlimit: %m"); + closelog (); + for (tmpint = rlim_ofile_cur - 1; --tmpint > 2;) + (void) close (tmpint); + sigaction (SIGPIPE, &sapipe, NULL); + execv (sep->se_server, sep->se_argv); + if (sep->se_socktype != SOCK_STREAM) + recv (0, buf, sizeof (buf), 0); + syslog (LOG_ERR, "execv %s: %m", sep->se_server); + _exit (1); + } } - } + if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) + close (ctrl); + } + } } - /* * Internet services provided internally by inetd: */ #define BUFSIZE 4096 -#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO -/* Echo service -- echo data back */ -static void echo_stream(int s, servtab_t *sep) +#if defined(CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO) || \ + defined(CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN) || \ + defined(CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME) +static int dg_badinput (struct sockaddr_in *dg_sin) { - char buffer[BUFSIZE]; - int i; - - setproctitle(sep->se_service, s); - while ((i = read(s, buffer, sizeof(buffer))) > 0 && - write(s, buffer, i) > 0) - ; - exit(0); + if (ntohs (dg_sin->sin_port) < IPPORT_RESERVED) + return (1); + if (dg_sin->sin_addr.s_addr == htonl (INADDR_BROADCAST)) + return (1); + /* XXX compare against broadcast addresses in SIOCGIFCONF list? */ + return (0); } +#endif +#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO /* Echo service -- echo data back */ -static void echo_dg(int s, servtab_t *sep) +/* ARGSUSED */ +static void +echo_stream (int s, servtab_t *sep) { - char buffer[BUFSIZE]; - int i; - socklen_t size; - struct sockaddr sa; - - (void)sep; + char buffer[BUFSIZE]; + int i; - size = sizeof(sa); - if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0) - return; - (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); + inetd_setproctitle (sep->se_service, s); + while ((i = read (s, buffer, sizeof (buffer))) > 0 && + write (s, buffer, i) > 0); + exit (0); } -#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */ +/* Echo service -- echo data back */ +/* ARGSUSED */ +static void +echo_dg (int s, servtab_t *sep __attribute__((unused))) +{ + char buffer[BUFSIZE]; + int i, size; + /* struct sockaddr_storage ss; */ + struct sockaddr sa; + + size = sizeof (sa); + if ((i = recvfrom (s, buffer, sizeof (buffer), 0, &sa, &size)) < 0) + return; + if (dg_badinput ((struct sockaddr_in *) &sa)) + return; + (void) sendto (s, buffer, i, 0, &sa, sizeof (sa)); +} +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */ #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD /* Discard service -- ignore data */ -static void discard_stream(int s, servtab_t *sep) +/* ARGSUSED */ +static void +discard_stream (int s, servtab_t *sep) { - char buffer[BUFSIZE]; + char buffer[BUFSIZE]; - setproctitle(sep->se_service, s); - while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) || - errno == EINTR) - ; - exit(0); + inetd_setproctitle (sep->se_service, s); + while ((errno = 0, read (s, buffer, sizeof (buffer)) > 0) || + errno == EINTR); + exit (0); } /* Discard service -- ignore data */ -static void discard_dg(int s, servtab_t *sep) +/* ARGSUSED */ +static void +discard_dg (int s, servtab_t *sep __attribute__((unused))) { - char buffer[BUFSIZE]; - (void)sep; - read(s, buffer, sizeof(buffer)); + char buffer[BUFSIZE]; + + (void) read (s, buffer, sizeof (buffer)); } -#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */ +#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */ #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN -#include #define LINESIZ 72 static char ring[128]; static char *endring; -static void initring(void) +static void +initring (void) { - int i; + int i; - endring = ring; + endring = ring; - for (i = 0; i <= 128; ++i) - if (isprint(i)) - *endring++ = i; + for (i = 0; i <= 128; ++i) + if (isprint (i)) + *endring++ = i; } /* Character generator */ -static void chargen_stream(int s, servtab_t *sep) +/* ARGSUSED */ +static void +chargen_stream (int s, servtab_t *sep) { - char *rs; - int len; - char text[LINESIZ+2]; + char *rs; + int len; + char text[LINESIZ + 2]; - setproctitle(sep->se_service, s); + inetd_setproctitle (sep->se_service, s); - if (!endring) { - initring(); - rs = ring; - } + if (!endring) { + initring (); + rs = ring; + } - text[LINESIZ] = '\r'; - text[LINESIZ + 1] = '\n'; - for (rs = ring;;) { - if ((len = endring - rs) >= LINESIZ) - memcpy(text, rs, LINESIZ); - else { - memcpy(text, rs, len); - memcpy(text + len, ring, LINESIZ - len); - } - if (++rs == endring) - rs = ring; - if (write(s, text, sizeof(text)) != sizeof(text)) - break; + text[LINESIZ] = '\r'; + text[LINESIZ + 1] = '\n'; + for (rs = ring;;) { + if ((len = endring - rs) >= LINESIZ) + memmove (text, rs, LINESIZ); + else { + memmove (text, rs, len); + memmove (text + len, ring, LINESIZ - len); } - exit(0); + if (++rs == endring) + rs = ring; + if (write (s, text, sizeof (text)) != sizeof (text)) + break; + } + exit (0); } /* Character generator */ -static void chargen_dg(int s, servtab_t *sep) +/* ARGSUSED */ +static void +chargen_dg (int s, servtab_t *sep __attribute__((unused))) { - struct sockaddr sa; - static char *rs; - size_t len; - socklen_t size; - char text[LINESIZ+2]; - - (void)sep; - - if (endring == 0) { - initring(); - rs = ring; - } - - size = sizeof(sa); - if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0) - return; - - if ((len = endring - rs) >= LINESIZ) - memcpy(text, rs, LINESIZ); - else { - memcpy(text, rs, len); - memcpy(text + len, ring, LINESIZ - len); - } - if (++rs == endring) - rs = ring; - text[LINESIZ] = '\r'; - text[LINESIZ + 1] = '\n'; - (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa)); + /* struct sockaddr_storage ss; */ + struct sockaddr sa; + static char *rs; + int len, size; + char text[LINESIZ + 2]; + + if (endring == 0) { + initring (); + rs = ring; + } + + size = sizeof (sa); + if (recvfrom (s, text, sizeof (text), 0, &sa, &size) < 0) + return; + if (dg_badinput ((struct sockaddr_in *) &sa)) + return; + + if ((len = endring - rs) >= LINESIZ) + memmove (text, rs, LINESIZ); + else { + memmove (text, rs, len); + memmove (text + len, ring, LINESIZ - len); + } + if (++rs == endring) + rs = ring; + text[LINESIZ] = '\r'; + text[LINESIZ + 1] = '\n'; + (void) sendto (s, text, sizeof (text), 0, &sa, sizeof (sa)); } #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */ @@ -1146,74 +1713,84 @@ static void chargen_dg(int s, servtab_t *sep) * some seventy years Bell Labs was asleep. */ -static long machtime(void) +static u_int machtime (void) { - struct timeval tv; + struct timeval tv; - if (gettimeofday(&tv, (struct timezone *)0) < 0) { - fprintf(stderr, "Unable to get time of day\n"); - return (0L); - } - return (htonl((long)tv.tv_sec + 2208988800UL)); + if (gettimeofday (&tv, NULL) < 0) { + fprintf (stderr, "Unable to get time of day\n"); + return (0L); + } + return (htonl ((u_int) tv.tv_sec + 2208988800UL)); } -static void machtime_stream(int s, servtab_t *sep) +/* ARGSUSED */ +static void +machtime_stream (int s, servtab_t *sep __attribute__((unused))) { - long result; - (void)sep; + u_int result; - result = machtime(); - write(s, (char *) &result, sizeof(result)); + result = machtime (); + (void) write (s, (char *) &result, sizeof (result)); } -static void machtime_dg(int s, servtab_t *sep) +/* ARGSUSED */ +static void +machtime_dg (int s, servtab_t *sep __attribute__((unused))) { - long result; - struct sockaddr sa; - socklen_t size; - (void)sep; - - size = sizeof(sa); - if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0) - return; - result = machtime(); - (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa)); + u_int result; + /* struct sockaddr_storage ss; */ + struct sockaddr sa; + struct sockaddr_in *dg_sin; + int size; + + size = sizeof (sa); + if (recvfrom (s, (char *) &result, sizeof (result), 0, &sa, &size) < 0) + return; + /* if (dg_badinput((struct sockaddr *)&ss)) */ + dg_sin = (struct sockaddr_in *) &sa; + if (dg_sin->sin_addr.s_addr == htonl (INADDR_BROADCAST) || + ntohs (dg_sin->sin_port) < IPPORT_RESERVED / 2) + return; + result = machtime (); + (void) sendto (s, (char *) &result, sizeof (result), 0, &sa, sizeof (sa)); } #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */ #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME /* Return human-readable time of day */ -static int human_readable_time_sprintf(char *buffer) -{ - time_t clocc = time(NULL); - - return sprintf(buffer, "%.24s\r\n", ctime(&clocc)); -} - -static void daytime_stream(int s, servtab_t *sep) +/* ARGSUSED */ +static void daytime_stream (int s, servtab_t *sep __attribute__((unused))) { - char buffer[256]; - size_t st = human_readable_time_sprintf(buffer); + char buffer[256]; + time_t t; - (void)sep; + t = time (NULL); - write(s, buffer, st); + (void) sprintf (buffer, "%.24s\r\n", ctime (&t)); + (void) write (s, buffer, strlen (buffer)); } /* Return human-readable time of day */ -static void daytime_dg(int s, servtab_t *sep) +/* ARGSUSED */ +void +daytime_dg (int s, servtab_t *sep __attribute__((unused))) { - char buffer[256]; - struct sockaddr sa; - socklen_t size; - - (void)sep; - - size = sizeof(sa); - if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) - return; - size = human_readable_time_sprintf(buffer); - sendto(s, buffer, size, 0, &sa, sizeof(sa)); + char buffer[256]; + time_t t; + /* struct sockaddr_storage ss; */ + struct sockaddr sa; + int size; + + t = time ((time_t *) 0); + + size = sizeof (sa); + if (recvfrom (s, buffer, sizeof (buffer), 0, &sa, &size) < 0) + return; + if (dg_badinput ((struct sockaddr_in *) &sa)) + return; + (void) sprintf (buffer, "%.24s\r\n", ctime (&t)); + (void) sendto (s, buffer, strlen (buffer), 0, &sa, sizeof (sa)); } #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */ -- 2.7.4