sync inetd with slackware-10.2 inetd (OpenBSD-1.79), have destroy bug 455
This commit was genuinely broken at the time (not related to our build system): scripts/config/mkconfigs.c (2005-08-01 - 2006-10-05 arasi) uretilen bbconfigopts.h icine .config satirlarini kacissiz, ham newline'larla gömüyordu - gecerli C degildi, busybox'un kendi bb_mkdep araci bile parse edemiyordu (7d219aab7 ile duzeltildi, mkconfigs shell scripte cevrildi)
No binary (build failed)
—
(log yok)
| @@ -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" | |
| @@ -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 <[email protected]> */ |
| /* | |
| * 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 <[email protected]> |
| - | * |
| - | * 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 <sys/file.h> |
| - | #include <sys/ioctl.h> |
| #include <sys/param.h> | |
| - | #include <sys/resource.h> |
| - | #include <sys/socket.h> |
| #include <sys/stat.h> | |
| - | #include <sys/time.h> |
| + | #include <sys/ioctl.h> |
| + | #include <sys/socket.h> |
| #include <sys/un.h> | |
| + | #include <sys/file.h> |
| #include <sys/wait.h> | |
| + | #include <sys/time.h> |
| + | #include <sys/resource.h> |
| + | |
| #include <netinet/in.h> | |
| - | #include <netinet/ip.h> |
| #include <arpa/inet.h> | |
| #include <errno.h> | |
| @@ -117,30 +147,51 @@ | |
| #include <syslog.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| - | #include <string.h> |
| - | #include <getopt.h> |
| #include <unistd.h> | |
| - | #include <stdarg.h> |
| + | #include <string.h> |
| + | #include <ctype.h> |
| #include <time.h> | |
| - | #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 <rpc/rpc.h> |
| + | #include <rpc/pmap_clnt.h> |
| + | #include <rpcsvc/nfs_prot.h> |
| #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 <ctype.h> |
| #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 */ | |