121134Sdist /*
266778Spendry * Copyright (c) 1983, 1991, 1993, 1994
361830Sbostic * The Regents of the University of California. All rights reserved.
433136Sbostic *
542799Sbostic * %sccs.include.redist.c%
621134Sdist */
721134Sdist
816374Skarels #ifndef lint
961830Sbostic static char copyright[] =
1066778Spendry "@(#) Copyright (c) 1983, 1991, 1993, 1994\n\
1161830Sbostic The Regents of the University of California. All rights reserved.\n";
1233136Sbostic #endif /* not lint */
1316374Skarels
1421134Sdist #ifndef lint
15*66779Skarels static char sccsid[] = "@(#)inetd.c 8.4 (Berkeley) 04/13/94";
1633136Sbostic #endif /* not lint */
1721134Sdist
1816374Skarels /*
1916374Skarels * Inetd - Internet super-server
2016374Skarels *
2157769Sandrew * This program invokes all internet services as needed. Connection-oriented
2257769Sandrew * services are invoked each time a connection is made, by creating a process.
2357769Sandrew * This process is passed the connection as file descriptor 0 and is expected
2457769Sandrew * to do a getpeername to find out the source host and port.
2516374Skarels *
2616374Skarels * Datagram oriented services are invoked when a datagram
2716374Skarels * arrives; a process is created and passed a pending message
2816374Skarels * on file descriptor 0. Datagram servers may either connect
2916374Skarels * to their peer, freeing up the original socket for inetd
3016374Skarels * to receive further messages on, or ``take over the socket'',
3116374Skarels * processing all arriving datagrams and, eventually, timing
3216374Skarels * out. The first type of server is said to be ``multi-threaded'';
3316374Skarels * the second type of server ``single-threaded''.
3416374Skarels *
3516374Skarels * Inetd uses a configuration file which is read at startup
3616374Skarels * and, possibly, at some later time in response to a hangup signal.
3716374Skarels * The configuration file is ``free format'' with fields given in the
3816374Skarels * order shown below. Continuation lines for an entry must being with
3916374Skarels * a space or tab. All fields must be present in each entry.
4016374Skarels *
4157769Sandrew * service name must be in /etc/services or must
4257769Sandrew * name a tcpmux service
4316374Skarels * socket type stream/dgram/raw/rdm/seqpacket
4416374Skarels * protocol must be in /etc/protocols
4516374Skarels * wait/nowait single-threaded/multi-threaded
4617346Sbloom * user user to run daemon as
4716374Skarels * server program full path name
4840740Sbostic * server program arguments maximum of MAXARGS (20)
4916374Skarels *
5057769Sandrew * TCP services without official port numbers are handled with the
5157769Sandrew * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
5257769Sandrew * requests. When a connection is made from a foreign host, the service
5357769Sandrew * requested is passed to tcpmux, which looks it up in the servtab list
5457769Sandrew * and returns the proper entry for the service. Tcpmux returns a
5557769Sandrew * negative reply if the service doesn't exist, otherwise the invoked
5657769Sandrew * server is expected to return the positive reply if the service type in
5757769Sandrew * inetd.conf file has the prefix "tcpmux/". If the service type has the
5857769Sandrew * prefix "tcpmux/+", tcpmux will return the positive reply for the
5957769Sandrew * process; this is for compatibility with older server code, and also
6057769Sandrew * allows you to invoke programs that use stdin/stdout without putting any
6157769Sandrew * special server code in them. Services that use tcpmux are "nowait"
6257769Sandrew * because they do not have a well-known port and hence cannot listen
6357769Sandrew * for new requests.
6457769Sandrew *
6516374Skarels * Comment lines are indicated by a `#' in column 1.
6616374Skarels */
6716374Skarels #include <sys/param.h>
6816374Skarels #include <sys/stat.h>
6916374Skarels #include <sys/ioctl.h>
7016374Skarels #include <sys/socket.h>
7116374Skarels #include <sys/wait.h>
7226921Slepreau #include <sys/time.h>
7326921Slepreau #include <sys/resource.h>
7416374Skarels
7516374Skarels #include <netinet/in.h>
7616374Skarels #include <arpa/inet.h>
7716374Skarels
7816374Skarels #include <errno.h>
7957769Sandrew #include <fcntl.h>
8016374Skarels #include <netdb.h>
8117346Sbloom #include <pwd.h>
8257769Sandrew #include <signal.h>
8336603Sbostic #include <stdio.h>
8457769Sandrew #include <stdlib.h>
8542067Sbostic #include <string.h>
8657769Sandrew #include <syslog.h>
8757769Sandrew #include <unistd.h>
8857769Sandrew
8937282Sbostic #include "pathnames.h"
9016374Skarels
9127535Skarels #define TOOMANY 40 /* don't start more than TOOMANY */
9227535Skarels #define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
9327535Skarels #define RETRYTIME (60*10) /* retry after bind or server fail */
9427535Skarels
9527535Skarels #define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
9627535Skarels
9716374Skarels
9816374Skarels int debug = 0;
9925046Skarels int nsock, maxsock;
10025046Skarels fd_set allsock;
10116374Skarels int options;
10227535Skarels int timingout;
10357769Sandrew int toomany = TOOMANY;
10416374Skarels struct servent *sp;
10516374Skarels
10616374Skarels struct servtab {
10716374Skarels char *se_service; /* name of service */
10816374Skarels int se_socktype; /* type of socket to use */
10916374Skarels char *se_proto; /* protocol used */
11016374Skarels short se_wait; /* single threaded server */
11116374Skarels short se_checked; /* looked at during merge */
11217346Sbloom char *se_user; /* user name to run as */
11326877Skarels struct biltin *se_bi; /* if built-in, description */
11416374Skarels char *se_server; /* server program */
11540740Sbostic #define MAXARGV 20
11616374Skarels char *se_argv[MAXARGV+1]; /* program arguments */
11716374Skarels int se_fd; /* open descriptor */
11857769Sandrew int se_type; /* type */
11916374Skarels struct sockaddr_in se_ctrladdr;/* bound address */
12027535Skarels int se_count; /* number started since se_time */
12127535Skarels struct timeval se_time; /* start of se_count */
12216374Skarels struct servtab *se_next;
12316374Skarels } *servtab;
12416374Skarels
12557769Sandrew #define NORM_TYPE 0
12657769Sandrew #define MUX_TYPE 1
12757769Sandrew #define MUXPLUS_TYPE 2
12857769Sandrew #define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \
12957769Sandrew ((sep)->se_type == MUXPLUS_TYPE))
13057769Sandrew #define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE)
13157769Sandrew
13226877Skarels
13366696Spendry void chargen_dg __P((int, struct servtab *));
13466696Spendry void chargen_stream __P((int, struct servtab *));
13566696Spendry void close_sep __P((struct servtab *));
13666696Spendry void config __P((int));
13766696Spendry void daytime_dg __P((int, struct servtab *));
13866696Spendry void daytime_stream __P((int, struct servtab *));
13966696Spendry void discard_dg __P((int, struct servtab *));
14066696Spendry void discard_stream __P((int, struct servtab *));
14166696Spendry void echo_dg __P((int, struct servtab *));
14266696Spendry void echo_stream __P((int, struct servtab *));
14366696Spendry void endconfig __P((void));
14466696Spendry struct servtab *enter __P((struct servtab *));
14566696Spendry void freeconfig __P((struct servtab *));
14666696Spendry struct servtab *getconfigent __P((void));
14766696Spendry void machtime_dg __P((int, struct servtab *));
14866696Spendry void machtime_stream __P((int, struct servtab *));
14966696Spendry char *newstr __P((char *));
15066696Spendry char *nextline __P((FILE *));
15166696Spendry void print_service __P((char *, struct servtab *));
15266696Spendry void reapchild __P((int));
15366696Spendry void retry __P((int));
15466696Spendry int setconfig __P((void));
15566696Spendry void setup __P((struct servtab *));
15666696Spendry char *sskip __P((char **));
15766696Spendry char *skip __P((char **));
15866696Spendry struct servtab *tcpmux __P((int));
15966696Spendry
16026877Skarels struct biltin {
16126877Skarels char *bi_service; /* internally provided service name */
16226877Skarels int bi_socktype; /* type of socket supported */
16326877Skarels short bi_fork; /* 1 if should fork before call */
16426877Skarels short bi_wait; /* 1 if should wait for child */
16566696Spendry void (*bi_fn)(); /* function which performs it */
16626877Skarels } biltins[] = {
16726877Skarels /* Echo received data */
16866696Spendry { "echo", SOCK_STREAM, 1, 0, echo_stream },
16966696Spendry { "echo", SOCK_DGRAM, 0, 0, echo_dg },
17026877Skarels
17126877Skarels /* Internet /dev/null */
17266696Spendry { "discard", SOCK_STREAM, 1, 0, discard_stream },
17366696Spendry { "discard", SOCK_DGRAM, 0, 0, discard_dg },
17426877Skarels
17526877Skarels /* Return 32 bit time since 1970 */
17666696Spendry { "time", SOCK_STREAM, 0, 0, machtime_stream },
17766696Spendry { "time", SOCK_DGRAM, 0, 0, machtime_dg },
17826877Skarels
17926877Skarels /* Return human-readable time */
18066696Spendry { "daytime", SOCK_STREAM, 0, 0, daytime_stream },
18166696Spendry { "daytime", SOCK_DGRAM, 0, 0, daytime_dg },
18226877Skarels
18326877Skarels /* Familiar character generator */
18466696Spendry { "chargen", SOCK_STREAM, 1, 0, chargen_stream },
18566696Spendry { "chargen", SOCK_DGRAM, 0, 0, chargen_dg },
18657769Sandrew
18766696Spendry { "tcpmux", SOCK_STREAM, 1, 0, (void (*)())tcpmux },
18857769Sandrew
18966696Spendry { NULL }
19026877Skarels };
19126877Skarels
19226877Skarels #define NUMINT (sizeof(intab) / sizeof(struct inent))
19337282Sbostic char *CONFIG = _PATH_INETDCONF;
19426877Skarels char **Argv;
19526877Skarels char *LastArg;
19616374Skarels
19766696Spendry int
main(argc,argv,envp)19826877Skarels main(argc, argv, envp)
19916374Skarels int argc;
20026877Skarels char *argv[], *envp[];
20116374Skarels {
20266696Spendry struct servtab *sep;
20366696Spendry struct passwd *pwd;
20427535Skarels struct sigvec sv;
20566696Spendry int tmpint, ch, dofork;
20666696Spendry pid_t pid;
20736603Sbostic char buf[50];
20816374Skarels
20926877Skarels Argv = argv;
21026877Skarels if (envp == 0 || *envp == 0)
21126877Skarels envp = argv;
21226877Skarels while (*envp)
21326877Skarels envp++;
21426877Skarels LastArg = envp[-1] + strlen(envp[-1]);
21516374Skarels
21657769Sandrew openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
21757769Sandrew
21857769Sandrew while ((ch = getopt(argc, argv, "dR:")) != EOF)
21936603Sbostic switch(ch) {
22016374Skarels case 'd':
22116374Skarels debug = 1;
22216374Skarels options |= SO_DEBUG;
22316374Skarels break;
22457769Sandrew case 'R': { /* invocation rate */
22557769Sandrew char *p;
22657769Sandrew
22757769Sandrew tmpint = strtol(optarg, &p, 0);
22857769Sandrew if (tmpint < 1 || *p)
22957769Sandrew syslog(LOG_ERR,
23057769Sandrew "-R %s: bad value for service invocation rate",
23157769Sandrew optarg);
23257769Sandrew else
23357769Sandrew toomany = tmpint;
23457769Sandrew break;
23557769Sandrew }
23636603Sbostic case '?':
23716374Skarels default:
23857769Sandrew syslog(LOG_ERR,
23957769Sandrew "usage: inetd [-d] [-R rate] [conf-file]");
24036603Sbostic exit(1);
24116374Skarels }
24236603Sbostic argc -= optind;
24336603Sbostic argv += optind;
24436603Sbostic
24516374Skarels if (argc > 0)
24616374Skarels CONFIG = argv[0];
24757769Sandrew if (debug == 0) {
24844711Skarels daemon(0, 0);
24957769Sandrew }
25066696Spendry memset(&sv, 0, sizeof(sv));
25127535Skarels sv.sv_mask = SIGBLOCK;
25227535Skarels sv.sv_handler = retry;
25327535Skarels sigvec(SIGALRM, &sv, (struct sigvec *)0);
25466696Spendry config(SIGHUP);
25527535Skarels sv.sv_handler = config;
25627535Skarels sigvec(SIGHUP, &sv, (struct sigvec *)0);
25727535Skarels sv.sv_handler = reapchild;
25827535Skarels sigvec(SIGCHLD, &sv, (struct sigvec *)0);
25927535Skarels
26036603Sbostic {
26136603Sbostic /* space for daemons to overwrite environment for ps */
26236603Sbostic #define DUMMYSIZE 100
26336603Sbostic char dummy[DUMMYSIZE];
26436603Sbostic
26536603Sbostic (void)memset(dummy, 'x', sizeof(DUMMYSIZE) - 1);
26636603Sbostic dummy[DUMMYSIZE - 1] = '\0';
26736603Sbostic (void)setenv("inetd_dummy", dummy, 1);
26836603Sbostic }
26936603Sbostic
27016374Skarels for (;;) {
27136603Sbostic int n, ctrl;
27227535Skarels fd_set readable;
27316374Skarels
27432091Skarels if (nsock == 0) {
27532091Skarels (void) sigblock(SIGBLOCK);
27632091Skarels while (nsock == 0)
27732246Sbostic sigpause(0L);
27832246Sbostic (void) sigsetmask(0L);
27932091Skarels }
28027535Skarels readable = allsock;
28127535Skarels if ((n = select(maxsock + 1, &readable, (fd_set *)0,
28227535Skarels (fd_set *)0, (struct timeval *)0)) <= 0) {
28327535Skarels if (n < 0 && errno != EINTR)
28457769Sandrew syslog(LOG_WARNING, "select: %m");
28527535Skarels sleep(1);
28627535Skarels continue;
28727535Skarels }
28827535Skarels for (sep = servtab; n && sep; sep = sep->se_next)
28946973Skarels if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
29046973Skarels n--;
29146973Skarels if (debug)
29246973Skarels fprintf(stderr, "someone wants %s\n",
29346973Skarels sep->se_service);
294*66779Skarels if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
29546973Skarels ctrl = accept(sep->se_fd, (struct sockaddr *)0,
29646973Skarels (int *)0);
29746973Skarels if (debug)
29846973Skarels fprintf(stderr, "accept, ctrl %d\n", ctrl);
29946973Skarels if (ctrl < 0) {
30057769Sandrew if (errno != EINTR)
30157769Sandrew syslog(LOG_WARNING,
30257769Sandrew "accept (for %s): %m",
30357769Sandrew sep->se_service);
30446973Skarels continue;
30546973Skarels }
30657769Sandrew /*
30757769Sandrew * Call tcpmux to find the real service to exec.
30857769Sandrew */
30957769Sandrew if (sep->se_bi &&
31066696Spendry sep->se_bi->bi_fn == (void (*)()) tcpmux) {
31157769Sandrew sep = tcpmux(ctrl);
31257769Sandrew if (sep == NULL) {
31357769Sandrew close(ctrl);
31457769Sandrew continue;
31557769Sandrew }
31657769Sandrew }
31746973Skarels } else
31846973Skarels ctrl = sep->se_fd;
31946973Skarels (void) sigblock(SIGBLOCK);
32046973Skarels pid = 0;
32146973Skarels dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
32246973Skarels if (dofork) {
32346973Skarels if (sep->se_count++ == 0)
32446973Skarels (void)gettimeofday(&sep->se_time,
32546973Skarels (struct timezone *)0);
32657769Sandrew else if (sep->se_count >= toomany) {
32727535Skarels struct timeval now;
32827535Skarels
32927535Skarels (void)gettimeofday(&now, (struct timezone *)0);
33027535Skarels if (now.tv_sec - sep->se_time.tv_sec >
33127535Skarels CNT_INTVL) {
33227535Skarels sep->se_time = now;
33327535Skarels sep->se_count = 1;
33427535Skarels } else {
33527535Skarels syslog(LOG_ERR,
33657769Sandrew "%s/%s server failing (looping), service terminated",
33727535Skarels sep->se_service, sep->se_proto);
33857769Sandrew close_sep(sep);
33957769Sandrew sigsetmask(0L);
34027535Skarels if (!timingout) {
34127535Skarels timingout = 1;
34227535Skarels alarm(RETRYTIME);
34327535Skarels }
34457769Sandrew continue;
34527535Skarels }
34646973Skarels }
34746973Skarels pid = fork();
34846973Skarels }
34946973Skarels if (pid < 0) {
35046973Skarels syslog(LOG_ERR, "fork: %m");
351*66779Skarels if (!sep->se_wait &&
352*66779Skarels sep->se_socktype == SOCK_STREAM)
35346973Skarels close(ctrl);
35446973Skarels sigsetmask(0L);
35546973Skarels sleep(1);
35646973Skarels continue;
35746973Skarels }
35846973Skarels if (pid && sep->se_wait) {
35946973Skarels sep->se_wait = pid;
36049806Sbostic if (sep->se_fd >= 0) {
36149806Sbostic FD_CLR(sep->se_fd, &allsock);
36249806Sbostic nsock--;
36349806Sbostic }
36446973Skarels }
36546973Skarels sigsetmask(0L);
36646973Skarels if (pid == 0) {
36746973Skarels if (debug && dofork)
36844711Skarels setsid();
36957769Sandrew if (dofork) {
37057769Sandrew if (debug)
37157769Sandrew fprintf(stderr, "+ Closing from %d\n",
37257769Sandrew maxsock);
37357769Sandrew for (tmpint = maxsock; tmpint > 2; tmpint--)
37436603Sbostic if (tmpint != ctrl)
37536603Sbostic close(tmpint);
37657769Sandrew }
37746973Skarels if (sep->se_bi)
37826877Skarels (*sep->se_bi->bi_fn)(ctrl, sep);
37946973Skarels else {
38046973Skarels if (debug)
38146973Skarels fprintf(stderr, "%d execl %s\n",
38246973Skarels getpid(), sep->se_server);
38326877Skarels dup2(ctrl, 0);
38426877Skarels close(ctrl);
38526877Skarels dup2(0, 1);
38626877Skarels dup2(0, 2);
38726877Skarels if ((pwd = getpwnam(sep->se_user)) == NULL) {
38826877Skarels syslog(LOG_ERR,
38957769Sandrew "%s/%s: %s: No such user",
39057769Sandrew sep->se_service, sep->se_proto,
39157769Sandrew sep->se_user);
39227535Skarels if (sep->se_socktype != SOCK_STREAM)
39327535Skarels recv(0, buf, sizeof (buf), 0);
39426877Skarels _exit(1);
39526877Skarels }
39626877Skarels if (pwd->pw_uid) {
39757769Sandrew if (setgid(pwd->pw_gid) < 0) {
39857769Sandrew syslog(LOG_ERR,
39957769Sandrew "%s: can't set gid %d: %m",
40057769Sandrew sep->se_service, pwd->pw_gid);
40157769Sandrew _exit(1);
40257769Sandrew }
40357769Sandrew (void) initgroups(pwd->pw_name,
40457769Sandrew pwd->pw_gid);
40557769Sandrew if (setuid(pwd->pw_uid) < 0) {
40657769Sandrew syslog(LOG_ERR,
40757769Sandrew "%s: can't set uid %d: %m",
40857769Sandrew sep->se_service, pwd->pw_uid);
40957769Sandrew _exit(1);
41057769Sandrew }
41126877Skarels }
41226877Skarels execv(sep->se_server, sep->se_argv);
41326877Skarels if (sep->se_socktype != SOCK_STREAM)
41426877Skarels recv(0, buf, sizeof (buf), 0);
41557769Sandrew syslog(LOG_ERR,
41657769Sandrew "cannot execute %s: %m", sep->se_server);
41726877Skarels _exit(1);
41846973Skarels }
41946973Skarels }
420*66779Skarels if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
42146973Skarels close(ctrl);
42216374Skarels }
42316374Skarels }
42416374Skarels }
42516374Skarels
42639838Sbostic void
reapchild(signo)42766696Spendry reapchild(signo)
42866696Spendry int signo;
42916374Skarels {
43046973Skarels int status;
43166696Spendry pid_t pid;
43266696Spendry struct servtab *sep;
43316374Skarels
43416374Skarels for (;;) {
43546973Skarels pid = wait3(&status, WNOHANG, (struct rusage *)0);
43616374Skarels if (pid <= 0)
43716374Skarels break;
43816374Skarels if (debug)
43957769Sandrew fprintf(stderr, "%d reaped, status %#x\n",
44057769Sandrew pid, status);
44116374Skarels for (sep = servtab; sep; sep = sep->se_next)
44216374Skarels if (sep->se_wait == pid) {
44346973Skarels if (status)
44416510Sralph syslog(LOG_WARNING,
44516510Sralph "%s: exit status 0x%x",
44616374Skarels sep->se_server, status);
44716374Skarels if (debug)
44816374Skarels fprintf(stderr, "restored %s, fd %d\n",
44916374Skarels sep->se_service, sep->se_fd);
45025046Skarels FD_SET(sep->se_fd, &allsock);
45125046Skarels nsock++;
45216374Skarels sep->se_wait = 1;
45316374Skarels }
45416374Skarels }
45516374Skarels }
45616374Skarels
45739838Sbostic void
config(signo)45866696Spendry config(signo)
45966696Spendry int signo;
46016374Skarels {
46166696Spendry struct servtab *sep, *cp, **sepp;
46257769Sandrew struct passwd *pwd;
46332246Sbostic long omask;
46416374Skarels
46516374Skarels if (!setconfig()) {
46616510Sralph syslog(LOG_ERR, "%s: %m", CONFIG);
46716374Skarels return;
46816374Skarels }
46916374Skarels for (sep = servtab; sep; sep = sep->se_next)
47016374Skarels sep->se_checked = 0;
47116374Skarels while (cp = getconfigent()) {
47257769Sandrew if ((pwd = getpwnam(cp->se_user)) == NULL) {
47357769Sandrew syslog(LOG_ERR,
47457769Sandrew "%s/%s: No such user '%s', service ignored",
47557769Sandrew cp->se_service, cp->se_proto, cp->se_user);
47657769Sandrew continue;
47757769Sandrew }
47816374Skarels for (sep = servtab; sep; sep = sep->se_next)
47916374Skarels if (strcmp(sep->se_service, cp->se_service) == 0 &&
48016374Skarels strcmp(sep->se_proto, cp->se_proto) == 0)
48116374Skarels break;
48216374Skarels if (sep != 0) {
48316374Skarels int i;
48416374Skarels
48527535Skarels omask = sigblock(SIGBLOCK);
48640745Sbostic /*
48740745Sbostic * sep->se_wait may be holding the pid of a daemon
48840745Sbostic * that we're waiting for. If so, don't overwrite
48940745Sbostic * it unless the config file explicitly says don't
49040745Sbostic * wait.
49140745Sbostic */
49240745Sbostic if (cp->se_bi == 0 &&
49340745Sbostic (sep->se_wait == 1 || cp->se_wait == 0))
49427535Skarels sep->se_wait = cp->se_wait;
49516374Skarels #define SWAP(a, b) { char *c = a; a = b; b = c; }
49626921Slepreau if (cp->se_user)
49726921Slepreau SWAP(sep->se_user, cp->se_user);
49816374Skarels if (cp->se_server)
49916374Skarels SWAP(sep->se_server, cp->se_server);
50016374Skarels for (i = 0; i < MAXARGV; i++)
50116374Skarels SWAP(sep->se_argv[i], cp->se_argv[i]);
50216374Skarels sigsetmask(omask);
50316374Skarels freeconfig(cp);
50429794Skarels if (debug)
50529794Skarels print_service("REDO", sep);
50629794Skarels } else {
50716374Skarels sep = enter(cp);
50829794Skarels if (debug)
50929794Skarels print_service("ADD ", sep);
51029794Skarels }
51116374Skarels sep->se_checked = 1;
51257769Sandrew if (ISMUX(sep)) {
51357769Sandrew sep->se_fd = -1;
51457769Sandrew continue;
51557769Sandrew }
51616374Skarels sp = getservbyname(sep->se_service, sep->se_proto);
51716374Skarels if (sp == 0) {
51816510Sralph syslog(LOG_ERR, "%s/%s: unknown service",
51916374Skarels sep->se_service, sep->se_proto);
52057769Sandrew sep->se_checked = 0;
52116374Skarels continue;
52216374Skarels }
52327535Skarels if (sp->s_port != sep->se_ctrladdr.sin_port) {
52460924Smckusick sep->se_ctrladdr.sin_family = AF_INET;
52527535Skarels sep->se_ctrladdr.sin_port = sp->s_port;
52657769Sandrew if (sep->se_fd >= 0)
52757769Sandrew close_sep(sep);
52816374Skarels }
52927535Skarels if (sep->se_fd == -1)
53027535Skarels setup(sep);
53116374Skarels }
53216374Skarels endconfig();
53316374Skarels /*
53416374Skarels * Purge anything not looked at above.
53516374Skarels */
53627535Skarels omask = sigblock(SIGBLOCK);
53716374Skarels sepp = &servtab;
53816374Skarels while (sep = *sepp) {
53916374Skarels if (sep->se_checked) {
54016374Skarels sepp = &sep->se_next;
54116374Skarels continue;
54216374Skarels }
54316374Skarels *sepp = sep->se_next;
54457769Sandrew if (sep->se_fd >= 0)
54557769Sandrew close_sep(sep);
54629794Skarels if (debug)
54729794Skarels print_service("FREE", sep);
54816374Skarels freeconfig(sep);
54916374Skarels free((char *)sep);
55016374Skarels }
55116374Skarels (void) sigsetmask(omask);
55216374Skarels }
55316374Skarels
55439838Sbostic void
retry(signo)55566696Spendry retry(signo)
55666696Spendry int signo;
55727535Skarels {
55866696Spendry struct servtab *sep;
55927535Skarels
56027535Skarels timingout = 0;
56127535Skarels for (sep = servtab; sep; sep = sep->se_next)
56227535Skarels if (sep->se_fd == -1)
56327535Skarels setup(sep);
56427535Skarels }
56527535Skarels
56666696Spendry void
setup(sep)56727535Skarels setup(sep)
56866696Spendry struct servtab *sep;
56927535Skarels {
57027535Skarels int on = 1;
57127535Skarels
57227535Skarels if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
57357769Sandrew if (debug)
57457769Sandrew fprintf(stderr, "socket failed on %s/%s: %s\n",
57557769Sandrew sep->se_service, sep->se_proto,
57657769Sandrew strerror(errno));
57727535Skarels syslog(LOG_ERR, "%s/%s: socket: %m",
57827535Skarels sep->se_service, sep->se_proto);
57927535Skarels return;
58027535Skarels }
58127535Skarels #define turnon(fd, opt) \
58227535Skarels setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
58327535Skarels if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
58427535Skarels turnon(sep->se_fd, SO_DEBUG) < 0)
58527535Skarels syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
58627535Skarels if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
58727535Skarels syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
58827535Skarels #undef turnon
58946907Sbostic if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
59027535Skarels sizeof (sep->se_ctrladdr)) < 0) {
59157769Sandrew if (debug)
59257769Sandrew fprintf(stderr, "bind failed on %s/%s: %s\n",
59357769Sandrew sep->se_service, sep->se_proto,
59457769Sandrew strerror(errno));
59527535Skarels syslog(LOG_ERR, "%s/%s: bind: %m",
59627535Skarels sep->se_service, sep->se_proto);
59727535Skarels (void) close(sep->se_fd);
59827535Skarels sep->se_fd = -1;
59927535Skarels if (!timingout) {
60027535Skarels timingout = 1;
60127535Skarels alarm(RETRYTIME);
60227535Skarels }
60327535Skarels return;
60427535Skarels }
60527535Skarels if (sep->se_socktype == SOCK_STREAM)
60627535Skarels listen(sep->se_fd, 10);
60727535Skarels FD_SET(sep->se_fd, &allsock);
60827535Skarels nsock++;
60927535Skarels if (sep->se_fd > maxsock)
61027535Skarels maxsock = sep->se_fd;
61157769Sandrew if (debug) {
61257769Sandrew fprintf(stderr, "registered %s on %d\n",
61357769Sandrew sep->se_server, sep->se_fd);
61457769Sandrew }
61527535Skarels }
61627535Skarels
61757769Sandrew /*
61857769Sandrew * Finish with a service and its socket.
61957769Sandrew */
62066696Spendry void
close_sep(sep)62157769Sandrew close_sep(sep)
62266696Spendry struct servtab *sep;
62357769Sandrew {
62457769Sandrew if (sep->se_fd >= 0) {
62557769Sandrew nsock--;
62657769Sandrew FD_CLR(sep->se_fd, &allsock);
62757769Sandrew (void) close(sep->se_fd);
62857769Sandrew sep->se_fd = -1;
62957769Sandrew }
63057769Sandrew sep->se_count = 0;
63157769Sandrew /*
63257769Sandrew * Don't keep the pid of this running deamon: when reapchild()
63357769Sandrew * reaps this pid, it would erroneously increment nsock.
63457769Sandrew */
63557769Sandrew if (sep->se_wait > 1)
63657769Sandrew sep->se_wait = 1;
63757769Sandrew }
63857769Sandrew
63916374Skarels struct servtab *
enter(cp)64016374Skarels enter(cp)
64116374Skarels struct servtab *cp;
64216374Skarels {
64366696Spendry struct servtab *sep;
64432246Sbostic long omask;
64516374Skarels
64616374Skarels sep = (struct servtab *)malloc(sizeof (*sep));
64716374Skarels if (sep == (struct servtab *)0) {
64816510Sralph syslog(LOG_ERR, "Out of memory.");
64916374Skarels exit(-1);
65016374Skarels }
65116374Skarels *sep = *cp;
65216374Skarels sep->se_fd = -1;
65327535Skarels omask = sigblock(SIGBLOCK);
65416374Skarels sep->se_next = servtab;
65516374Skarels servtab = sep;
65616374Skarels sigsetmask(omask);
65716374Skarels return (sep);
65816374Skarels }
65916374Skarels
66016374Skarels FILE *fconfig = NULL;
66116374Skarels struct servtab serv;
66266696Spendry char line[LINE_MAX];
66316374Skarels
66466696Spendry int
setconfig()66516374Skarels setconfig()
66616374Skarels {
66716374Skarels
66816374Skarels if (fconfig != NULL) {
66957769Sandrew fseek(fconfig, 0L, SEEK_SET);
67016374Skarels return (1);
67116374Skarels }
67216374Skarels fconfig = fopen(CONFIG, "r");
67316374Skarels return (fconfig != NULL);
67416374Skarels }
67516374Skarels
67666696Spendry void
endconfig()67716374Skarels endconfig()
67816374Skarels {
67936603Sbostic if (fconfig) {
68036603Sbostic (void) fclose(fconfig);
68136603Sbostic fconfig = NULL;
68236603Sbostic }
68316374Skarels }
68416374Skarels
68516374Skarels struct servtab *
getconfigent()68616374Skarels getconfigent()
68716374Skarels {
68866696Spendry struct servtab *sep = &serv;
68916374Skarels int argc;
69066696Spendry char *cp, *arg;
69157769Sandrew static char TCPMUX_TOKEN[] = "tcpmux/";
69257769Sandrew #define MUX_LEN (sizeof(TCPMUX_TOKEN)-1)
69316374Skarels
69426877Skarels more:
69557769Sandrew while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
69616374Skarels ;
69716374Skarels if (cp == NULL)
69816374Skarels return ((struct servtab *)0);
69957769Sandrew /*
70057769Sandrew * clear the static buffer, since some fields (se_ctrladdr,
70157769Sandrew * for example) don't get initialized here.
70257769Sandrew */
70366696Spendry memset((caddr_t)sep, 0, sizeof *sep);
70416374Skarels arg = skip(&cp);
70557769Sandrew if (cp == NULL) {
70657769Sandrew /* got an empty line containing just blanks/tabs. */
70757769Sandrew goto more;
70857769Sandrew }
70957769Sandrew if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
71057769Sandrew char *c = arg + MUX_LEN;
71157769Sandrew if (*c == '+') {
71257769Sandrew sep->se_type = MUXPLUS_TYPE;
71357769Sandrew c++;
71457769Sandrew } else
71557769Sandrew sep->se_type = MUX_TYPE;
71657769Sandrew sep->se_service = newstr(c);
71757769Sandrew } else {
71857769Sandrew sep->se_service = newstr(arg);
71957769Sandrew sep->se_type = NORM_TYPE;
72057769Sandrew }
72157769Sandrew arg = sskip(&cp);
72216374Skarels if (strcmp(arg, "stream") == 0)
72316374Skarels sep->se_socktype = SOCK_STREAM;
72416374Skarels else if (strcmp(arg, "dgram") == 0)
72516374Skarels sep->se_socktype = SOCK_DGRAM;
72616374Skarels else if (strcmp(arg, "rdm") == 0)
72716374Skarels sep->se_socktype = SOCK_RDM;
72816374Skarels else if (strcmp(arg, "seqpacket") == 0)
72916374Skarels sep->se_socktype = SOCK_SEQPACKET;
73016374Skarels else if (strcmp(arg, "raw") == 0)
73116374Skarels sep->se_socktype = SOCK_RAW;
73216374Skarels else
73316374Skarels sep->se_socktype = -1;
73457769Sandrew sep->se_proto = newstr(sskip(&cp));
73557769Sandrew arg = sskip(&cp);
73616374Skarels sep->se_wait = strcmp(arg, "wait") == 0;
73757769Sandrew if (ISMUX(sep)) {
73857769Sandrew /*
73957769Sandrew * Silently enforce "nowait" for TCPMUX services since
74057769Sandrew * they don't have an assigned port to listen on.
74157769Sandrew */
74257769Sandrew sep->se_wait = 0;
74357769Sandrew
74457769Sandrew if (strcmp(sep->se_proto, "tcp")) {
74557769Sandrew syslog(LOG_ERR,
74657769Sandrew "%s: bad protocol for tcpmux service %s",
74757769Sandrew CONFIG, sep->se_service);
74857769Sandrew goto more;
74957769Sandrew }
75057769Sandrew if (sep->se_socktype != SOCK_STREAM) {
75157769Sandrew syslog(LOG_ERR,
75257769Sandrew "%s: bad socket type for tcpmux service %s",
75357769Sandrew CONFIG, sep->se_service);
75457769Sandrew goto more;
75557769Sandrew }
75657769Sandrew }
75757769Sandrew sep->se_user = newstr(sskip(&cp));
75857769Sandrew sep->se_server = newstr(sskip(&cp));
75926877Skarels if (strcmp(sep->se_server, "internal") == 0) {
76066696Spendry struct biltin *bi;
76126877Skarels
76226877Skarels for (bi = biltins; bi->bi_service; bi++)
76326877Skarels if (bi->bi_socktype == sep->se_socktype &&
76426877Skarels strcmp(bi->bi_service, sep->se_service) == 0)
76526877Skarels break;
76626877Skarels if (bi->bi_service == 0) {
76757769Sandrew syslog(LOG_ERR, "internal service %s unknown",
76826877Skarels sep->se_service);
76926877Skarels goto more;
77026877Skarels }
77126877Skarels sep->se_bi = bi;
77226877Skarels sep->se_wait = bi->bi_wait;
77329794Skarels } else
77429794Skarels sep->se_bi = NULL;
77516374Skarels argc = 0;
77616374Skarels for (arg = skip(&cp); cp; arg = skip(&cp))
77716374Skarels if (argc < MAXARGV)
77846907Sbostic sep->se_argv[argc++] = newstr(arg);
77916374Skarels while (argc <= MAXARGV)
78016374Skarels sep->se_argv[argc++] = NULL;
78116374Skarels return (sep);
78216374Skarels }
78316374Skarels
78466696Spendry void
freeconfig(cp)78516374Skarels freeconfig(cp)
78666696Spendry struct servtab *cp;
78716374Skarels {
78816374Skarels int i;
78916374Skarels
79016374Skarels if (cp->se_service)
79116374Skarels free(cp->se_service);
79216374Skarels if (cp->se_proto)
79316374Skarels free(cp->se_proto);
79426921Slepreau if (cp->se_user)
79526921Slepreau free(cp->se_user);
79616374Skarels if (cp->se_server)
79716374Skarels free(cp->se_server);
79816374Skarels for (i = 0; i < MAXARGV; i++)
79916374Skarels if (cp->se_argv[i])
80016374Skarels free(cp->se_argv[i]);
80116374Skarels }
80216374Skarels
80357769Sandrew
80457769Sandrew /*
80557769Sandrew * Safe skip - if skip returns null, log a syntax error in the
80657769Sandrew * configuration file and exit.
80757769Sandrew */
80816374Skarels char *
sskip(cpp)80957769Sandrew sskip(cpp)
81057769Sandrew char **cpp;
81157769Sandrew {
81266696Spendry char *cp;
81357769Sandrew
81457769Sandrew cp = skip(cpp);
81557769Sandrew if (cp == NULL) {
81657769Sandrew syslog(LOG_ERR, "%s: syntax error", CONFIG);
81757769Sandrew exit(-1);
81857769Sandrew }
81957769Sandrew return (cp);
82057769Sandrew }
82157769Sandrew
82257769Sandrew char *
skip(cpp)82316374Skarels skip(cpp)
82416374Skarels char **cpp;
82516374Skarels {
82666696Spendry char *cp = *cpp;
82716374Skarels char *start;
82816374Skarels
82916374Skarels again:
83016374Skarels while (*cp == ' ' || *cp == '\t')
83116374Skarels cp++;
83216374Skarels if (*cp == '\0') {
83343455Sbostic int c;
83416374Skarels
83516374Skarels c = getc(fconfig);
83636603Sbostic (void) ungetc(c, fconfig);
83716374Skarels if (c == ' ' || c == '\t')
83816374Skarels if (cp = nextline(fconfig))
83916374Skarels goto again;
84016374Skarels *cpp = (char *)0;
84116374Skarels return ((char *)0);
84216374Skarels }
84316374Skarels start = cp;
84416374Skarels while (*cp && *cp != ' ' && *cp != '\t')
84516374Skarels cp++;
84616374Skarels if (*cp != '\0')
84716374Skarels *cp++ = '\0';
84816374Skarels *cpp = cp;
84916374Skarels return (start);
85016374Skarels }
85116374Skarels
85216374Skarels char *
nextline(fd)85316374Skarels nextline(fd)
85416374Skarels FILE *fd;
85516374Skarels {
85616374Skarels char *cp;
85716374Skarels
85826921Slepreau if (fgets(line, sizeof (line), fd) == NULL)
85916374Skarels return ((char *)0);
86066696Spendry cp = strchr(line, '\n');
86116374Skarels if (cp)
86216374Skarels *cp = '\0';
86316374Skarels return (line);
86416374Skarels }
86516374Skarels
86616374Skarels char *
newstr(cp)86746907Sbostic newstr(cp)
86816374Skarels char *cp;
86916374Skarels {
87046973Skarels if (cp = strdup(cp ? cp : ""))
87166696Spendry return (cp);
87246973Skarels syslog(LOG_ERR, "strdup: %m");
87346907Sbostic exit(-1);
87416374Skarels }
87526877Skarels
87666696Spendry void
setproctitle(a,s)87726877Skarels setproctitle(a, s)
87826877Skarels char *a;
87926877Skarels int s;
88026877Skarels {
88126877Skarels int size;
88266696Spendry char *cp;
88326877Skarels struct sockaddr_in sin;
88426877Skarels char buf[80];
88526877Skarels
88626877Skarels cp = Argv[0];
88726877Skarels size = sizeof(sin);
88846907Sbostic if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
88932442Sbostic (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
89026877Skarels else
89132442Sbostic (void) sprintf(buf, "-%s", a);
89226877Skarels strncpy(cp, buf, LastArg - cp);
89326877Skarels cp += strlen(cp);
89426877Skarels while (cp < LastArg)
89526877Skarels *cp++ = ' ';
89626877Skarels }
89726877Skarels
89826877Skarels /*
89926877Skarels * Internet services provided internally by inetd:
90026877Skarels */
90149986Skarels #define BUFSIZE 8192
90226877Skarels
90326877Skarels /* ARGSUSED */
90466696Spendry void
echo_stream(s,sep)90526877Skarels echo_stream(s, sep) /* Echo service -- echo data back */
90626877Skarels int s;
90726877Skarels struct servtab *sep;
90826877Skarels {
90938573Skarels char buffer[BUFSIZE];
91026877Skarels int i;
91126877Skarels
91232091Skarels setproctitle(sep->se_service, s);
91326877Skarels while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
91426877Skarels write(s, buffer, i) > 0)
91526877Skarels ;
91626877Skarels exit(0);
91726877Skarels }
91826877Skarels
91926877Skarels /* ARGSUSED */
92066696Spendry void
echo_dg(s,sep)92126877Skarels echo_dg(s, sep) /* Echo service -- echo data back */
92226877Skarels int s;
92326877Skarels struct servtab *sep;
92426877Skarels {
92538573Skarels char buffer[BUFSIZE];
92626877Skarels int i, size;
92726877Skarels struct sockaddr sa;
92826877Skarels
92926877Skarels size = sizeof(sa);
93026877Skarels if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
93126877Skarels return;
93226877Skarels (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
93326877Skarels }
93426877Skarels
93526877Skarels /* ARGSUSED */
93666696Spendry void
discard_stream(s,sep)93726877Skarels discard_stream(s, sep) /* Discard service -- ignore data */
93826877Skarels int s;
93926877Skarels struct servtab *sep;
94026877Skarels {
94149986Skarels int ret;
94238573Skarels char buffer[BUFSIZE];
94326877Skarels
94432091Skarels setproctitle(sep->se_service, s);
94526877Skarels while (1) {
94649986Skarels while ((ret = read(s, buffer, sizeof(buffer))) > 0)
94726877Skarels ;
94849986Skarels if (ret == 0 || errno != EINTR)
94926877Skarels break;
95026877Skarels }
95126877Skarels exit(0);
95226877Skarels }
95326877Skarels
95426877Skarels /* ARGSUSED */
95566696Spendry void
discard_dg(s,sep)95626877Skarels discard_dg(s, sep) /* Discard service -- ignore data */
95726877Skarels int s;
95826877Skarels struct servtab *sep;
95926877Skarels {
96038573Skarels char buffer[BUFSIZE];
96126877Skarels
96226877Skarels (void) read(s, buffer, sizeof(buffer));
96326877Skarels }
96426877Skarels
96526877Skarels #include <ctype.h>
96626877Skarels #define LINESIZ 72
96726877Skarels char ring[128];
96826877Skarels char *endring;
96926877Skarels
97066696Spendry void
initring()97126877Skarels initring()
97226877Skarels {
97366696Spendry int i;
97426877Skarels
97526877Skarels endring = ring;
97626877Skarels
97726877Skarels for (i = 0; i <= 128; ++i)
97826877Skarels if (isprint(i))
97926877Skarels *endring++ = i;
98026877Skarels }
98126877Skarels
98226877Skarels /* ARGSUSED */
98366696Spendry void
chargen_stream(s,sep)98426877Skarels chargen_stream(s, sep) /* Character generator */
98526877Skarels int s;
98626877Skarels struct servtab *sep;
98726877Skarels {
98832246Sbostic int len;
98966696Spendry char *rs, text[LINESIZ+2];
99026877Skarels
99132091Skarels setproctitle(sep->se_service, s);
99232246Sbostic
99332246Sbostic if (!endring) {
99426877Skarels initring();
99532246Sbostic rs = ring;
99632246Sbostic }
99726877Skarels
99832246Sbostic text[LINESIZ] = '\r';
99932246Sbostic text[LINESIZ + 1] = '\n';
100032246Sbostic for (rs = ring;;) {
100132246Sbostic if ((len = endring - rs) >= LINESIZ)
100266696Spendry memmove(text, rs, LINESIZ);
100332246Sbostic else {
100466696Spendry memmove(text, rs, len);
100566696Spendry memmove(text + len, ring, LINESIZ - len);
100632246Sbostic }
100732246Sbostic if (++rs == endring)
100826877Skarels rs = ring;
100932246Sbostic if (write(s, text, sizeof(text)) != sizeof(text))
101026877Skarels break;
101126877Skarels }
101226877Skarels exit(0);
101326877Skarels }
101426877Skarels
101526877Skarels /* ARGSUSED */
101666696Spendry void
chargen_dg(s,sep)101726877Skarels chargen_dg(s, sep) /* Character generator */
101826877Skarels int s;
101926877Skarels struct servtab *sep;
102026877Skarels {
102132246Sbostic struct sockaddr sa;
102232246Sbostic static char *rs;
102332246Sbostic int len, size;
102426877Skarels char text[LINESIZ+2];
102526877Skarels
102632246Sbostic if (endring == 0) {
102726877Skarels initring();
102832246Sbostic rs = ring;
102932246Sbostic }
103026877Skarels
103126877Skarels size = sizeof(sa);
103226877Skarels if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
103326877Skarels return;
103432246Sbostic
103532246Sbostic if ((len = endring - rs) >= LINESIZ)
103666696Spendry memmove(text, rs, LINESIZ);
103732246Sbostic else {
103866696Spendry memmove(text, rs, len);
103966696Spendry memmove(text + len, ring, LINESIZ - len);
104032246Sbostic }
104132246Sbostic if (++rs == endring)
104226877Skarels rs = ring;
104332246Sbostic text[LINESIZ] = '\r';
104432246Sbostic text[LINESIZ + 1] = '\n';
104526877Skarels (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
104626877Skarels }
104726877Skarels
104826877Skarels /*
104926877Skarels * Return a machine readable date and time, in the form of the
105026877Skarels * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
105126877Skarels * returns the number of seconds since midnight, Jan 1, 1970,
105226877Skarels * we must add 2208988800 seconds to this figure to make up for
105326877Skarels * some seventy years Bell Labs was asleep.
105426877Skarels */
105526877Skarels
105626877Skarels long
machtime()105726877Skarels machtime()
105826877Skarels {
105926877Skarels struct timeval tv;
106026877Skarels
106126921Slepreau if (gettimeofday(&tv, (struct timezone *)0) < 0) {
106257769Sandrew if (debug)
106357769Sandrew fprintf(stderr, "Unable to get time of day\n");
106426921Slepreau return (0L);
106526877Skarels }
106660073Storek #define OFFSET ((u_long)25567 * 24*60*60)
106760073Storek return (htonl((long)(tv.tv_sec + OFFSET)));
106860073Storek #undef OFFSET
106926877Skarels }
107026877Skarels
107126877Skarels /* ARGSUSED */
107266696Spendry void
machtime_stream(s,sep)107326877Skarels machtime_stream(s, sep)
107426877Skarels int s;
107526877Skarels struct servtab *sep;
107626877Skarels {
107726877Skarels long result;
107826877Skarels
107926877Skarels result = machtime();
108026877Skarels (void) write(s, (char *) &result, sizeof(result));
108126877Skarels }
108226877Skarels
108326877Skarels /* ARGSUSED */
108466696Spendry void
machtime_dg(s,sep)108526877Skarels machtime_dg(s, sep)
108626877Skarels int s;
108726877Skarels struct servtab *sep;
108826877Skarels {
108926877Skarels long result;
109026877Skarels struct sockaddr sa;
109126877Skarels int size;
109226877Skarels
109326877Skarels size = sizeof(sa);
109426921Slepreau if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
109526877Skarels return;
109626877Skarels result = machtime();
109726877Skarels (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
109826877Skarels }
109926877Skarels
110026877Skarels /* ARGSUSED */
110166696Spendry void
daytime_stream(s,sep)110226877Skarels daytime_stream(s, sep) /* Return human-readable time of day */
110326877Skarels int s;
110426877Skarels struct servtab *sep;
110526877Skarels {
110626877Skarels char buffer[256];
110757769Sandrew time_t clock;
110826877Skarels
110926877Skarels clock = time((time_t *) 0);
111026877Skarels
111132442Sbostic (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
111226921Slepreau (void) write(s, buffer, strlen(buffer));
111326877Skarels }
111426877Skarels
111526877Skarels /* ARGSUSED */
111666696Spendry void
daytime_dg(s,sep)111726877Skarels daytime_dg(s, sep) /* Return human-readable time of day */
111826877Skarels int s;
111926877Skarels struct servtab *sep;
112026877Skarels {
112126877Skarels char buffer[256];
112257769Sandrew time_t clock;
112326877Skarels struct sockaddr sa;
112426877Skarels int size;
112526877Skarels
112626877Skarels clock = time((time_t *) 0);
112726877Skarels
112826877Skarels size = sizeof(sa);
112926877Skarels if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
113026877Skarels return;
113132442Sbostic (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
113226877Skarels (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
113326877Skarels }
113429794Skarels
113529794Skarels /*
113629794Skarels * print_service:
113729794Skarels * Dump relevant information to stderr
113829794Skarels */
113966696Spendry void
print_service(action,sep)114029794Skarels print_service(action, sep)
114129794Skarels char *action;
114229794Skarels struct servtab *sep;
114329794Skarels {
114429794Skarels fprintf(stderr,
114529794Skarels "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
114629794Skarels action, sep->se_service, sep->se_proto,
114736603Sbostic sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server);
114829794Skarels }
114957769Sandrew
115057769Sandrew /*
115157769Sandrew * Based on TCPMUX.C by Mark K. Lottor November 1988
115257769Sandrew * sri-nic::ps:<mkl>tcpmux.c
115357769Sandrew */
115457769Sandrew
115557769Sandrew
115657769Sandrew static int /* # of characters upto \r,\n or \0 */
getline(fd,buf,len)115757769Sandrew getline(fd, buf, len)
115857769Sandrew int fd;
115957769Sandrew char *buf;
116057769Sandrew int len;
116157769Sandrew {
116257769Sandrew int count = 0, n;
116357769Sandrew
116457769Sandrew do {
116557769Sandrew n = read(fd, buf, len-count);
116657769Sandrew if (n == 0)
116766696Spendry return (count);
116857769Sandrew if (n < 0)
116957769Sandrew return (-1);
117057769Sandrew while (--n >= 0) {
117157769Sandrew if (*buf == '\r' || *buf == '\n' || *buf == '\0')
117266696Spendry return (count);
117357769Sandrew count++;
117457769Sandrew buf++;
117557769Sandrew }
117657769Sandrew } while (count < len);
117757769Sandrew return (count);
117857769Sandrew }
117957769Sandrew
118057769Sandrew #define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */
118157769Sandrew
118257769Sandrew #define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1)
118357769Sandrew
118457769Sandrew struct servtab *
tcpmux(s)118557769Sandrew tcpmux(s)
118657769Sandrew int s;
118757769Sandrew {
118866696Spendry struct servtab *sep;
118957769Sandrew char service[MAX_SERV_LEN+1];
119057769Sandrew int len;
119157769Sandrew
119257769Sandrew /* Get requested service name */
119357769Sandrew if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
119466696Spendry strwrite(s, "-Error reading service name\r\n");
119566696Spendry return (NULL);
119657769Sandrew }
119757769Sandrew service[len] = '\0';
119857769Sandrew
119957769Sandrew if (debug)
120066696Spendry fprintf(stderr, "tcpmux: someone wants %s\n", service);
120157769Sandrew
120257769Sandrew /*
120357769Sandrew * Help is a required command, and lists available services,
120457769Sandrew * one per line.
120557769Sandrew */
120666696Spendry if (!strcasecmp(service, "help")) {
120766696Spendry for (sep = servtab; sep; sep = sep->se_next) {
120866696Spendry if (!ISMUX(sep))
120966696Spendry continue;
121066696Spendry (void)write(s,sep->se_service,strlen(sep->se_service));
121166696Spendry strwrite(s, "\r\n");
121266696Spendry }
121366696Spendry return (NULL);
121457769Sandrew }
121557769Sandrew
121657769Sandrew /* Try matching a service in inetd.conf with the request */
121757769Sandrew for (sep = servtab; sep; sep = sep->se_next) {
121866696Spendry if (!ISMUX(sep))
121966696Spendry continue;
122066696Spendry if (!strcasecmp(service, sep->se_service)) {
122166696Spendry if (ISMUXPLUS(sep)) {
122266696Spendry strwrite(s, "+Go\r\n");
122366696Spendry }
122466696Spendry return (sep);
122557769Sandrew }
122657769Sandrew }
122757769Sandrew strwrite(s, "-Service not available\r\n");
122866696Spendry return (NULL);
122957769Sandrew }
1230