45d38cbc2080f09b1403039699412021418691e8
* Copyright (c) 1983, 1991, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
static char copyright
[] =
"@(#) Copyright (c) 1983, 1991, 1993\n\
The Regents of the University of California. All rights reserved.\n";
static char sccsid
[] = "@(#)inetd.c 8.1 (Berkeley) %G%";
* 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 or must
* 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)
* TCP services without official port numbers are handled with the
* RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
* requests. When a connection is made from a foreign host, the service
* requested is passed to tcpmux, which looks it up in the servtab list
* and returns the proper entry for the service. Tcpmux returns a
* negative reply if the service doesn't exist, otherwise the invoked
* server is expected to return the positive reply if the service type in
* inetd.conf file has the prefix "tcpmux/". If the service type has the
* prefix "tcpmux/+", tcpmux will return the positive reply for the
* process; this is for compatibility with older server code, and also
* allows you to invoke programs that use stdin/stdout without putting any
* special server code in them. Services that use tcpmux are "nowait"
* because they do not have a well-known port and hence cannot listen
* Comment lines are indicated by a `#' in column 1.
#include <sys/resource.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))
void config(), reapchild(), retry();
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 */
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 */
#define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \
((sep)->se_type == MUXPLUS_TYPE))
#define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE)
int echo_stream(), discard_stream(), machtime_stream();
int daytime_stream(), chargen_stream();
int echo_dg(), discard_dg(), machtime_dg(), daytime_dg(), chargen_dg();
struct servtab
*tcpmux();
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 */
int (*bi_fn
)(); /* function which performs it */
"echo", SOCK_STREAM
, 1, 0, echo_stream
,
"echo", SOCK_DGRAM
, 0, 0, echo_dg
,
"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
,
"tcpmux", SOCK_STREAM
, 1, 0, (int (*)())tcpmux
,
#define NUMINT (sizeof(intab) / sizeof(struct inent))
char *CONFIG
= _PATH_INETDCONF
;
register struct servtab
*sep
;
register struct passwd
*pwd
;
if (envp
== 0 || *envp
== 0)
LastArg
= envp
[-1] + strlen(envp
[-1]);
openlog("inetd", LOG_PID
| LOG_NOWAIT
, LOG_DAEMON
);
while ((ch
= getopt(argc
, argv
, "dR:")) != EOF
)
case 'R': { /* invocation rate */
tmpint
= strtol(optarg
, &p
, 0);
"-R %s: bad value for service invocation rate",
"usage: inetd [-d] [-R rate] [conf-file]");
sigvec(SIGALRM
, &sv
, (struct sigvec
*)0);
sigvec(SIGHUP
, &sv
, (struct sigvec
*)0);
sv
.sv_handler
= reapchild
;
sigvec(SIGCHLD
, &sv
, (struct sigvec
*)0);
/* space for daemons to overwrite environment for ps */
(void)memset(dummy
, 'x', sizeof(DUMMYSIZE
) - 1);
dummy
[DUMMYSIZE
- 1] = '\0';
(void)setenv("inetd_dummy", dummy
, 1);
(void) sigblock(SIGBLOCK
);
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");
for (sep
= servtab
; n
&& sep
; sep
= sep
->se_next
)
if (sep
->se_fd
!= -1 && FD_ISSET(sep
->se_fd
, &readable
)) {
fprintf(stderr
, "someone wants %s\n",
if (sep
->se_socktype
== SOCK_STREAM
) {
ctrl
= accept(sep
->se_fd
, (struct sockaddr
*)0,
fprintf(stderr
, "accept, ctrl %d\n", ctrl
);
* Call tcpmux to find the real service to exec.
sep
->se_bi
->bi_fn
== (int (*)()) tcpmux
) {
(void) sigblock(SIGBLOCK
);
dofork
= (sep
->se_bi
== 0 || sep
->se_bi
->bi_fork
);
if (sep
->se_count
++ == 0)
(void)gettimeofday(&sep
->se_time
,
else if (sep
->se_count
>= toomany
) {
(void)gettimeofday(&now
, (struct timezone
*)0);
if (now
.tv_sec
- sep
->se_time
.tv_sec
>
"%s/%s server failing (looping), service terminated",
sep
->se_service
, sep
->se_proto
);
syslog(LOG_ERR
, "fork: %m");
if (sep
->se_socktype
== SOCK_STREAM
)
if (pid
&& sep
->se_wait
) {
FD_CLR(sep
->se_fd
, &allsock
);
fprintf(stderr
, "+ Closing from %d\n",
for (tmpint
= maxsock
; tmpint
> 2; tmpint
--)
(*sep
->se_bi
->bi_fn
)(ctrl
, sep
);
fprintf(stderr
, "%d execl %s\n",
getpid(), sep
->se_server
);
if ((pwd
= getpwnam(sep
->se_user
)) == NULL
) {
"%s/%s: %s: No such user",
sep
->se_service
, sep
->se_proto
,
if (sep
->se_socktype
!= SOCK_STREAM
)
recv(0, buf
, sizeof (buf
), 0);
if (setgid(pwd
->pw_gid
) < 0) {
"%s: can't set gid %d: %m",
sep
->se_service
, pwd
->pw_gid
);
(void) initgroups(pwd
->pw_name
,
if (setuid(pwd
->pw_uid
) < 0) {
"%s: can't set uid %d: %m",
sep
->se_service
, pwd
->pw_uid
);
execv(sep
->se_server
, sep
->se_argv
);
if (sep
->se_socktype
!= SOCK_STREAM
)
recv(0, buf
, sizeof (buf
), 0);
"cannot execute %s: %m", sep
->se_server
);
if (sep
->se_socktype
== SOCK_STREAM
)
register struct servtab
*sep
;
pid
= wait3(&status
, WNOHANG
, (struct rusage
*)0);
fprintf(stderr
, "%d reaped, status %#x\n",
for (sep
= servtab
; sep
; sep
= sep
->se_next
)
if (sep
->se_wait
== pid
) {
fprintf(stderr
, "restored %s, fd %d\n",
sep
->se_service
, sep
->se_fd
);
FD_SET(sep
->se_fd
, &allsock
);
register struct servtab
*sep
, *cp
, **sepp
;
struct servtab
*getconfigent(), *enter();
syslog(LOG_ERR
, "%s: %m", CONFIG
);
for (sep
= servtab
; sep
; sep
= sep
->se_next
)
while (cp
= getconfigent()) {
if ((pwd
= getpwnam(cp
->se_user
)) == NULL
) {
"%s/%s: No such user '%s', service ignored",
cp
->se_service
, cp
->se_proto
, cp
->se_user
);
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)
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
(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; }
SWAP(sep
->se_user
, cp
->se_user
);
SWAP(sep
->se_server
, cp
->se_server
);
for (i
= 0; i
< MAXARGV
; i
++)
SWAP(sep
->se_argv
[i
], cp
->se_argv
[i
]);
print_service("REDO", sep
);
print_service("ADD ", sep
);
sp
= getservbyname(sep
->se_service
, sep
->se_proto
);
syslog(LOG_ERR
, "%s/%s: unknown service",
sep
->se_service
, sep
->se_proto
);
if (sp
->s_port
!= sep
->se_ctrladdr
.sin_port
) {
sep
->se_ctrladdr
.sin_family
= AF_INET
;
sep
->se_ctrladdr
.sin_port
= sp
->s_port
;
* Purge anything not looked at above.
omask
= sigblock(SIGBLOCK
);
print_service("FREE", sep
);
(void) sigsetmask(omask
);
register struct servtab
*sep
;
for (sep
= servtab
; sep
; sep
= sep
->se_next
)
register struct servtab
*sep
;
if ((sep
->se_fd
= socket(AF_INET
, sep
->se_socktype
, 0)) < 0) {
fprintf(stderr
, "socket failed on %s/%s: %s\n",
sep
->se_service
, sep
->se_proto
,
syslog(LOG_ERR
, "%s/%s: socket: %m",
sep
->se_service
, sep
->se_proto
);
#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");
if (bind(sep
->se_fd
, (struct sockaddr
*)&sep
->se_ctrladdr
,
sizeof (sep
->se_ctrladdr
)) < 0) {
fprintf(stderr
, "bind failed on %s/%s: %s\n",
sep
->se_service
, sep
->se_proto
,
syslog(LOG_ERR
, "%s/%s: bind: %m",
sep
->se_service
, sep
->se_proto
);
(void) close(sep
->se_fd
);
if (sep
->se_socktype
== SOCK_STREAM
)
FD_SET(sep
->se_fd
, &allsock
);
if (sep
->se_fd
> maxsock
)
fprintf(stderr
, "registered %s on %d\n",
sep
->se_server
, sep
->se_fd
);
* Finish with a service and its socket.
register struct servtab
*sep
;
FD_CLR(sep
->se_fd
, &allsock
);
(void) close(sep
->se_fd
);
* Don't keep the pid of this running deamon: when reapchild()
* reaps this pid, it would erroneously increment nsock.
register struct servtab
*sep
;
sep
= (struct servtab
*)malloc(sizeof (*sep
));
if (sep
== (struct servtab
*)0) {
syslog(LOG_ERR
, "Out of memory.");
omask
= sigblock(SIGBLOCK
);
char *sskip(), *skip(), *nextline();
fseek(fconfig
, 0L, SEEK_SET
);
fconfig
= fopen(CONFIG
, "r");
return (fconfig
!= NULL
);
register struct servtab
*sep
= &serv
;
char *cp
, *arg
, *newstr();
static char TCPMUX_TOKEN
[] = "tcpmux/";
#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1)
while ((cp
= nextline(fconfig
)) && (*cp
== '#' || *cp
== '\0'))
return ((struct servtab
*)0);
* clear the static buffer, since some fields (se_ctrladdr,
* for example) don't get initialized here.
bzero((caddr_t
)sep
, sizeof *sep
);
/* got an empty line containing just blanks/tabs. */
if (strncmp(arg
, TCPMUX_TOKEN
, MUX_LEN
) == 0) {
sep
->se_type
= MUXPLUS_TYPE
;
sep
->se_service
= newstr(c
);
sep
->se_service
= newstr(arg
);
sep
->se_type
= NORM_TYPE
;
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
;
sep
->se_proto
= newstr(sskip(&cp
));
sep
->se_wait
= strcmp(arg
, "wait") == 0;
* Silently enforce "nowait" for TCPMUX services since
* they don't have an assigned port to listen on.
if (strcmp(sep
->se_proto
, "tcp")) {
"%s: bad protocol for tcpmux service %s",
CONFIG
, sep
->se_service
);
if (sep
->se_socktype
!= SOCK_STREAM
) {
"%s: bad socket type for tcpmux service %s",
CONFIG
, sep
->se_service
);
sep
->se_user
= newstr(sskip(&cp
));
sep
->se_server
= newstr(sskip(&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)
if (bi
->bi_service
== 0) {
syslog(LOG_ERR
, "internal service %s unknown",
sep
->se_wait
= bi
->bi_wait
;
for (arg
= skip(&cp
); cp
; arg
= skip(&cp
))
sep
->se_argv
[argc
++] = newstr(arg
);
sep
->se_argv
[argc
++] = NULL
;
register struct servtab
*cp
;
for (i
= 0; i
< MAXARGV
; i
++)
* Safe skip - if skip returns null, log a syntax error in the
* configuration file and exit.
syslog(LOG_ERR
, "%s: syntax error", CONFIG
);
register char *cp
= *cpp
;
while (*cp
== ' ' || *cp
== '\t')
(void) ungetc(c
, fconfig
);
if (c
== ' ' || c
== '\t')
if (cp
= nextline(fconfig
))
while (*cp
&& *cp
!= ' ' && *cp
!= '\t')
if (fgets(line
, sizeof (line
), fd
) == NULL
)
if (cp
= strdup(cp
? cp
: ""))
syslog(LOG_ERR
, "strdup: %m");
if (getpeername(s
, (struct sockaddr
*)&sin
, &size
) == 0)
(void) sprintf(buf
, "-%s [%s]", a
, inet_ntoa(sin
.sin_addr
));
(void) sprintf(buf
, "-%s", a
);
strncpy(cp
, buf
, LastArg
- cp
);
* Internet services provided internally by inetd:
echo_stream(s
, sep
) /* Echo service -- echo data back */
setproctitle(sep
->se_service
, s
);
while ((i
= read(s
, buffer
, sizeof(buffer
))) > 0 &&
echo_dg(s
, sep
) /* Echo service -- echo data back */
if ((i
= recvfrom(s
, buffer
, sizeof(buffer
), 0, &sa
, &size
)) < 0)
(void) sendto(s
, buffer
, i
, 0, &sa
, sizeof(sa
));
discard_stream(s
, sep
) /* Discard service -- ignore data */
setproctitle(sep
->se_service
, s
);
while ((ret
= read(s
, buffer
, sizeof(buffer
))) > 0)
if (ret
== 0 || errno
!= EINTR
)
discard_dg(s
, sep
) /* Discard service -- ignore data */
(void) read(s
, buffer
, sizeof(buffer
));
for (i
= 0; i
<= 128; ++i
)
chargen_stream(s
, sep
) /* Character generator */
setproctitle(sep
->se_service
, s
);
text
[LINESIZ
+ 1] = '\n';
if ((len
= endring
- rs
) >= LINESIZ
)
bcopy(rs
, text
, LINESIZ
);
bcopy(ring
, text
+ len
, LINESIZ
- len
);
if (write(s
, text
, sizeof(text
)) != sizeof(text
))
chargen_dg(s
, sep
) /* Character generator */
if (recvfrom(s
, text
, sizeof(text
), 0, &sa
, &size
) < 0)
if ((len
= endring
- rs
) >= LINESIZ
)
bcopy(rs
, text
, LINESIZ
);
bcopy(ring
, text
+ len
, LINESIZ
- len
);
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.
if (gettimeofday(&tv
, (struct timezone
*)0) < 0) {
fprintf(stderr
, "Unable to get time of day\n");
#define OFFSET ((u_long)25567 * 24*60*60)
return (htonl((long)(tv
.tv_sec
+ OFFSET
)));
(void) write(s
, (char *) &result
, sizeof(result
));
if (recvfrom(s
, (char *)&result
, sizeof(result
), 0, &sa
, &size
) < 0)
(void) sendto(s
, (char *) &result
, sizeof(result
), 0, &sa
, sizeof(sa
));
daytime_stream(s
, sep
) /* Return human-readable time of day */
clock
= time((time_t *) 0);
(void) sprintf(buffer
, "%.24s\r\n", ctime(&clock
));
(void) write(s
, buffer
, strlen(buffer
));
daytime_dg(s
, sep
) /* Return human-readable time of day */
clock
= time((time_t *) 0);
if (recvfrom(s
, buffer
, sizeof(buffer
), 0, &sa
, &size
) < 0)
(void) sprintf(buffer
, "%.24s\r\n", ctime(&clock
));
(void) sendto(s
, buffer
, strlen(buffer
), 0, &sa
, sizeof(sa
));
* Dump relevant information to stderr
print_service(action
, sep
)
"%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
action
, sep
->se_service
, sep
->se_proto
,
sep
->se_wait
, sep
->se_user
, (int)sep
->se_bi
, sep
->se_server
);
* Based on TCPMUX.C by Mark K. Lottor November 1988
* sri-nic::ps:<mkl>tcpmux.c
static int /* # of characters upto \r,\n or \0 */
n
= read(fd
, buf
, len
-count
);
if (*buf
== '\r' || *buf
== '\n' || *buf
== '\0')
#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */
#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1)
register struct servtab
*sep
;
char service
[MAX_SERV_LEN
+1];
/* Get requested service name */
if ((len
= getline(s
, service
, MAX_SERV_LEN
)) < 0) {
strwrite(s
, "-Error reading service name\r\n");
fprintf(stderr
, "tcpmux: someone wants %s\n", service
);
* Help is a required command, and lists available services,
if (!strcasecmp(service
,"help")) {
for (sep
= servtab
; sep
; sep
= sep
->se_next
) {
(void) write(s
, sep
->se_service
, strlen(sep
->se_service
));
/* Try matching a service in inetd.conf with the request */
for (sep
= servtab
; sep
; sep
= sep
->se_next
) {
if (!strcasecmp(service
,sep
->se_service
)) {
strwrite(s
, "-Service not available\r\n");