* Copyright (c) 1983,1991 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
/*static char sccsid[] = "from: @(#)inetd.c 5.30 (Berkeley) 6/3/91";*/
static char rcsid
[] = "$Id: inetd.c,v 1.3 1993/10/11 21:47:50 jkh Exp $";
* 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
* 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 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 single-threaded/multi-threaded
* user user to run daemon as
* server program full path name
* server program arguments maximum of MAXARGS (20)
* service name/version must be in /etc/rpc
* 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 <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))
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_rpc
; /* ==1 if this is an RPC service */
int se_rpc_prog
; /* RPC program number */
u_int se_rpc_lowvers
; /* RPC low version */
u_int se_rpc_highvers
; /* RPC high version */
int se_count
; /* number started since se_time */
struct timeval se_time
; /* start of se_count */
/* should be in <rpc/rpc.h> ?? */
int pmap_unset(u_long prognum
,u_long versnum
);
int pmap_set(u_long prognum
,u_long versnum
,int protocol
,u_short port
);
/* comes from -lutil ?? */
void unregisterrpc(struct servtab
*sep
);
void setup(struct servtab
*sep
);
struct servtab
* enter(struct servtab
*cp
);
struct servtab
* getconfigent();
void freeconfig(struct servtab
*cp
);
char * nextline(FILE *fd
);
void setproctitle(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 chargen_stream(int s
,struct servtab
*sep
);
void chargen_dg(int s
,struct servtab
*sep
);
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
);
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
)(); /* 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
},
#define NUMINT (sizeof(intab) / sizeof(struct inent))
char *CONFIG
= _PATH_INETDCONF
;
main(int argc
, char **argv
, char **envp
)
if (envp
== 0 || *envp
== 0)
LastArg
= envp
[-1] + strlen(envp
[-1]);
while ((ch
= getopt(argc
, argv
, "dl")) != EOF
)
fprintf(stderr
, "usage: inetd [-d]");
openlog("inetd", LOG_PID
| LOG_NOWAIT
, LOG_DAEMON
);
bzero((char *)&sv
, sizeof(sv
));
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\n");
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
);
syslog(LOG_WARNING
, "accept (for %s): %m",
if(getpeername(ctrl
,(struct sockaddr
*)&peer
,&i
)) {
"getpeername(for %s): %m",
syslog(LOG_INFO
,"%s from %s",
inet_ntoa(peer
.sin_addr
));
(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\n",
sep
->se_service
, sep
->se_proto
);
FD_CLR(sep
->se_fd
, &allsock
);
(void) close(sep
->se_fd
);
syslog(LOG_ERR
, "fork: %m");
if (sep
->se_socktype
== SOCK_STREAM
)
if (pid
&& sep
->se_wait
) {
FD_CLR(sep
->se_fd
, &allsock
);
for (tmpint
= maxsock
; --tmpint
> 2; )
(*sep
->se_bi
->bi_fn
)(ctrl
, sep
);
fprintf(stderr
, "%d execl %s\n",
getpid(), sep
->se_server
);
if ((pwd
= getpwnam(sep
->se_user
)) == NULL
) {
"getpwnam: %s: No such user",
if (sep
->se_socktype
!= SOCK_STREAM
)
recv(0, buf
, sizeof (buf
), 0);
(void) setgid((gid_t
)pwd
->pw_gid
);
initgroups(pwd
->pw_name
, 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
);
if (sep
->se_socktype
== SOCK_STREAM
)
pid
= wait3(&status
, WNOHANG
, (struct rusage
*)0);
fprintf(stderr
, "%d reaped\n", pid
);
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
);
struct servtab
*sep
, *cp
, **sepp
;
syslog(LOG_ERR
, "%s: %m", CONFIG
);
for (sep
= servtab
; sep
; sep
= sep
->se_next
)
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)
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
);
(void) close(sep
->se_fd
);
if (sp
->s_port
!= sep
->se_ctrladdr
.sin_port
) {
sep
->se_ctrladdr
.sin_port
= sp
->s_port
;
(void) close(sep
->se_fd
);
rpc
= getrpcbyname(sep
->se_service
);
syslog(LOG_ERR
, "%s/%s unknown RPC service.",
sep
->se_service
, sep
->se_proto
);
(void) close(sep
->se_fd
);
if (rpc
->r_number
!= sep
->se_rpc_prog
) {
sep
->se_rpc_prog
= rpc
->r_number
;
(void) close(sep
->se_fd
);
* Purge anything not looked at above.
omask
= sigblock(SIGBLOCK
);
FD_CLR(sep
->se_fd
, &allsock
);
(void) close(sep
->se_fd
);
print_service("FREE", sep
);
if (sep
->se_rpc
&& sep
->se_rpc_prog
> 0)
(void) sigsetmask(omask
);
unregisterrpc(struct servtab
*sep
)
omask
= sigblock(SIGBLOCK
);
for (sepp
= servtab
; sepp
; sepp
= sepp
->se_next
) {
if (sep
->se_checked
== 0 ||
sep
->se_rpc_prog
!= sepp
->se_rpc_prog
)
print_service("UNREG", sep
);
for (i
= sep
->se_rpc_lowvers
; i
<= sep
->se_rpc_highvers
; i
++)
pmap_unset(sep
->se_rpc_prog
, i
);
(void) close(sep
->se_fd
);
(void) sigsetmask(omask
);
for (sep
= servtab
; sep
; sep
= sep
->se_next
)
setup(struct servtab
*sep
)
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
);
#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) {
syslog(LOG_ERR
, "%s/%s: bind: %m",
sep
->se_service
, sep
->se_proto
);
(void) close(sep
->se_fd
);
if (getsockname(sep
->se_fd
, (struct sockaddr
*)&sep
->se_ctrladdr
,
syslog(LOG_ERR
, "%s/%s: getsockname: %m",
sep
->se_service
, sep
->se_proto
);
(void) close(sep
->se_fd
);
print_service("REG ", sep
);
for (i
= sep
->se_rpc_lowvers
; i
<= sep
->se_rpc_highvers
; i
++) {
pmap_unset(sep
->se_rpc_prog
, i
);
pmap_set(sep
->se_rpc_prog
, i
,
(sep
->se_socktype
== SOCK_DGRAM
)
? IPPROTO_UDP
: IPPROTO_TCP
,
ntohs(sep
->se_ctrladdr
.sin_port
));
if (sep
->se_socktype
== SOCK_STREAM
)
FD_SET(sep
->se_fd
, &allsock
);
if (sep
->se_fd
> maxsock
)
enter(struct servtab
*cp
)
sep
= (struct servtab
*)malloc(sizeof (*sep
));
if (sep
== (struct servtab
*)0) {
syslog(LOG_ERR
, "Out of memory.");
omask
= sigblock(SIGBLOCK
);
fseek(fconfig
, 0L, L_SET
);
fconfig
= fopen(CONFIG
, "r");
return (fconfig
!= NULL
);
struct servtab
*sep
= &serv
;
while ((cp
= nextline(fconfig
)) && *cp
== '#')
return ((struct servtab
*)0);
sep
->se_service
= newstr(arg
);
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(skip(&cp
));
if (strncmp(sep
->se_proto
, "rpc/", 4) == 0) {
sep
->se_rpc_prog
= sep
->se_rpc_lowvers
= sep
->se_rpc_lowvers
= 0;
sep
->se_ctrladdr
.sin_family
= AF_INET
;
sep
->se_ctrladdr
.sin_port
= 0;
sep
->se_ctrladdr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
if ((versp
= rindex(sep
->se_service
, '/'))) {
switch (sscanf(versp
, "%d-%d",
&sep
->se_rpc_highvers
)) {
syslog(LOG_ERR
, "bad RPC version specifier; %s\n", sep
->se_service
);
sep
->se_rpc_highvers
= 1;
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) {
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\n",
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
;
freeconfig(struct servtab
*cp
)
for (i
= 0; i
< MAXARGV
; i
++)
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");
setproctitle(char *a
, int s
)
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(int s
,struct servtab
*sep
) /* Echo service -- echo data back */
setproctitle(sep
->se_service
, s
);
while ((i
= read(s
, buffer
, sizeof(buffer
))) > 0 &&
write(s
, buffer
, i
) == i
)
echo_dg(int s
,struct servtab
*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(int s
,struct servtab
*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(int s
,struct servtab
*sep
) /* Discard service -- ignore data */
(void) read(s
, buffer
, sizeof(buffer
));
for (i
= 0; i
<= 128; ++i
)
chargen_stream(int s
,struct servtab
*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(int s
,struct servtab
*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");
return (htonl((long)tv
.tv_sec
+ 2208988800U));
machtime_stream(int s
,struct servtab
*sep
)
(void) write(s
, (char *) &result
, sizeof(result
));
machtime_dg(int s
,struct servtab
*sep
)
if (recvfrom(s
, (char *)&result
, sizeof(result
), 0, &sa
, &size
) < 0)
(void) sendto(s
, (char *) &result
, sizeof(result
), 0, &sa
, sizeof(sa
));
daytime_stream(int s
,struct servtab
*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(int s
,struct servtab
*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(char *action
, struct servtab
*sep
)
"%s: %s rpcnum=%d, rpcvers=%d/%d, wait=%d, user=%s builtin=%x server=%s\n",
sep
->se_rpc_prog
, sep
->se_rpc_lowvers
, sep
->se_rpc_highvers
,
sep
->se_wait
, sep
->se_user
, (int)sep
->se_bi
, sep
->se_server
);
"%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
);