diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile new file mode 100644 index 0000000..e786440 --- /dev/null +++ b/usr.sbin/inetd/Makefile @@ -0,0 +1,9 @@ +# BSDI $Id: Makefile,v 1.1 1998/01/24 08:35:42 taubert Exp $ +# @(#)Makefile 5.5 (Berkeley) 6/29/90 + +PROG= inetd +LDADD= -lnetdb +MAN8= inetd.0 +MLINKS= inetd.8 inetd.5 + +.INCLUDE: /src/gno/prog.mk diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8 new file mode 100644 index 0000000..5dbb198 --- /dev/null +++ b/usr.sbin/inetd/inetd.8 @@ -0,0 +1,222 @@ +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" 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. +.\" +.\" @(#)inetd.8 6.7 (Berkeley) 3/16/91 +.\" +.Dd March 16, 1991 +.Dt INETD 8 +.Os BSD 4.3 +.Sh NAME +.Nm inetd +.Nd internet +.Dq super-server +.Sh SYNOPSIS +.Nm inetd +.Op Fl d +.Op Ar configuration file +.Sh DESCRIPTION +.Nm Inetd +should be run at boot time by +.Pa /etc/rc.local +(see +.Xr rc 8 ) . +It then listens for connections on certain +internet sockets. When a connection is found on one +of its sockets, it decides what service the socket +corresponds to, and invokes a program to service the request. +After the program is +finished, it continues to listen on the socket (except in some cases which +will be described below). Essentially, +.Nm inetd +allows running one daemon to invoke several others, +reducing load on the system. +.Pp +The option available for +.Nm inetd: +.Bl -tag -width Ds +.It Fl d +Turns on debugging. +.El +.Pp +Upon execution, +.Nm inetd +reads its configuration information from a configuration +file which, by default, is +.Pa /etc/inetd.conf . +There must be an entry for each field of the configuration +file, with entries for each field separated by a tab or +a space. Comments are denoted by a ``#'' at the beginning +of a line. There must be an entry for each field. The +fields of the configuration file are as follows: +.Pp +.Bd -unfilled -offset indent -compact +service name +socket type +protocol +wait/nowait +user +server program +server program arguments +.Ed +.Pp +The +.Em service-name +entry is the name of a valid service in +the file +.Pa /etc/services . +For +.Dq internal +services (discussed below), the service +name +.Em must +be the official name of the service (that is, the first entry in +.Pa /etc/services ) . +.Pp +The +.Em socket-type +should be one of +.Dq stream , +.Dq dgram , +.Dq raw , +.Dq rdm , +or +.Dq seqpacket , +depending on whether the socket is a stream, datagram, raw, +reliably delivered message, or sequenced packet socket. +.Pp +The +.Em protocol +must be a valid protocol as given in +.Pa /etc/protocols . +Examples might be +.Dq tcp +or +.Dq udp . +.Pp +The +.Em wait/nowait +entry is applicable to datagram sockets only (other sockets should +have a +.Dq nowait +entry in this space). If a datagram server connects +to its peer, freeing the socket so +.Nm inetd +can received further messages on the socket, it is said to be +a +.Dq multi-threaded +server, and should use the +.Dq nowait +entry. For datagram servers which process all incoming datagrams +on a socket and eventually time out, the server is said to be +.Dq single-threaded +and should use a +.Dq wait +entry. +.Xr Comsat 8 +.Pq Xr biff 1 +and +.Xr talkd 8 +are both examples of the latter type of +datagram server. +.Xr Tftpd 8 +is an exception; it is a datagram server that establishes pseudo-connections. +It must be listed as +.Dq wait +in order to avoid a race; +the server reads the first packet, creates a new socket, +and then forks and exits to allow +.Nm inetd +to check for new service requests to spawn new servers. +.Pp +The +.Em user +entry should contain the user name of the user as whom the server +should run. This allows for servers to be given less permission +than root. +.Pp +The +.Em server-program +entry should contain the pathname of the program which is to be +executed by +.Nm inetd +when a request is found on its socket. If +.Nm inetd +provides this service internally, this entry should +be +.Dq internal . +.Pp +The +.Em server program arguments +should be just as arguments +normally are, starting with argv[0], which is the name of +the program. If the service is provided internally, the +word +.Dq internal +should take the place of this entry. +.Pp +.Nm Inetd +provides several +.Dq trivial +services internally by use of +routines within itself. These services are +.Dq echo , +.Dq discard , +.Dq chargen +(character generator), +.Dq daytime +(human readable time), and +.Dq time +(machine readable time, +in the form of the number of seconds since midnight, January +1, 1900). All of these services are tcp based. For +details of these services, consult the appropriate +.Tn RFC +from the Network Information Center. +.Pp +.Nm Inetd +rereads its configuration file when it receives a hangup signal, +.Dv SIGHUP . +Services may be added, deleted or modified when the configuration file +is reread. +.Sh SEE ALSO +.Xr comsat 8 , +.Xr fingerd 8 , +.Xr ftpd 8 , +.Xr rexecd 8 , +.Xr rlogind 8 , +.Xr rshd 8 , +.Xr telnetd 8 , +.Xr tftpd 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c new file mode 100644 index 0000000..87a7e79 --- /dev/null +++ b/usr.sbin/inetd/inetd.c @@ -0,0 +1,920 @@ +/* BSDI $Id: inetd.c,v 1.1 1998/01/24 08:35:45 taubert Exp $ */ + +/* + * Copyright (c) 1983,1991 The Regents of the University of California. + * All rights reserved. + * + * 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1983 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)inetd.c 5.30 (Berkeley) 6/3/91"; +#endif /* not lint */ + +/* + * Inetd - Internet super-server + * + * This program invokes all internet services as needed. + * connection-oriented services are invoked each time a + * connection is made, by creating a process. This process + * is passed the connection as file descriptor 0 and is + * expected to do a getpeername to find out the source host + * and port. + * + * Datagram oriented services are invoked when a datagram + * arrives; a process is created and passed a pending message + * on file descriptor 0. Datagram servers may either connect + * to their peer, freeing up the original socket for inetd + * to receive further messages on, or ``take over the socket'', + * processing all arriving datagrams and, eventually, timing + * out. The first type of server is said to be ``multi-threaded''; + * the second type of server ``single-threaded''. + * + * 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 + * 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 single-threaded/multi-threaded + * user user to run daemon as + * server program full path name + * server program arguments maximum of MAXARGS (20) + * + * Comment lines are indicated by a `#' in column 1. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +#include "protos.h" + +#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 SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) + +int debug = 0; +int nsock = 0, maxsock = -1; +fd_set allsock; +int options = 0; +int timingout = 0; +struct servent *sp = NULL; + +struct servtab { + char *se_service; /* name of service */ + int se_socktype; /* type of socket to use */ + 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 */ + struct biltin *se_bi; /* if built-in, description */ + char *se_server; /* server program */ +#define MAXARGV 20 + char *se_argv[MAXARGV+1]; /* program arguments */ + int se_fd; /* open descriptor */ + struct sockaddr_in se_ctrladdr;/* bound address */ + int se_count; /* number started since se_time */ + struct timeval se_time; /* start of se_count */ + struct servtab *se_next; +} *servtab; + +struct biltin { + 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, struct servtab *); + /* function which performs it */ +} biltins[] = { + /* Echo received data */ + "echo", SOCK_STREAM, 1, 0, echo_stream, + "echo", SOCK_DGRAM, 0, 0, echo_dg, + + /* Internet /dev/null */ + "discard", SOCK_STREAM, 1, 0, discard_stream, + "discard", SOCK_DGRAM, 0, 0, discard_dg, + + /* Return 32 bit time since 1970 */ + "time", SOCK_STREAM, 0, 0, machtime_stream, + "time", SOCK_DGRAM, 0, 0, machtime_dg, + + /* Return human-readable time */ + "daytime", SOCK_STREAM, 0, 0, daytime_stream, + "daytime", SOCK_DGRAM, 0, 0, daytime_dg, + + /* Familiar character generator */ + "chargen", SOCK_STREAM, 1, 0, chargen_stream, + "chargen", SOCK_DGRAM, 0, 0, chargen_dg, + 0 +}; + +#pragma databank 1 +int fork_child(struct servtab *sep, int dofork, int ctrl) +{ +register struct passwd *pwd; +register int tmpint; +static char buf[50]; + + sigsetmask(0L); + if (dofork) + for (tmpint = maxsock; tmpint > STDERR_FILENO; tmpint--) + if (tmpint != ctrl) + close(tmpint); + if (sep->se_bi) + (*sep->se_bi->bi_fn)(ctrl, sep); + else { + if (debug) + fprintf(stderr, "%d execl %s\n", getpid(), sep->se_server); + dup2(ctrl, STDIN_FILENO); + close(ctrl); + dup2(STDIN_FILENO, STDOUT_FILENO); +if (!debug) + dup2(STDIN_FILENO, STDERR_FILENO); + + SetOutputDevice(3, 2l); + SetErrorDevice(3, 3l); + SetInputDevice(3, 1l); + + 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 (pwd->pw_uid) { + (void) setgid((gid_t)pwd->pw_gid); + (void) setuid((uid_t)pwd->pw_uid); + } + 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_socktype == SOCK_STREAM) + close(ctrl); + return 0; +} +#pragma databank 0 + +#define NUMINT (sizeof(intab) / sizeof(struct inent)) +char *CONFIG = _PATH_INETDCONF; + +int main(int argc, char **argv) +{ +register struct servtab *sep; +int ch, pid, dofork; + + while ((ch = getopt(argc, argv, "d")) != EOF) + switch(ch) { + case 'd': + debug = 1; + options |= SO_DEBUG; + break; + case '?': + default: + fprintf(stderr, "usage: inetd [-d]"); + exit(1); + } + argc -= optind; + argv += optind; + + if (argc > 0) + CONFIG = argv[0]; + openlog("inetd", 0, LOG_DAEMON); + signal(SIGALRM, retry); + config(0, 0); + signal(SIGHUP, config); + signal(SIGCHLD, reapchild); + + { + /* space for daemons to overwrite environment for ps */ +#define DUMMYSIZE 100 + char dummy[DUMMYSIZE]; + + (void)memset(dummy, 'x', sizeof(DUMMYSIZE) - 1); + dummy[DUMMYSIZE - 1] = '\0'; + (void)setenv("inetd_dummy", dummy, 1); + } + + for (;;) { + int n, ctrl; + fd_set readable; + + if (nsock == 0) { + (void) sigblock(SIGBLOCK); + while (nsock == 0) + sigpause(0L); + (void) sigsetmask(0L); + } + readable = allsock; + if ((n = select(maxsock + 1, &readable, (fd_set *)0, + (fd_set *)0, (struct timeval *)0)) <= 0) { + if (n < 0 && errno != EINTR) + syslog(LOG_WARNING, "select: %m\n"); + 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 (debug) + fprintf(stderr, "someone wants %s\n", + sep->se_service); + if (sep->se_socktype == SOCK_STREAM) { + ctrl = accept(sep->se_fd, (struct __SOCKADDR *)0, + (int *)0); + if (debug) + fprintf(stderr, "accept, ctrl %d\n", ctrl); + if (ctrl < 0) { + if (errno == EINTR) + continue; + syslog(LOG_WARNING, "accept (for %s): %m", + sep->se_service); + continue; + } + } else + ctrl = sep->se_fd; + (void) sigblock(SIGBLOCK); + pid = 0; + dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork); + if (dofork) { + if (sep->se_count++ == 0) + (void)gettimeofday(&sep->se_time, + (struct timezone *)0); + else if (sep->se_count >= TOOMANY) { + struct timeval now; + + (void)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\n", + sep->se_service, sep->se_proto); + FD_CLR(sep->se_fd, &allsock); + (void) close(sep->se_fd); + sep->se_fd = -1; + sep->se_count = 0; + nsock--; + if (!timingout) { + timingout = 1; + alarm(RETRYTIME); + } + } + } + pid = fork2(fork_child, 768, 0, "inetd fork", 4, sep, dofork, ctrl); + } + if (pid < 0) { + syslog(LOG_ERR, "fork: %m"); + if (sep->se_socktype == SOCK_STREAM) + close(ctrl); + sigsetmask(0L); + sleep(1); + continue; + } + if (pid && sep->se_wait) { + sep->se_wait = pid; + if (sep->se_fd >= 0) { + FD_CLR(sep->se_fd, &allsock); + nsock--; + } + } + if (pid == 0) { + fork_child(sep, dofork, ctrl); + } else { + sigsetmask(0L); + if (sep->se_socktype == SOCK_STREAM) + close(ctrl); + } + } + } +} + +#pragma databank 1 +void reapchild(pid_t sig, int code) +{ +union wait status; +int pid; +register struct servtab *sep; + + pid = wait(&status); + if (pid <= 0) + return; + if (debug) + fprintf(stderr, "%d reaped\n", pid); + for (sep = servtab; sep; sep = sep->se_next) + if (sep->se_wait == pid) { + if (status.w_status) + syslog(LOG_WARNING, + "%s: exit status 0x%x", + sep->se_server, status.w_status); + if (debug) + fprintf(stderr, "restored %s, fd %d\n", + sep->se_service, sep->se_fd); + if (sep->se_fd >= 0) { + FD_SET(sep->se_fd, &allsock); + nsock++; + } + sep->se_wait = 1; + } +} + +void config(pid_t sig, int code) +{ +register struct servtab *sep, *cp, **sepp; +long omask; + + if (!setconfig()) { + syslog(LOG_ERR, "%s: %m", CONFIG); + return; + } + for (sep = servtab; sep; sep = sep->se_next) + sep->se_checked = 0; + while (cp = getconfigent()) { + 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; + + 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 (cp->se_bi == 0 && + (sep->se_wait == 1 || cp->se_wait == 0)) + sep->se_wait = cp->se_wait; +#define SWAP(a, b) { char *c = a; a = b; b = c; } + if (cp->se_user) + SWAP(sep->se_user, cp->se_user); + if (cp->se_server) + SWAP(sep->se_server, cp->se_server); + for (i = 0; i < MAXARGV; i++) + SWAP(sep->se_argv[i], cp->se_argv[i]); + sigsetmask(omask); + freeconfig(cp); + if (debug) + print_service("REDO", sep); + } else { + sep = enter(cp); + if (debug) + print_service("ADD ", sep); + } + sep->se_checked = 1; + sp = getservbyname(sep->se_service, sep->se_proto); + if (sp == 0) { + syslog(LOG_ERR, "%s/%s: unknown service", + sep->se_service, sep->se_proto); + if (sep->se_fd != -1) + (void) close(sep->se_fd); + sep->se_fd = -1; + continue; + } + if (sp->s_port != sep->se_ctrladdr.sin_port) { + sep->se_ctrladdr.sin_port = sp->s_port; + if (sep->se_fd != -1) + (void) close(sep->se_fd); + sep->se_fd = -1; + } + if (sep->se_fd == -1) + setup(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); + } + if (debug) + print_service("FREE", sep); + freeconfig(sep); + free((char *)sep); + } + (void) sigsetmask(omask); +} + +void retry(pid_t sig, int code) +{ +register struct servtab *sep; + + timingout = 0; + for (sep = servtab; sep; sep = sep->se_next) + if (sep->se_fd == -1) + setup(sep); +} +#pragma databank 0 + +void setup(struct servtab *sep) +{ +int on = 1; + + if ((sep->se_fd = socket(AF_INET, 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 (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && + turnon(sep->se_fd, SO_DEBUG) < 0) + syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); + if (turnon(sep->se_fd, SO_REUSEADDR) < 0) + syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); +#undef turnon + if (bind(sep->se_fd, (struct __SOCKADDR *)&sep->se_ctrladdr, + sizeof (sep->se_ctrladdr)) < 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); + } + return; + } + if (sep->se_socktype == SOCK_STREAM) + listen(sep->se_fd, 10); + FD_SET(sep->se_fd, &allsock); + nsock++; + if (sep->se_fd > maxsock) + maxsock = sep->se_fd; +} + +struct servtab *enter(struct servtab *cp) +{ +register struct servtab *sep; +long omask; + + sep = (struct servtab *)malloc(sizeof (*sep)); + if (sep == (struct servtab *)0) { + syslog(LOG_ERR, "Out of memory."); + exit(-1); + } + *sep = *cp; + sep->se_fd = -1; + omask = sigblock(SIGBLOCK); + sep->se_next = servtab; + servtab = sep; + sigsetmask(omask); + return (sep); +} + +FILE *fconfig = NULL; +struct servtab serv; +char line[256]; + +int setconfig(void) +{ + if (fconfig != NULL) { + fseek(fconfig, 0L, L_SET); + return (1); + } + fconfig = fopen(CONFIG, "r"); + return (fconfig != NULL); +} + +void endconfig(void) +{ + if (fconfig) { + (void) fclose(fconfig); + fconfig = NULL; + } +} + +struct servtab *getconfigent(void) +{ +register struct servtab *sep = &serv; +int argc; +char *cp, *arg; + +more: + while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0')) + ; + if (cp == NULL) + return ((struct servtab *)0); + sep->se_service = newstr(skip(&cp)); + 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_ctrladdr.sin_family = AF_INET; + sep->se_proto = newstr(skip(&cp)); + arg = skip(&cp); + sep->se_wait = strcmp(arg, "wait") == 0; + sep->se_user = newstr(skip(&cp)); + sep->se_server = newstr(skip(&cp)); + if (strcmp(sep->se_server, "internal") == 0) { + register 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\n", + sep->se_service); + goto more; + } + sep->se_bi = bi; + sep->se_wait = bi->bi_wait; + } else + sep->se_bi = NULL; + 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; + return (sep); +} + +void freeconfig(register struct servtab *cp) +{ +int i; + + if (cp->se_service) + free(cp->se_service); + if (cp->se_proto) + free(cp->se_proto); + if (cp->se_user) + free(cp->se_user); + if (cp->se_server) + free(cp->se_server); + for (i = 0; i < MAXARGV; i++) + if (cp->se_argv[i]) + free(cp->se_argv[i]); +} + +char *skip(char **cpp) +{ +register char *cp = *cpp; +char *start; + +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(fconfig)) + goto again; + *cpp = (char *)0; + return ((char *)0); + } + start = cp; + while (*cp && *cp != ' ' && *cp != '\t') + cp++; + if (*cp != '\0') + *cp++ = '\0'; + *cpp = cp; + return (start); +} + +char *nextline(FILE *fd) +{ +char *cp; + + if (fgets(line, sizeof (line), fd) == NULL) + return ((char *)0); + cp = line; + while((*cp != '\0') && ((*cp == ' ') || (*cp == '\t'))) + cp++; + strcpy(line, cp); + cp = index(line, '\n'); + if (cp) + *cp = '\0'; + return (line); +} + +char *newstr(char *cp) +{ + if (cp = strdup(cp ? cp : "")) + return(cp); + syslog(LOG_ERR, "strdup: %m"); + exit(-1); +} + +void socktitle(char *a, int s) +{ +#ifdef HAVE_SETPROCTITLE +int size; +struct sockaddr_in sin; + + size = sizeof(sin); + if (getpeername(s, (struct __SOCKADDR *)&sin, &size) == 0) + setproctitle("-%s [%s]", a, inet_ntoa(sin.sin_addr)); + else + setproctitle("-%s", a); +#endif +} + +/* + * Internet services provided internally by inetd: + */ +#define BUFSIZE 8192 + +/* ARGSUSED */ +void echo_stream(int s, struct servtab *sep) +/* Echo service -- echo data back */ +{ +int i; +char *buffer = (char *)malloc(BUFSIZE); + + if (!buffer) return; + socktitle(sep->se_service, s); + while ((i = read(s, buffer, BUFSIZE)) > 0 && + write(s, buffer, i) > 0) + ; + free(buffer); +} + +/* ARGSUSED */ +void echo_dg(int s, struct servtab *sep) +/* Echo service -- echo data back */ +{ +int i, size; +struct __SOCKADDR sa; +char *buffer = (char *)malloc(BUFSIZE); + + if (!buffer) return; + size = sizeof(sa); + if ((i = recvfrom(s, buffer, BUFSIZE, 0, &sa, &size)) >= 0) + (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); + free(buffer); +} + +/* ARGSUSED */ +void discard_stream(int s, struct servtab *sep) +/* Discard service -- ignore data */ +{ +int ret; +static char buffer[BUFSIZE]; + + socktitle(sep->se_service, s); + while (1) { + while ((ret = read(s, buffer, sizeof(buffer))) > 0) + ; + if (ret == 0 || errno != EINTR) + break; + } +} + +/* ARGSUSED */ +void discard_dg(int s, struct servtab *sep) +/* Discard service -- ignore data */ +{ +static char buffer[BUFSIZE]; + + (void) read(s, buffer, sizeof(buffer)); +} + +#include +#define LINESIZ 72 +char ring[128]; +char *endring = NULL; + +void initring(void) +{ +register int i; + + endring = ring; + + for (i = 0; i <= 128; ++i) + if (isprint(i)) + *endring++ = i; +} + +/* ARGSUSED */ +void chargen_stream(int s, struct servtab *sep) +/* Character generator */ +{ +register char *rs; +int len; +char text[LINESIZ+2]; + + socktitle(sep->se_service, s); + + if (!endring) { + initring(); + rs = ring; + } + + text[LINESIZ] = '\r'; + text[LINESIZ + 1] = '\n'; + for (rs = ring;;) { + if ((len = endring - rs) >= LINESIZ) + bcopy(rs, text, LINESIZ); + else { + bcopy(rs, text, len); + bcopy(ring, text + len, LINESIZ - len); + } + if (++rs == endring) + rs = ring; + if (write(s, text, sizeof(text)) != sizeof(text)) + break; + } +} + +/* ARGSUSED */ +void chargen_dg(int s, struct servtab *sep) +/* Character generator */ +{ +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 ((len = endring - rs) >= LINESIZ) + bcopy(rs, text, LINESIZ); + else { + bcopy(rs, text, len); + bcopy(ring, text + len, LINESIZ - len); + } + if (++rs == endring) + rs = ring; + text[LINESIZ] = '\r'; + text[LINESIZ + 1] = '\n'; + (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa)); +} + +/* + * Return a machine readable date and time, in the form of the + * number of seconds since midnight, Jan 1, 1900. Since gettimeofday + * returns the number of seconds since midnight, Jan 1, 1970, + * we must add 2208988800 seconds to this figure to make up for + * some seventy years Bell Labs was asleep. + */ + +long machtime(void) +{ +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 + 2208988800)); +} + +/* ARGSUSED */ +void machtime_stream(int s, struct servtab *sep) +{ +long result; + + result = machtime(); + (void) write(s, (char *) &result, sizeof(result)); +} + +/* ARGSUSED */ +void machtime_dg(int s, struct servtab *sep) +{ +long result; +struct __SOCKADDR sa; +int size; + + 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)); +} + +/* ARGSUSED */ +void daytime_stream(int s, struct servtab *sep) +/* Return human-readable time of day */ +{ +char buffer[64]; +time_t clock; + + clock = time((time_t *) 0); + + (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); + (void) write(s, buffer, strlen(buffer)); +} + +/* ARGSUSED */ +void daytime_dg(int s, struct servtab *sep) +/* Return human-readable time of day */ +{ +char buffer[64]; +time_t clock; +struct __SOCKADDR sa; +int size; + + clock = time((time_t *) 0); + + size = sizeof(sa); + if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) + return; + (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); + (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa)); +} + +/* + * print_service: + * Dump relevant information to stderr + */ +void print_service(char *action, struct servtab *sep) +{ + fprintf(stderr, + "%s: %s proto=%s, wait=%d, user=%s builtin=%06lx server=%s\n", + action, sep->se_service, sep->se_proto, + sep->se_wait, sep->se_user, sep->se_bi, sep->se_server); +} diff --git a/usr.sbin/inetd/inetd.desc b/usr.sbin/inetd/inetd.desc new file mode 100644 index 0000000..003eb39 --- /dev/null +++ b/usr.sbin/inetd/inetd.desc @@ -0,0 +1,9 @@ +Name: inetd +Version: 1.0 (October 1997) +Shell: GNO & GS/TCP +Author: Derek Taubert (from BSDi code) +Contact: taubert@geeks.org +Where: /usr/bin +FTP: ground.isca.uiowa.edu apple2.caltech.edu trenco.myrias.com + + Internet services daemon diff --git a/usr.sbin/inetd/inetd.rez b/usr.sbin/inetd/inetd.rez new file mode 100644 index 0000000..02db010 --- /dev/null +++ b/usr.sbin/inetd/inetd.rez @@ -0,0 +1,31 @@ +/* + * Resources for inetd version and comment + * + * $Id: inetd.rez,v 1.1 1998/01/24 08:35:48 taubert Exp $ + */ +#define PROG "inetd" +#define DESC "Internet services daemon." + +#include "Types.rez" + +/* + * Version + */ +resource rVersion (1, purgeable3) { + { 1, 0, 0, /* Version 1.0.0 */ + release, /* development|alpha|beta|final|release */ + 0 }, /* non-final release number */ + verUS, /* Country */ + PROG, /* Program name */ + DESC " Released with GNO/ME and GS/TCP." +}; + + +/* + * Comment + */ +resource rComment (1, purgeable3) { + PROG " v1.0 (October 1997)\n" + "GNO utility: " DESC "\n" + "Ported from BSDi code by Derek Taubert." +}; diff --git a/usr.sbin/inetd/pathnames.h b/usr.sbin/inetd/pathnames.h new file mode 100644 index 0000000..520f77a --- /dev/null +++ b/usr.sbin/inetd/pathnames.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * 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. + * + * @(#)pathnames.h 5.3 (Berkeley) 6/1/90 + */ + +#include + +#define _PATH_INETDCONF "/etc/inetd.conf" diff --git a/usr.sbin/inetd/protos.h b/usr.sbin/inetd/protos.h new file mode 100644 index 0000000..6c70821 --- /dev/null +++ b/usr.sbin/inetd/protos.h @@ -0,0 +1,36 @@ + +#include +#include + +#define _exit(__i) return(__i) + +segment "inetd "; + +int fork_child(struct servtab *sep, int dofork, int ctrl); +void reapchild(int sig, int code); +void config(int sig, int code); +void retry(int sig, int code); +void setup(struct servtab *sep); +struct servtab *enter(struct servtab *cp); +int setconfig(void); +void endconfig(void); +struct servtab *getconfigent(void); +void freeconfig(struct servtab *cp); +char *skip(char **cpp); +char *nextline(FILE *fd); +char *newstr(char *cp); +void socktitle(char *a, int s); +void echo_stream(int s, struct servtab *sep); +void echo_dg(int s, struct servtab *sep); +void discard_stream(int s, struct servtab *sep); +void discard_dg(int s, struct servtab *sep); +void initring(void); +void chargen_stream(int s, struct servtab *sep); +void chargen_dg(int s, struct servtab *sep); +long machtime(void); +void machtime_stream(int s, struct servtab *sep); +void machtime_dg(int s, struct servtab *sep); +void daytime_stream(int s, struct servtab *sep); +void daytime_dg(int s, struct servtab *sep); +void print_service(char *action, struct servtab *sep); + diff --git a/usr.sbin/inetd/tags b/usr.sbin/inetd/tags new file mode 100644 index 0000000..4214724 --- /dev/null +++ b/usr.sbin/inetd/tags @@ -0,0 +1,29 @@ +Minetd inetd.c /^main(argc, argv, envp)$/ +SWAP inetd.c /^#define SWAP(a, b) { char *c = a; a = b; b = c; }$/ +chargen_dg inetd.c /^chargen_dg(s, sep) \/* Character generator *\/$/ +chargen_stream inetd.c /^chargen_stream(s, sep) \/* Character generator */ +config inetd.c /^config()$/ +daytime_dg inetd.c /^daytime_dg(s, sep) \/* Return human-readable time/ +daytime_stream inetd.c /^daytime_stream(s, sep) \/* Return human-readable / +discard_dg inetd.c /^discard_dg(s, sep) \/* Discard service -- ignore / +discard_stream inetd.c /^discard_stream(s, sep) \/* Discard service -- ign/ +echo_dg inetd.c /^echo_dg(s, sep) \/* Echo service -- echo data ba/ +echo_stream inetd.c /^echo_stream(s, sep) \/* Echo service -- echo data/ +endconfig inetd.c /^endconfig()$/ +enter inetd.c /^enter(cp)$/ +freeconfig inetd.c /^freeconfig(cp)$/ +getconfigent inetd.c /^getconfigent()$/ +initring inetd.c /^initring()$/ +machtime inetd.c /^machtime()$/ +machtime_dg inetd.c /^machtime_dg(s, sep)$/ +machtime_stream inetd.c /^machtime_stream(s, sep)$/ +newstr inetd.c /^newstr(cp)$/ +nextline inetd.c /^nextline(fd)$/ +print_service inetd.c /^print_service(action, sep)$/ +reapchild inetd.c /^reapchild()$/ +retry inetd.c /^retry()$/ +setconfig inetd.c /^setconfig()$/ +setup inetd.c /^setup(sep)$/ +skip inetd.c /^skip(cpp)$/ +socktitle inetd.c /^socktitle(a, s)$/ +turnon inetd.c /^#define turnon(fd, opt) \\$/