date and time created 93/08/07 09:11:55 by eric
[unix-history] / usr / src / usr.sbin / inetd / inetd.c
CommitLineData
528b0614 1/*
f4b24d5f
KB
2 * Copyright (c) 1983, 1991, 1993
3 * The Regents of the University of California. All rights reserved.
9166e6a5 4 *
32ce521f 5 * %sccs.include.redist.c%
528b0614
DF
6 */
7
8#ifndef lint
f4b24d5f
KB
9static char copyright[] =
10"@(#) Copyright (c) 1983, 1991, 1993\n\
11 The Regents of the University of California. All rights reserved.\n";
9166e6a5 12#endif /* not lint */
528b0614 13
21ed1185 14#ifndef lint
f4b24d5f 15static char sccsid[] = "@(#)inetd.c 8.1 (Berkeley) %G%";
9166e6a5 16#endif /* not lint */
21ed1185
MK
17
18/*
19 * Inetd - Internet super-server
20 *
6e9dfe5b
AC
21 * This program invokes all internet services as needed. Connection-oriented
22 * services are invoked each time a connection is made, by creating a process.
23 * This process is passed the connection as file descriptor 0 and is expected
24 * to do a getpeername to find out the source host and port.
21ed1185
MK
25 *
26 * Datagram oriented services are invoked when a datagram
27 * arrives; a process is created and passed a pending message
28 * on file descriptor 0. Datagram servers may either connect
29 * to their peer, freeing up the original socket for inetd
30 * to receive further messages on, or ``take over the socket'',
31 * processing all arriving datagrams and, eventually, timing
32 * out. The first type of server is said to be ``multi-threaded'';
33 * the second type of server ``single-threaded''.
34 *
35 * Inetd uses a configuration file which is read at startup
36 * and, possibly, at some later time in response to a hangup signal.
37 * The configuration file is ``free format'' with fields given in the
38 * order shown below. Continuation lines for an entry must being with
39 * a space or tab. All fields must be present in each entry.
40 *
6e9dfe5b
AC
41 * service name must be in /etc/services or must
42 * name a tcpmux service
21ed1185
MK
43 * socket type stream/dgram/raw/rdm/seqpacket
44 * protocol must be in /etc/protocols
45 * wait/nowait single-threaded/multi-threaded
7d5eb6c4 46 * user user to run daemon as
21ed1185 47 * server program full path name
cc4df4a4 48 * server program arguments maximum of MAXARGS (20)
21ed1185 49 *
6e9dfe5b
AC
50 * TCP services without official port numbers are handled with the
51 * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
52 * requests. When a connection is made from a foreign host, the service
53 * requested is passed to tcpmux, which looks it up in the servtab list
54 * and returns the proper entry for the service. Tcpmux returns a
55 * negative reply if the service doesn't exist, otherwise the invoked
56 * server is expected to return the positive reply if the service type in
57 * inetd.conf file has the prefix "tcpmux/". If the service type has the
58 * prefix "tcpmux/+", tcpmux will return the positive reply for the
59 * process; this is for compatibility with older server code, and also
60 * allows you to invoke programs that use stdin/stdout without putting any
61 * special server code in them. Services that use tcpmux are "nowait"
62 * because they do not have a well-known port and hence cannot listen
63 * for new requests.
64 *
21ed1185
MK
65 * Comment lines are indicated by a `#' in column 1.
66 */
67#include <sys/param.h>
68#include <sys/stat.h>
69#include <sys/ioctl.h>
70#include <sys/socket.h>
21ed1185 71#include <sys/wait.h>
2a6b82aa
JL
72#include <sys/time.h>
73#include <sys/resource.h>
21ed1185
MK
74
75#include <netinet/in.h>
76#include <arpa/inet.h>
77
78#include <errno.h>
6e9dfe5b 79#include <fcntl.h>
21ed1185 80#include <netdb.h>
7d5eb6c4 81#include <pwd.h>
6e9dfe5b 82#include <signal.h>
567752b5 83#include <stdio.h>
6e9dfe5b 84#include <stdlib.h>
38dde0cd 85#include <string.h>
6e9dfe5b
AC
86#include <syslog.h>
87#include <unistd.h>
88
bd070810 89#include "pathnames.h"
21ed1185 90
fc99bd8d
MK
91#define TOOMANY 40 /* don't start more than TOOMANY */
92#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
93#define RETRYTIME (60*10) /* retry after bind or server fail */
94
95#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
96
21ed1185 97
664e9651 98void config(), reapchild(), retry();
21ed1185
MK
99
100int debug = 0;
5859fc43
MK
101int nsock, maxsock;
102fd_set allsock;
21ed1185 103int options;
fc99bd8d 104int timingout;
6e9dfe5b 105int toomany = TOOMANY;
21ed1185
MK
106struct servent *sp;
107
108struct servtab {
109 char *se_service; /* name of service */
110 int se_socktype; /* type of socket to use */
111 char *se_proto; /* protocol used */
112 short se_wait; /* single threaded server */
113 short se_checked; /* looked at during merge */
7d5eb6c4 114 char *se_user; /* user name to run as */
b1b30605 115 struct biltin *se_bi; /* if built-in, description */
21ed1185 116 char *se_server; /* server program */
cc4df4a4 117#define MAXARGV 20
21ed1185
MK
118 char *se_argv[MAXARGV+1]; /* program arguments */
119 int se_fd; /* open descriptor */
6e9dfe5b 120 int se_type; /* type */
21ed1185 121 struct sockaddr_in se_ctrladdr;/* bound address */
fc99bd8d
MK
122 int se_count; /* number started since se_time */
123 struct timeval se_time; /* start of se_count */
21ed1185
MK
124 struct servtab *se_next;
125} *servtab;
126
6e9dfe5b
AC
127#define NORM_TYPE 0
128#define MUX_TYPE 1
129#define MUXPLUS_TYPE 2
130#define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \
131 ((sep)->se_type == MUXPLUS_TYPE))
132#define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE)
133
b1b30605
MK
134int echo_stream(), discard_stream(), machtime_stream();
135int daytime_stream(), chargen_stream();
136int echo_dg(), discard_dg(), machtime_dg(), daytime_dg(), chargen_dg();
6e9dfe5b 137struct servtab *tcpmux();
b1b30605
MK
138
139struct biltin {
140 char *bi_service; /* internally provided service name */
141 int bi_socktype; /* type of socket supported */
142 short bi_fork; /* 1 if should fork before call */
143 short bi_wait; /* 1 if should wait for child */
144 int (*bi_fn)(); /* function which performs it */
145} biltins[] = {
146 /* Echo received data */
147 "echo", SOCK_STREAM, 1, 0, echo_stream,
148 "echo", SOCK_DGRAM, 0, 0, echo_dg,
149
150 /* Internet /dev/null */
151 "discard", SOCK_STREAM, 1, 0, discard_stream,
152 "discard", SOCK_DGRAM, 0, 0, discard_dg,
153
154 /* Return 32 bit time since 1970 */
155 "time", SOCK_STREAM, 0, 0, machtime_stream,
156 "time", SOCK_DGRAM, 0, 0, machtime_dg,
157
158 /* Return human-readable time */
159 "daytime", SOCK_STREAM, 0, 0, daytime_stream,
160 "daytime", SOCK_DGRAM, 0, 0, daytime_dg,
161
162 /* Familiar character generator */
163 "chargen", SOCK_STREAM, 1, 0, chargen_stream,
164 "chargen", SOCK_DGRAM, 0, 0, chargen_dg,
6e9dfe5b
AC
165
166 "tcpmux", SOCK_STREAM, 1, 0, (int (*)())tcpmux,
167
168 NULL
b1b30605
MK
169};
170
171#define NUMINT (sizeof(intab) / sizeof(struct inent))
bd070810 172char *CONFIG = _PATH_INETDCONF;
b1b30605
MK
173char **Argv;
174char *LastArg;
21ed1185 175
b1b30605 176main(argc, argv, envp)
21ed1185 177 int argc;
b1b30605 178 char *argv[], *envp[];
21ed1185 179{
21ed1185 180 register struct servtab *sep;
7d5eb6c4 181 register struct passwd *pwd;
567752b5 182 register int tmpint;
fc99bd8d 183 struct sigvec sv;
567752b5
KB
184 int ch, pid, dofork;
185 char buf[50];
21ed1185 186
b1b30605
MK
187 Argv = argv;
188 if (envp == 0 || *envp == 0)
189 envp = argv;
190 while (*envp)
191 envp++;
192 LastArg = envp[-1] + strlen(envp[-1]);
21ed1185 193
6e9dfe5b
AC
194 openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
195
196 while ((ch = getopt(argc, argv, "dR:")) != EOF)
567752b5 197 switch(ch) {
21ed1185
MK
198 case 'd':
199 debug = 1;
200 options |= SO_DEBUG;
201 break;
6e9dfe5b
AC
202 case 'R': { /* invocation rate */
203 char *p;
204
205 tmpint = strtol(optarg, &p, 0);
206 if (tmpint < 1 || *p)
207 syslog(LOG_ERR,
208 "-R %s: bad value for service invocation rate",
209 optarg);
210 else
211 toomany = tmpint;
212 break;
213 }
567752b5 214 case '?':
21ed1185 215 default:
6e9dfe5b
AC
216 syslog(LOG_ERR,
217 "usage: inetd [-d] [-R rate] [conf-file]");
567752b5 218 exit(1);
21ed1185 219 }
567752b5
KB
220 argc -= optind;
221 argv += optind;
222
21ed1185
MK
223 if (argc > 0)
224 CONFIG = argv[0];
6e9dfe5b 225 if (debug == 0) {
995785db 226 daemon(0, 0);
6e9dfe5b
AC
227 }
228 bzero(&sv, sizeof(sv));
fc99bd8d
MK
229 sv.sv_mask = SIGBLOCK;
230 sv.sv_handler = retry;
231 sigvec(SIGALRM, &sv, (struct sigvec *)0);
21ed1185 232 config();
fc99bd8d
MK
233 sv.sv_handler = config;
234 sigvec(SIGHUP, &sv, (struct sigvec *)0);
235 sv.sv_handler = reapchild;
236 sigvec(SIGCHLD, &sv, (struct sigvec *)0);
237
567752b5
KB
238 {
239 /* space for daemons to overwrite environment for ps */
240#define DUMMYSIZE 100
241 char dummy[DUMMYSIZE];
242
243 (void)memset(dummy, 'x', sizeof(DUMMYSIZE) - 1);
244 dummy[DUMMYSIZE - 1] = '\0';
245 (void)setenv("inetd_dummy", dummy, 1);
246 }
247
21ed1185 248 for (;;) {
567752b5 249 int n, ctrl;
fc99bd8d
MK
250 fd_set readable;
251
0ee95e67
MK
252 if (nsock == 0) {
253 (void) sigblock(SIGBLOCK);
254 while (nsock == 0)
92732149
KB
255 sigpause(0L);
256 (void) sigsetmask(0L);
0ee95e67 257 }
fc99bd8d
MK
258 readable = allsock;
259 if ((n = select(maxsock + 1, &readable, (fd_set *)0,
260 (fd_set *)0, (struct timeval *)0)) <= 0) {
261 if (n < 0 && errno != EINTR)
6e9dfe5b 262 syslog(LOG_WARNING, "select: %m");
fc99bd8d
MK
263 sleep(1);
264 continue;
265 }
266 for (sep = servtab; n && sep; sep = sep->se_next)
093d40e3
MK
267 if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
268 n--;
269 if (debug)
270 fprintf(stderr, "someone wants %s\n",
271 sep->se_service);
272 if (sep->se_socktype == SOCK_STREAM) {
273 ctrl = accept(sep->se_fd, (struct sockaddr *)0,
274 (int *)0);
275 if (debug)
276 fprintf(stderr, "accept, ctrl %d\n", ctrl);
277 if (ctrl < 0) {
6e9dfe5b
AC
278 if (errno != EINTR)
279 syslog(LOG_WARNING,
280 "accept (for %s): %m",
281 sep->se_service);
093d40e3
MK
282 continue;
283 }
6e9dfe5b
AC
284 /*
285 * Call tcpmux to find the real service to exec.
286 */
287 if (sep->se_bi &&
288 sep->se_bi->bi_fn == (int (*)()) tcpmux) {
289 sep = tcpmux(ctrl);
290 if (sep == NULL) {
291 close(ctrl);
292 continue;
293 }
294 }
093d40e3
MK
295 } else
296 ctrl = sep->se_fd;
297 (void) sigblock(SIGBLOCK);
298 pid = 0;
299 dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
300 if (dofork) {
301 if (sep->se_count++ == 0)
302 (void)gettimeofday(&sep->se_time,
303 (struct timezone *)0);
6e9dfe5b 304 else if (sep->se_count >= toomany) {
fc99bd8d
MK
305 struct timeval now;
306
307 (void)gettimeofday(&now, (struct timezone *)0);
308 if (now.tv_sec - sep->se_time.tv_sec >
309 CNT_INTVL) {
310 sep->se_time = now;
311 sep->se_count = 1;
312 } else {
313 syslog(LOG_ERR,
6e9dfe5b 314 "%s/%s server failing (looping), service terminated",
fc99bd8d 315 sep->se_service, sep->se_proto);
6e9dfe5b
AC
316 close_sep(sep);
317 sigsetmask(0L);
fc99bd8d
MK
318 if (!timingout) {
319 timingout = 1;
320 alarm(RETRYTIME);
321 }
6e9dfe5b 322 continue;
fc99bd8d 323 }
093d40e3
MK
324 }
325 pid = fork();
326 }
327 if (pid < 0) {
328 syslog(LOG_ERR, "fork: %m");
329 if (sep->se_socktype == SOCK_STREAM)
330 close(ctrl);
331 sigsetmask(0L);
332 sleep(1);
333 continue;
334 }
335 if (pid && sep->se_wait) {
336 sep->se_wait = pid;
514e253c
KB
337 if (sep->se_fd >= 0) {
338 FD_CLR(sep->se_fd, &allsock);
339 nsock--;
340 }
093d40e3
MK
341 }
342 sigsetmask(0L);
343 if (pid == 0) {
344 if (debug && dofork)
995785db 345 setsid();
6e9dfe5b
AC
346 if (dofork) {
347 if (debug)
348 fprintf(stderr, "+ Closing from %d\n",
349 maxsock);
350 for (tmpint = maxsock; tmpint > 2; tmpint--)
567752b5
KB
351 if (tmpint != ctrl)
352 close(tmpint);
6e9dfe5b 353 }
093d40e3 354 if (sep->se_bi)
b1b30605 355 (*sep->se_bi->bi_fn)(ctrl, sep);
093d40e3
MK
356 else {
357 if (debug)
358 fprintf(stderr, "%d execl %s\n",
359 getpid(), sep->se_server);
b1b30605
MK
360 dup2(ctrl, 0);
361 close(ctrl);
362 dup2(0, 1);
363 dup2(0, 2);
364 if ((pwd = getpwnam(sep->se_user)) == NULL) {
365 syslog(LOG_ERR,
6e9dfe5b
AC
366 "%s/%s: %s: No such user",
367 sep->se_service, sep->se_proto,
368 sep->se_user);
fc99bd8d
MK
369 if (sep->se_socktype != SOCK_STREAM)
370 recv(0, buf, sizeof (buf), 0);
b1b30605
MK
371 _exit(1);
372 }
373 if (pwd->pw_uid) {
6e9dfe5b
AC
374 if (setgid(pwd->pw_gid) < 0) {
375 syslog(LOG_ERR,
376 "%s: can't set gid %d: %m",
377 sep->se_service, pwd->pw_gid);
378 _exit(1);
379 }
380 (void) initgroups(pwd->pw_name,
381 pwd->pw_gid);
382 if (setuid(pwd->pw_uid) < 0) {
383 syslog(LOG_ERR,
384 "%s: can't set uid %d: %m",
385 sep->se_service, pwd->pw_uid);
386 _exit(1);
387 }
b1b30605 388 }
b1b30605
MK
389 execv(sep->se_server, sep->se_argv);
390 if (sep->se_socktype != SOCK_STREAM)
391 recv(0, buf, sizeof (buf), 0);
6e9dfe5b
AC
392 syslog(LOG_ERR,
393 "cannot execute %s: %m", sep->se_server);
b1b30605 394 _exit(1);
093d40e3
MK
395 }
396 }
397 if (sep->se_socktype == SOCK_STREAM)
398 close(ctrl);
21ed1185 399 }
21ed1185
MK
400 }
401}
402
664e9651 403void
21ed1185
MK
404reapchild()
405{
093d40e3 406 int status;
21ed1185
MK
407 int pid;
408 register struct servtab *sep;
409
410 for (;;) {
093d40e3 411 pid = wait3(&status, WNOHANG, (struct rusage *)0);
21ed1185
MK
412 if (pid <= 0)
413 break;
414 if (debug)
6e9dfe5b
AC
415 fprintf(stderr, "%d reaped, status %#x\n",
416 pid, status);
21ed1185
MK
417 for (sep = servtab; sep; sep = sep->se_next)
418 if (sep->se_wait == pid) {
093d40e3 419 if (status)
6a3125d9
RC
420 syslog(LOG_WARNING,
421 "%s: exit status 0x%x",
21ed1185
MK
422 sep->se_server, status);
423 if (debug)
424 fprintf(stderr, "restored %s, fd %d\n",
425 sep->se_service, sep->se_fd);
5859fc43
MK
426 FD_SET(sep->se_fd, &allsock);
427 nsock++;
21ed1185
MK
428 sep->se_wait = 1;
429 }
430 }
431}
432
664e9651 433void
21ed1185
MK
434config()
435{
436 register struct servtab *sep, *cp, **sepp;
437 struct servtab *getconfigent(), *enter();
6e9dfe5b 438 struct passwd *pwd;
92732149 439 long omask;
21ed1185
MK
440
441 if (!setconfig()) {
6a3125d9 442 syslog(LOG_ERR, "%s: %m", CONFIG);
21ed1185
MK
443 return;
444 }
445 for (sep = servtab; sep; sep = sep->se_next)
446 sep->se_checked = 0;
447 while (cp = getconfigent()) {
6e9dfe5b
AC
448 if ((pwd = getpwnam(cp->se_user)) == NULL) {
449 syslog(LOG_ERR,
450 "%s/%s: No such user '%s', service ignored",
451 cp->se_service, cp->se_proto, cp->se_user);
452 continue;
453 }
21ed1185
MK
454 for (sep = servtab; sep; sep = sep->se_next)
455 if (strcmp(sep->se_service, cp->se_service) == 0 &&
456 strcmp(sep->se_proto, cp->se_proto) == 0)
457 break;
458 if (sep != 0) {
459 int i;
460
fc99bd8d 461 omask = sigblock(SIGBLOCK);
90aae428
KB
462 /*
463 * sep->se_wait may be holding the pid of a daemon
464 * that we're waiting for. If so, don't overwrite
465 * it unless the config file explicitly says don't
466 * wait.
467 */
468 if (cp->se_bi == 0 &&
469 (sep->se_wait == 1 || cp->se_wait == 0))
fc99bd8d 470 sep->se_wait = cp->se_wait;
21ed1185 471#define SWAP(a, b) { char *c = a; a = b; b = c; }
2a6b82aa
JL
472 if (cp->se_user)
473 SWAP(sep->se_user, cp->se_user);
21ed1185
MK
474 if (cp->se_server)
475 SWAP(sep->se_server, cp->se_server);
476 for (i = 0; i < MAXARGV; i++)
477 SWAP(sep->se_argv[i], cp->se_argv[i]);
478 sigsetmask(omask);
479 freeconfig(cp);
9a0fbd5b
MK
480 if (debug)
481 print_service("REDO", sep);
482 } else {
21ed1185 483 sep = enter(cp);
9a0fbd5b
MK
484 if (debug)
485 print_service("ADD ", sep);
486 }
21ed1185 487 sep->se_checked = 1;
6e9dfe5b
AC
488 if (ISMUX(sep)) {
489 sep->se_fd = -1;
490 continue;
491 }
21ed1185
MK
492 sp = getservbyname(sep->se_service, sep->se_proto);
493 if (sp == 0) {
6a3125d9 494 syslog(LOG_ERR, "%s/%s: unknown service",
21ed1185 495 sep->se_service, sep->se_proto);
6e9dfe5b 496 sep->se_checked = 0;
21ed1185
MK
497 continue;
498 }
fc99bd8d 499 if (sp->s_port != sep->se_ctrladdr.sin_port) {
5003ab23 500 sep->se_ctrladdr.sin_family = AF_INET;
fc99bd8d 501 sep->se_ctrladdr.sin_port = sp->s_port;
6e9dfe5b
AC
502 if (sep->se_fd >= 0)
503 close_sep(sep);
21ed1185 504 }
fc99bd8d
MK
505 if (sep->se_fd == -1)
506 setup(sep);
21ed1185
MK
507 }
508 endconfig();
509 /*
510 * Purge anything not looked at above.
511 */
fc99bd8d 512 omask = sigblock(SIGBLOCK);
21ed1185
MK
513 sepp = &servtab;
514 while (sep = *sepp) {
515 if (sep->se_checked) {
516 sepp = &sep->se_next;
517 continue;
518 }
519 *sepp = sep->se_next;
6e9dfe5b
AC
520 if (sep->se_fd >= 0)
521 close_sep(sep);
9a0fbd5b
MK
522 if (debug)
523 print_service("FREE", sep);
21ed1185
MK
524 freeconfig(sep);
525 free((char *)sep);
526 }
527 (void) sigsetmask(omask);
528}
529
664e9651 530void
fc99bd8d
MK
531retry()
532{
533 register struct servtab *sep;
534
535 timingout = 0;
536 for (sep = servtab; sep; sep = sep->se_next)
537 if (sep->se_fd == -1)
538 setup(sep);
539}
540
541setup(sep)
542 register struct servtab *sep;
543{
544 int on = 1;
545
546 if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
6e9dfe5b
AC
547 if (debug)
548 fprintf(stderr, "socket failed on %s/%s: %s\n",
549 sep->se_service, sep->se_proto,
550 strerror(errno));
fc99bd8d
MK
551 syslog(LOG_ERR, "%s/%s: socket: %m",
552 sep->se_service, sep->se_proto);
553 return;
554 }
555#define turnon(fd, opt) \
556setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
557 if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
558 turnon(sep->se_fd, SO_DEBUG) < 0)
559 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
560 if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
561 syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
562#undef turnon
95236f21 563 if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
fc99bd8d 564 sizeof (sep->se_ctrladdr)) < 0) {
6e9dfe5b
AC
565 if (debug)
566 fprintf(stderr, "bind failed on %s/%s: %s\n",
567 sep->se_service, sep->se_proto,
568 strerror(errno));
fc99bd8d
MK
569 syslog(LOG_ERR, "%s/%s: bind: %m",
570 sep->se_service, sep->se_proto);
571 (void) close(sep->se_fd);
572 sep->se_fd = -1;
573 if (!timingout) {
574 timingout = 1;
575 alarm(RETRYTIME);
576 }
577 return;
578 }
579 if (sep->se_socktype == SOCK_STREAM)
580 listen(sep->se_fd, 10);
581 FD_SET(sep->se_fd, &allsock);
582 nsock++;
583 if (sep->se_fd > maxsock)
584 maxsock = sep->se_fd;
6e9dfe5b
AC
585 if (debug) {
586 fprintf(stderr, "registered %s on %d\n",
587 sep->se_server, sep->se_fd);
588 }
589}
590
591/*
592 * Finish with a service and its socket.
593 */
594close_sep(sep)
595 register struct servtab *sep;
596{
597 if (sep->se_fd >= 0) {
598 nsock--;
599 FD_CLR(sep->se_fd, &allsock);
600 (void) close(sep->se_fd);
601 sep->se_fd = -1;
602 }
603 sep->se_count = 0;
604 /*
605 * Don't keep the pid of this running deamon: when reapchild()
606 * reaps this pid, it would erroneously increment nsock.
607 */
608 if (sep->se_wait > 1)
609 sep->se_wait = 1;
fc99bd8d
MK
610}
611
21ed1185
MK
612struct servtab *
613enter(cp)
614 struct servtab *cp;
615{
616 register struct servtab *sep;
92732149 617 long omask;
21ed1185
MK
618
619 sep = (struct servtab *)malloc(sizeof (*sep));
620 if (sep == (struct servtab *)0) {
6a3125d9 621 syslog(LOG_ERR, "Out of memory.");
21ed1185
MK
622 exit(-1);
623 }
624 *sep = *cp;
625 sep->se_fd = -1;
fc99bd8d 626 omask = sigblock(SIGBLOCK);
21ed1185
MK
627 sep->se_next = servtab;
628 servtab = sep;
629 sigsetmask(omask);
630 return (sep);
631}
632
633FILE *fconfig = NULL;
634struct servtab serv;
635char line[256];
6e9dfe5b 636char *sskip(), *skip(), *nextline();
21ed1185
MK
637
638setconfig()
639{
640
641 if (fconfig != NULL) {
6e9dfe5b 642 fseek(fconfig, 0L, SEEK_SET);
21ed1185
MK
643 return (1);
644 }
645 fconfig = fopen(CONFIG, "r");
646 return (fconfig != NULL);
647}
648
649endconfig()
650{
567752b5
KB
651 if (fconfig) {
652 (void) fclose(fconfig);
653 fconfig = NULL;
654 }
21ed1185
MK
655}
656
657struct servtab *
658getconfigent()
659{
660 register struct servtab *sep = &serv;
21ed1185 661 int argc;
95236f21 662 char *cp, *arg, *newstr();
6e9dfe5b
AC
663 static char TCPMUX_TOKEN[] = "tcpmux/";
664#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1)
21ed1185 665
b1b30605 666more:
6e9dfe5b 667 while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
21ed1185
MK
668 ;
669 if (cp == NULL)
670 return ((struct servtab *)0);
6e9dfe5b
AC
671 /*
672 * clear the static buffer, since some fields (se_ctrladdr,
673 * for example) don't get initialized here.
674 */
675 bzero((caddr_t)sep, sizeof *sep);
21ed1185 676 arg = skip(&cp);
6e9dfe5b
AC
677 if (cp == NULL) {
678 /* got an empty line containing just blanks/tabs. */
679 goto more;
680 }
681 if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
682 char *c = arg + MUX_LEN;
683 if (*c == '+') {
684 sep->se_type = MUXPLUS_TYPE;
685 c++;
686 } else
687 sep->se_type = MUX_TYPE;
688 sep->se_service = newstr(c);
689 } else {
690 sep->se_service = newstr(arg);
691 sep->se_type = NORM_TYPE;
692 }
693 arg = sskip(&cp);
21ed1185
MK
694 if (strcmp(arg, "stream") == 0)
695 sep->se_socktype = SOCK_STREAM;
696 else if (strcmp(arg, "dgram") == 0)
697 sep->se_socktype = SOCK_DGRAM;
698 else if (strcmp(arg, "rdm") == 0)
699 sep->se_socktype = SOCK_RDM;
700 else if (strcmp(arg, "seqpacket") == 0)
701 sep->se_socktype = SOCK_SEQPACKET;
702 else if (strcmp(arg, "raw") == 0)
703 sep->se_socktype = SOCK_RAW;
704 else
705 sep->se_socktype = -1;
6e9dfe5b
AC
706 sep->se_proto = newstr(sskip(&cp));
707 arg = sskip(&cp);
21ed1185 708 sep->se_wait = strcmp(arg, "wait") == 0;
6e9dfe5b
AC
709 if (ISMUX(sep)) {
710 /*
711 * Silently enforce "nowait" for TCPMUX services since
712 * they don't have an assigned port to listen on.
713 */
714 sep->se_wait = 0;
715
716 if (strcmp(sep->se_proto, "tcp")) {
717 syslog(LOG_ERR,
718 "%s: bad protocol for tcpmux service %s",
719 CONFIG, sep->se_service);
720 goto more;
721 }
722 if (sep->se_socktype != SOCK_STREAM) {
723 syslog(LOG_ERR,
724 "%s: bad socket type for tcpmux service %s",
725 CONFIG, sep->se_service);
726 goto more;
727 }
728 }
729 sep->se_user = newstr(sskip(&cp));
730 sep->se_server = newstr(sskip(&cp));
b1b30605
MK
731 if (strcmp(sep->se_server, "internal") == 0) {
732 register struct biltin *bi;
733
734 for (bi = biltins; bi->bi_service; bi++)
735 if (bi->bi_socktype == sep->se_socktype &&
736 strcmp(bi->bi_service, sep->se_service) == 0)
737 break;
738 if (bi->bi_service == 0) {
6e9dfe5b 739 syslog(LOG_ERR, "internal service %s unknown",
b1b30605
MK
740 sep->se_service);
741 goto more;
742 }
743 sep->se_bi = bi;
744 sep->se_wait = bi->bi_wait;
9a0fbd5b
MK
745 } else
746 sep->se_bi = NULL;
21ed1185
MK
747 argc = 0;
748 for (arg = skip(&cp); cp; arg = skip(&cp))
749 if (argc < MAXARGV)
95236f21 750 sep->se_argv[argc++] = newstr(arg);
21ed1185
MK
751 while (argc <= MAXARGV)
752 sep->se_argv[argc++] = NULL;
753 return (sep);
754}
755
756freeconfig(cp)
757 register struct servtab *cp;
758{
759 int i;
760
761 if (cp->se_service)
762 free(cp->se_service);
763 if (cp->se_proto)
764 free(cp->se_proto);
2a6b82aa
JL
765 if (cp->se_user)
766 free(cp->se_user);
21ed1185
MK
767 if (cp->se_server)
768 free(cp->se_server);
769 for (i = 0; i < MAXARGV; i++)
770 if (cp->se_argv[i])
771 free(cp->se_argv[i]);
772}
773
6e9dfe5b
AC
774
775/*
776 * Safe skip - if skip returns null, log a syntax error in the
777 * configuration file and exit.
778 */
779char *
780sskip(cpp)
781 char **cpp;
782{
783 register char *cp;
784
785 cp = skip(cpp);
786 if (cp == NULL) {
787 syslog(LOG_ERR, "%s: syntax error", CONFIG);
788 exit(-1);
789 }
790 return (cp);
791}
792
21ed1185
MK
793char *
794skip(cpp)
795 char **cpp;
796{
797 register char *cp = *cpp;
798 char *start;
799
800again:
801 while (*cp == ' ' || *cp == '\t')
802 cp++;
803 if (*cp == '\0') {
4650c563 804 int c;
21ed1185
MK
805
806 c = getc(fconfig);
567752b5 807 (void) ungetc(c, fconfig);
21ed1185
MK
808 if (c == ' ' || c == '\t')
809 if (cp = nextline(fconfig))
810 goto again;
811 *cpp = (char *)0;
812 return ((char *)0);
813 }
814 start = cp;
815 while (*cp && *cp != ' ' && *cp != '\t')
816 cp++;
817 if (*cp != '\0')
818 *cp++ = '\0';
819 *cpp = cp;
820 return (start);
821}
822
823char *
824nextline(fd)
825 FILE *fd;
826{
827 char *cp;
828
2a6b82aa 829 if (fgets(line, sizeof (line), fd) == NULL)
21ed1185
MK
830 return ((char *)0);
831 cp = index(line, '\n');
832 if (cp)
833 *cp = '\0';
834 return (line);
835}
836
837char *
95236f21 838newstr(cp)
21ed1185
MK
839 char *cp;
840{
093d40e3 841 if (cp = strdup(cp ? cp : ""))
95236f21 842 return(cp);
093d40e3 843 syslog(LOG_ERR, "strdup: %m");
95236f21 844 exit(-1);
21ed1185 845}
b1b30605
MK
846
847setproctitle(a, s)
848 char *a;
849 int s;
850{
851 int size;
852 register char *cp;
853 struct sockaddr_in sin;
854 char buf[80];
855
856 cp = Argv[0];
857 size = sizeof(sin);
95236f21 858 if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
9bd38ba8 859 (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
b1b30605 860 else
9bd38ba8 861 (void) sprintf(buf, "-%s", a);
b1b30605
MK
862 strncpy(cp, buf, LastArg - cp);
863 cp += strlen(cp);
864 while (cp < LastArg)
865 *cp++ = ' ';
866}
867
868/*
869 * Internet services provided internally by inetd:
870 */
a1fe6118 871#define BUFSIZE 8192
b1b30605
MK
872
873/* ARGSUSED */
874echo_stream(s, sep) /* Echo service -- echo data back */
875 int s;
876 struct servtab *sep;
877{
b9c70736 878 char buffer[BUFSIZE];
b1b30605
MK
879 int i;
880
0ee95e67 881 setproctitle(sep->se_service, s);
b1b30605
MK
882 while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
883 write(s, buffer, i) > 0)
884 ;
885 exit(0);
886}
887
888/* ARGSUSED */
889echo_dg(s, sep) /* Echo service -- echo data back */
890 int s;
891 struct servtab *sep;
892{
b9c70736 893 char buffer[BUFSIZE];
b1b30605
MK
894 int i, size;
895 struct sockaddr sa;
896
897 size = sizeof(sa);
898 if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
899 return;
900 (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
901}
902
903/* ARGSUSED */
904discard_stream(s, sep) /* Discard service -- ignore data */
905 int s;
906 struct servtab *sep;
907{
a1fe6118 908 int ret;
b9c70736 909 char buffer[BUFSIZE];
b1b30605 910
0ee95e67 911 setproctitle(sep->se_service, s);
b1b30605 912 while (1) {
a1fe6118 913 while ((ret = read(s, buffer, sizeof(buffer))) > 0)
b1b30605 914 ;
a1fe6118 915 if (ret == 0 || errno != EINTR)
b1b30605
MK
916 break;
917 }
918 exit(0);
919}
920
921/* ARGSUSED */
922discard_dg(s, sep) /* Discard service -- ignore data */
923 int s;
924 struct servtab *sep;
925{
b9c70736 926 char buffer[BUFSIZE];
b1b30605
MK
927
928 (void) read(s, buffer, sizeof(buffer));
929}
930
931#include <ctype.h>
932#define LINESIZ 72
933char ring[128];
934char *endring;
935
936initring()
937{
938 register int i;
939
940 endring = ring;
941
942 for (i = 0; i <= 128; ++i)
943 if (isprint(i))
944 *endring++ = i;
945}
946
947/* ARGSUSED */
948chargen_stream(s, sep) /* Character generator */
949 int s;
950 struct servtab *sep;
951{
92732149
KB
952 register char *rs;
953 int len;
b1b30605 954 char text[LINESIZ+2];
b1b30605 955
0ee95e67 956 setproctitle(sep->se_service, s);
92732149
KB
957
958 if (!endring) {
b1b30605 959 initring();
92732149
KB
960 rs = ring;
961 }
b1b30605 962
92732149
KB
963 text[LINESIZ] = '\r';
964 text[LINESIZ + 1] = '\n';
965 for (rs = ring;;) {
966 if ((len = endring - rs) >= LINESIZ)
967 bcopy(rs, text, LINESIZ);
968 else {
969 bcopy(rs, text, len);
970 bcopy(ring, text + len, LINESIZ - len);
b1b30605 971 }
92732149
KB
972 if (++rs == endring)
973 rs = ring;
974 if (write(s, text, sizeof(text)) != sizeof(text))
b1b30605
MK
975 break;
976 }
977 exit(0);
978}
979
980/* ARGSUSED */
981chargen_dg(s, sep) /* Character generator */
982 int s;
983 struct servtab *sep;
984{
b1b30605 985 struct sockaddr sa;
92732149
KB
986 static char *rs;
987 int len, size;
988 char text[LINESIZ+2];
b1b30605 989
92732149 990 if (endring == 0) {
b1b30605 991 initring();
92732149
KB
992 rs = ring;
993 }
b1b30605
MK
994
995 size = sizeof(sa);
996 if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
997 return;
b1b30605 998
92732149
KB
999 if ((len = endring - rs) >= LINESIZ)
1000 bcopy(rs, text, LINESIZ);
1001 else {
1002 bcopy(rs, text, len);
1003 bcopy(ring, text + len, LINESIZ - len);
1004 }
1005 if (++rs == endring)
1006 rs = ring;
1007 text[LINESIZ] = '\r';
1008 text[LINESIZ + 1] = '\n';
b1b30605
MK
1009 (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
1010}
1011
1012/*
1013 * Return a machine readable date and time, in the form of the
1014 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
1015 * returns the number of seconds since midnight, Jan 1, 1970,
1016 * we must add 2208988800 seconds to this figure to make up for
1017 * some seventy years Bell Labs was asleep.
1018 */
b1b30605
MK
1019
1020long
1021machtime()
1022{
1023 struct timeval tv;
1024
2a6b82aa 1025 if (gettimeofday(&tv, (struct timezone *)0) < 0) {
6e9dfe5b
AC
1026 if (debug)
1027 fprintf(stderr, "Unable to get time of day\n");
2a6b82aa 1028 return (0L);
b1b30605 1029 }
9a61decd
CT
1030#define OFFSET ((u_long)25567 * 24*60*60)
1031 return (htonl((long)(tv.tv_sec + OFFSET)));
1032#undef OFFSET
b1b30605
MK
1033}
1034
1035/* ARGSUSED */
1036machtime_stream(s, sep)
1037 int s;
1038 struct servtab *sep;
1039{
1040 long result;
1041
1042 result = machtime();
1043 (void) write(s, (char *) &result, sizeof(result));
1044}
1045
1046/* ARGSUSED */
1047machtime_dg(s, sep)
1048 int s;
1049 struct servtab *sep;
1050{
1051 long result;
1052 struct sockaddr sa;
1053 int size;
1054
1055 size = sizeof(sa);
2a6b82aa 1056 if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
b1b30605
MK
1057 return;
1058 result = machtime();
1059 (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
1060}
1061
1062/* ARGSUSED */
1063daytime_stream(s, sep) /* Return human-readable time of day */
1064 int s;
1065 struct servtab *sep;
1066{
1067 char buffer[256];
6e9dfe5b 1068 time_t clock;
b1b30605
MK
1069
1070 clock = time((time_t *) 0);
1071
9bd38ba8 1072 (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
2a6b82aa 1073 (void) write(s, buffer, strlen(buffer));
b1b30605
MK
1074}
1075
1076/* ARGSUSED */
1077daytime_dg(s, sep) /* Return human-readable time of day */
1078 int s;
1079 struct servtab *sep;
1080{
1081 char buffer[256];
6e9dfe5b 1082 time_t clock;
b1b30605
MK
1083 struct sockaddr sa;
1084 int size;
b1b30605
MK
1085
1086 clock = time((time_t *) 0);
1087
1088 size = sizeof(sa);
1089 if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
1090 return;
9bd38ba8 1091 (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
b1b30605
MK
1092 (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
1093}
9a0fbd5b
MK
1094
1095/*
1096 * print_service:
1097 * Dump relevant information to stderr
1098 */
1099print_service(action, sep)
1100 char *action;
1101 struct servtab *sep;
1102{
1103 fprintf(stderr,
1104 "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
1105 action, sep->se_service, sep->se_proto,
567752b5 1106 sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server);
9a0fbd5b 1107}
6e9dfe5b
AC
1108
1109/*
1110 * Based on TCPMUX.C by Mark K. Lottor November 1988
1111 * sri-nic::ps:<mkl>tcpmux.c
1112 */
1113
1114
1115static int /* # of characters upto \r,\n or \0 */
1116getline(fd, buf, len)
1117 int fd;
1118 char *buf;
1119 int len;
1120{
1121 int count = 0, n;
1122
1123 do {
1124 n = read(fd, buf, len-count);
1125 if (n == 0)
1126 return count;
1127 if (n < 0)
1128 return (-1);
1129 while (--n >= 0) {
1130 if (*buf == '\r' || *buf == '\n' || *buf == '\0')
1131 return count;
1132 count++;
1133 buf++;
1134 }
1135 } while (count < len);
1136 return (count);
1137}
1138
1139#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */
1140
1141#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1)
1142
1143struct servtab *
1144tcpmux(s)
1145 int s;
1146{
1147 register struct servtab *sep;
1148 char service[MAX_SERV_LEN+1];
1149 int len;
1150
1151 /* Get requested service name */
1152 if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
1153 strwrite(s, "-Error reading service name\r\n");
1154 return(NULL);
1155 }
1156 service[len] = '\0';
1157
1158 if (debug)
1159 fprintf(stderr, "tcpmux: someone wants %s\n", service);
1160
1161 /*
1162 * Help is a required command, and lists available services,
1163 * one per line.
1164 */
1165 if (!strcasecmp(service,"help")) {
1166 for (sep = servtab; sep; sep = sep->se_next) {
1167 if (!ISMUX(sep))
1168 continue;
1169 (void) write(s, sep->se_service, strlen(sep->se_service));
1170 strwrite(s, "\r\n");
1171 }
1172 return(NULL);
1173 }
1174
1175 /* Try matching a service in inetd.conf with the request */
1176 for (sep = servtab; sep; sep = sep->se_next) {
1177 if (!ISMUX(sep))
1178 continue;
1179 if (!strcasecmp(service,sep->se_service)) {
1180 if (ISMUXPLUS(sep)) {
1181 strwrite(s, "+Go\r\n");
1182 }
1183 return(sep);
1184 }
1185 }
1186 strwrite(s, "-Service not available\r\n");
1187 return(NULL);
1188}