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