Commit | Line | Data |
---|---|---|
528b0614 DF |
1 | /* |
2 | * Copyright (c) 1983 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
7 | #ifndef lint | |
8 | char copyright[] = | |
9 | "@(#) Copyright (c) 1983 Regents of the University of California.\n\ | |
10 | All rights reserved.\n"; | |
11 | #endif not lint | |
12 | ||
21ed1185 | 13 | #ifndef lint |
92732149 | 14 | static char sccsid[] = "@(#)inetd.c 5.9 (Berkeley) %G%"; |
528b0614 | 15 | #endif not lint |
21ed1185 MK |
16 | |
17 | /* | |
18 | * Inetd - Internet super-server | |
19 | * | |
20 | * This program invokes all internet services as needed. | |
21 | * connection-oriented services are invoked each time a | |
22 | * connection is made, by creating a process. This process | |
23 | * is passed the connection as file descriptor 0 and is | |
24 | * expected to do a getpeername to find out the source host | |
25 | * and port. | |
26 | * | |
27 | * Datagram oriented services are invoked when a datagram | |
28 | * arrives; a process is created and passed a pending message | |
29 | * on file descriptor 0. Datagram servers may either connect | |
30 | * to their peer, freeing up the original socket for inetd | |
31 | * to receive further messages on, or ``take over the socket'', | |
32 | * processing all arriving datagrams and, eventually, timing | |
33 | * out. The first type of server is said to be ``multi-threaded''; | |
34 | * the second type of server ``single-threaded''. | |
35 | * | |
36 | * Inetd uses a configuration file which is read at startup | |
37 | * and, possibly, at some later time in response to a hangup signal. | |
38 | * The configuration file is ``free format'' with fields given in the | |
39 | * order shown below. Continuation lines for an entry must being with | |
40 | * a space or tab. All fields must be present in each entry. | |
41 | * | |
42 | * service name must be in /etc/services | |
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 MK |
47 | * server program full path name |
48 | * server program arguments maximum of MAXARGS (5) | |
49 | * | |
50 | * Comment lines are indicated by a `#' in column 1. | |
51 | */ | |
52 | #include <sys/param.h> | |
53 | #include <sys/stat.h> | |
54 | #include <sys/ioctl.h> | |
55 | #include <sys/socket.h> | |
56 | #include <sys/file.h> | |
57 | #include <sys/wait.h> | |
2a6b82aa JL |
58 | #include <sys/time.h> |
59 | #include <sys/resource.h> | |
21ed1185 MK |
60 | |
61 | #include <netinet/in.h> | |
62 | #include <arpa/inet.h> | |
63 | ||
64 | #include <errno.h> | |
65 | #include <stdio.h> | |
66 | #include <signal.h> | |
67 | #include <netdb.h> | |
6a3125d9 | 68 | #include <syslog.h> |
7d5eb6c4 | 69 | #include <pwd.h> |
21ed1185 | 70 | |
fc99bd8d MK |
71 | #define TOOMANY 40 /* don't start more than TOOMANY */ |
72 | #define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ | |
73 | #define RETRYTIME (60*10) /* retry after bind or server fail */ | |
74 | ||
75 | #define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) | |
76 | ||
21ed1185 MK |
77 | extern int errno; |
78 | ||
fc99bd8d | 79 | int reapchild(), retry(); |
21ed1185 MK |
80 | char *index(); |
81 | char *malloc(); | |
82 | ||
83 | int debug = 0; | |
5859fc43 MK |
84 | int nsock, maxsock; |
85 | fd_set allsock; | |
21ed1185 | 86 | int options; |
fc99bd8d | 87 | int timingout; |
21ed1185 MK |
88 | struct servent *sp; |
89 | ||
90 | struct servtab { | |
91 | char *se_service; /* name of service */ | |
92 | int se_socktype; /* type of socket to use */ | |
93 | char *se_proto; /* protocol used */ | |
94 | short se_wait; /* single threaded server */ | |
95 | short se_checked; /* looked at during merge */ | |
7d5eb6c4 | 96 | char *se_user; /* user name to run as */ |
b1b30605 | 97 | struct biltin *se_bi; /* if built-in, description */ |
21ed1185 MK |
98 | char *se_server; /* server program */ |
99 | #define MAXARGV 5 | |
100 | char *se_argv[MAXARGV+1]; /* program arguments */ | |
101 | int se_fd; /* open descriptor */ | |
102 | struct sockaddr_in se_ctrladdr;/* bound address */ | |
fc99bd8d MK |
103 | int se_count; /* number started since se_time */ |
104 | struct timeval se_time; /* start of se_count */ | |
21ed1185 MK |
105 | struct servtab *se_next; |
106 | } *servtab; | |
107 | ||
b1b30605 MK |
108 | int echo_stream(), discard_stream(), machtime_stream(); |
109 | int daytime_stream(), chargen_stream(); | |
110 | int echo_dg(), discard_dg(), machtime_dg(), daytime_dg(), chargen_dg(); | |
111 | ||
112 | struct biltin { | |
113 | char *bi_service; /* internally provided service name */ | |
114 | int bi_socktype; /* type of socket supported */ | |
115 | short bi_fork; /* 1 if should fork before call */ | |
116 | short bi_wait; /* 1 if should wait for child */ | |
117 | int (*bi_fn)(); /* function which performs it */ | |
118 | } biltins[] = { | |
119 | /* Echo received data */ | |
120 | "echo", SOCK_STREAM, 1, 0, echo_stream, | |
121 | "echo", SOCK_DGRAM, 0, 0, echo_dg, | |
122 | ||
123 | /* Internet /dev/null */ | |
124 | "discard", SOCK_STREAM, 1, 0, discard_stream, | |
125 | "discard", SOCK_DGRAM, 0, 0, discard_dg, | |
126 | ||
127 | /* Return 32 bit time since 1970 */ | |
128 | "time", SOCK_STREAM, 0, 0, machtime_stream, | |
129 | "time", SOCK_DGRAM, 0, 0, machtime_dg, | |
130 | ||
131 | /* Return human-readable time */ | |
132 | "daytime", SOCK_STREAM, 0, 0, daytime_stream, | |
133 | "daytime", SOCK_DGRAM, 0, 0, daytime_dg, | |
134 | ||
135 | /* Familiar character generator */ | |
136 | "chargen", SOCK_STREAM, 1, 0, chargen_stream, | |
137 | "chargen", SOCK_DGRAM, 0, 0, chargen_dg, | |
138 | 0 | |
139 | }; | |
140 | ||
141 | #define NUMINT (sizeof(intab) / sizeof(struct inent)) | |
21ed1185 | 142 | char *CONFIG = "/etc/inetd.conf"; |
b1b30605 MK |
143 | char **Argv; |
144 | char *LastArg; | |
21ed1185 | 145 | |
b1b30605 | 146 | main(argc, argv, envp) |
21ed1185 | 147 | int argc; |
b1b30605 | 148 | char *argv[], *envp[]; |
21ed1185 | 149 | { |
21ed1185 | 150 | register struct servtab *sep; |
7d5eb6c4 | 151 | register struct passwd *pwd; |
21ed1185 | 152 | char *cp, buf[50]; |
fc99bd8d MK |
153 | int pid, i, dofork; |
154 | struct sigvec sv; | |
21ed1185 | 155 | |
b1b30605 MK |
156 | Argv = argv; |
157 | if (envp == 0 || *envp == 0) | |
158 | envp = argv; | |
159 | while (*envp) | |
160 | envp++; | |
161 | LastArg = envp[-1] + strlen(envp[-1]); | |
21ed1185 MK |
162 | argc--, argv++; |
163 | while (argc > 0 && *argv[0] == '-') { | |
164 | for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { | |
165 | ||
166 | case 'd': | |
167 | debug = 1; | |
168 | options |= SO_DEBUG; | |
169 | break; | |
170 | ||
171 | default: | |
172 | fprintf(stderr, | |
173 | "inetd: Unknown flag -%c ignored.\n", *cp); | |
174 | break; | |
175 | } | |
176 | nextopt: | |
177 | argc--, argv++; | |
178 | } | |
179 | if (argc > 0) | |
180 | CONFIG = argv[0]; | |
181 | #ifndef DEBUG | |
182 | if (fork()) | |
183 | exit(0); | |
184 | { int s; | |
185 | for (s = 0; s < 10; s++) | |
186 | (void) close(s); | |
187 | } | |
188 | (void) open("/", O_RDONLY); | |
189 | (void) dup2(0, 1); | |
190 | (void) dup2(0, 2); | |
191 | { int tt = open("/dev/tty", O_RDWR); | |
192 | if (tt > 0) { | |
2a6b82aa | 193 | ioctl(tt, TIOCNOTTY, (char *)0); |
21ed1185 MK |
194 | close(tt); |
195 | } | |
196 | } | |
92732149 | 197 | (void) setpgrp(0, 0); |
21ed1185 | 198 | #endif |
fc99bd8d MK |
199 | openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); |
200 | bzero((char *)&sv, sizeof(sv)); | |
201 | sv.sv_mask = SIGBLOCK; | |
202 | sv.sv_handler = retry; | |
203 | sigvec(SIGALRM, &sv, (struct sigvec *)0); | |
21ed1185 | 204 | config(); |
fc99bd8d MK |
205 | sv.sv_handler = config; |
206 | sigvec(SIGHUP, &sv, (struct sigvec *)0); | |
207 | sv.sv_handler = reapchild; | |
208 | sigvec(SIGCHLD, &sv, (struct sigvec *)0); | |
209 | ||
21ed1185 | 210 | for (;;) { |
fc99bd8d MK |
211 | int s, ctrl, n; |
212 | fd_set readable; | |
213 | ||
0ee95e67 MK |
214 | if (nsock == 0) { |
215 | (void) sigblock(SIGBLOCK); | |
216 | while (nsock == 0) | |
92732149 KB |
217 | sigpause(0L); |
218 | (void) sigsetmask(0L); | |
0ee95e67 | 219 | } |
fc99bd8d MK |
220 | readable = allsock; |
221 | if ((n = select(maxsock + 1, &readable, (fd_set *)0, | |
222 | (fd_set *)0, (struct timeval *)0)) <= 0) { | |
223 | if (n < 0 && errno != EINTR) | |
0ee95e67 | 224 | syslog(LOG_WARNING, "select: %m\n"); |
fc99bd8d MK |
225 | sleep(1); |
226 | continue; | |
227 | } | |
228 | for (sep = servtab; n && sep; sep = sep->se_next) | |
229 | if (FD_ISSET(sep->se_fd, &readable)) { | |
230 | n--; | |
21ed1185 MK |
231 | if (debug) |
232 | fprintf(stderr, "someone wants %s\n", sep->se_service); | |
5859fc43 | 233 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { |
fc99bd8d MK |
234 | ctrl = accept(sep->se_fd, (struct sockaddr *)0, |
235 | (int *)0); | |
6a3125d9 RC |
236 | if (debug) |
237 | fprintf(stderr, "accept, ctrl %d\n", ctrl); | |
21ed1185 MK |
238 | if (ctrl < 0) { |
239 | if (errno == EINTR) | |
240 | continue; | |
6a3125d9 | 241 | syslog(LOG_WARNING, "accept: %m"); |
21ed1185 MK |
242 | continue; |
243 | } | |
244 | } else | |
245 | ctrl = sep->se_fd; | |
fc99bd8d | 246 | (void) sigblock(SIGBLOCK); |
b1b30605 | 247 | pid = 0; |
fc99bd8d MK |
248 | dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork); |
249 | if (dofork) { | |
250 | if (sep->se_count++ == 0) | |
251 | (void)gettimeofday(&sep->se_time, | |
252 | (struct timezone *)0); | |
253 | else if (sep->se_count >= TOOMANY) { | |
254 | struct timeval now; | |
255 | ||
256 | (void)gettimeofday(&now, (struct timezone *)0); | |
257 | if (now.tv_sec - sep->se_time.tv_sec > | |
258 | CNT_INTVL) { | |
259 | sep->se_time = now; | |
260 | sep->se_count = 1; | |
261 | } else { | |
262 | syslog(LOG_ERR, | |
263 | "%s/%s server failing (looping), service terminated\n", | |
264 | sep->se_service, sep->se_proto); | |
265 | FD_CLR(sep->se_fd, &allsock); | |
266 | (void) close(sep->se_fd); | |
267 | sep->se_fd = -1; | |
268 | sep->se_count = 0; | |
269 | nsock--; | |
92732149 | 270 | sigsetmask(0L); |
fc99bd8d MK |
271 | if (!timingout) { |
272 | timingout = 1; | |
273 | alarm(RETRYTIME); | |
274 | } | |
275 | continue; | |
276 | } | |
277 | } | |
b1b30605 | 278 | pid = fork(); |
fc99bd8d | 279 | } |
21ed1185 | 280 | if (pid < 0) { |
5859fc43 | 281 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) |
21ed1185 | 282 | close(ctrl); |
92732149 | 283 | sigsetmask(0L); |
21ed1185 MK |
284 | sleep(1); |
285 | continue; | |
286 | } | |
fc99bd8d | 287 | if (pid && sep->se_wait) { |
21ed1185 | 288 | sep->se_wait = pid; |
fc99bd8d | 289 | FD_CLR(sep->se_fd, &allsock); |
5859fc43 | 290 | nsock--; |
21ed1185 | 291 | } |
92732149 | 292 | sigsetmask(0L); |
21ed1185 MK |
293 | if (pid == 0) { |
294 | #ifdef DEBUG | |
fc99bd8d MK |
295 | int tt; |
296 | ||
297 | if (dofork && (tt = open("/dev/tty", O_RDWR)) > 0) { | |
21ed1185 MK |
298 | ioctl(tt, TIOCNOTTY, 0); |
299 | close(tt); | |
300 | } | |
301 | #endif | |
fc99bd8d | 302 | if (dofork) |
b1b30605 MK |
303 | for (i = getdtablesize(); --i > 2; ) |
304 | if (i != ctrl) | |
305 | close(i); | |
306 | if (sep->se_bi) | |
307 | (*sep->se_bi->bi_fn)(ctrl, sep); | |
308 | else { | |
309 | dup2(ctrl, 0); | |
310 | close(ctrl); | |
311 | dup2(0, 1); | |
312 | dup2(0, 2); | |
313 | if ((pwd = getpwnam(sep->se_user)) == NULL) { | |
314 | syslog(LOG_ERR, | |
315 | "getpwnam: %s: No such user", | |
316 | sep->se_user); | |
fc99bd8d MK |
317 | if (sep->se_socktype != SOCK_STREAM) |
318 | recv(0, buf, sizeof (buf), 0); | |
b1b30605 MK |
319 | _exit(1); |
320 | } | |
321 | if (pwd->pw_uid) { | |
2a6b82aa | 322 | (void) setgid((gid_t)pwd->pw_gid); |
b1b30605 | 323 | initgroups(pwd->pw_name, pwd->pw_gid); |
2a6b82aa | 324 | (void) setuid((uid_t)pwd->pw_uid); |
b1b30605 MK |
325 | } |
326 | if (debug) | |
327 | fprintf(stderr, "%d execl %s\n", | |
328 | getpid(), sep->se_server); | |
329 | execv(sep->se_server, sep->se_argv); | |
330 | if (sep->se_socktype != SOCK_STREAM) | |
331 | recv(0, buf, sizeof (buf), 0); | |
332 | syslog(LOG_ERR, "execv %s: %m", sep->se_server); | |
333 | _exit(1); | |
7d5eb6c4 | 334 | } |
21ed1185 | 335 | } |
5859fc43 | 336 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) |
21ed1185 | 337 | close(ctrl); |
fc99bd8d | 338 | } |
21ed1185 MK |
339 | } |
340 | } | |
341 | ||
342 | reapchild() | |
343 | { | |
344 | union wait status; | |
345 | int pid; | |
346 | register struct servtab *sep; | |
347 | ||
348 | for (;;) { | |
2a6b82aa | 349 | pid = wait3(&status, WNOHANG, (struct rusage *)0); |
21ed1185 MK |
350 | if (pid <= 0) |
351 | break; | |
352 | if (debug) | |
353 | fprintf(stderr, "%d reaped\n", pid); | |
354 | for (sep = servtab; sep; sep = sep->se_next) | |
355 | if (sep->se_wait == pid) { | |
356 | if (status.w_status) | |
6a3125d9 RC |
357 | syslog(LOG_WARNING, |
358 | "%s: exit status 0x%x", | |
21ed1185 MK |
359 | sep->se_server, status); |
360 | if (debug) | |
361 | fprintf(stderr, "restored %s, fd %d\n", | |
362 | sep->se_service, sep->se_fd); | |
5859fc43 MK |
363 | FD_SET(sep->se_fd, &allsock); |
364 | nsock++; | |
21ed1185 MK |
365 | sep->se_wait = 1; |
366 | } | |
367 | } | |
368 | } | |
369 | ||
370 | config() | |
371 | { | |
372 | register struct servtab *sep, *cp, **sepp; | |
373 | struct servtab *getconfigent(), *enter(); | |
92732149 | 374 | long omask; |
21ed1185 MK |
375 | |
376 | if (!setconfig()) { | |
6a3125d9 | 377 | syslog(LOG_ERR, "%s: %m", CONFIG); |
21ed1185 MK |
378 | return; |
379 | } | |
380 | for (sep = servtab; sep; sep = sep->se_next) | |
381 | sep->se_checked = 0; | |
382 | while (cp = getconfigent()) { | |
383 | for (sep = servtab; sep; sep = sep->se_next) | |
384 | if (strcmp(sep->se_service, cp->se_service) == 0 && | |
385 | strcmp(sep->se_proto, cp->se_proto) == 0) | |
386 | break; | |
387 | if (sep != 0) { | |
388 | int i; | |
389 | ||
fc99bd8d MK |
390 | omask = sigblock(SIGBLOCK); |
391 | if (cp->se_bi == 0) | |
392 | sep->se_wait = cp->se_wait; | |
21ed1185 | 393 | #define SWAP(a, b) { char *c = a; a = b; b = c; } |
2a6b82aa JL |
394 | if (cp->se_user) |
395 | SWAP(sep->se_user, cp->se_user); | |
21ed1185 MK |
396 | if (cp->se_server) |
397 | SWAP(sep->se_server, cp->se_server); | |
398 | for (i = 0; i < MAXARGV; i++) | |
399 | SWAP(sep->se_argv[i], cp->se_argv[i]); | |
400 | sigsetmask(omask); | |
401 | freeconfig(cp); | |
9a0fbd5b MK |
402 | if (debug) |
403 | print_service("REDO", sep); | |
404 | } else { | |
21ed1185 | 405 | sep = enter(cp); |
9a0fbd5b MK |
406 | if (debug) |
407 | print_service("ADD ", sep); | |
408 | } | |
21ed1185 | 409 | sep->se_checked = 1; |
21ed1185 MK |
410 | sp = getservbyname(sep->se_service, sep->se_proto); |
411 | if (sp == 0) { | |
6a3125d9 | 412 | syslog(LOG_ERR, "%s/%s: unknown service", |
21ed1185 MK |
413 | sep->se_service, sep->se_proto); |
414 | continue; | |
415 | } | |
fc99bd8d MK |
416 | if (sp->s_port != sep->se_ctrladdr.sin_port) { |
417 | sep->se_ctrladdr.sin_port = sp->s_port; | |
418 | if (sep->se_fd != -1) | |
419 | (void) close(sep->se_fd); | |
420 | sep->se_fd = -1; | |
21ed1185 | 421 | } |
fc99bd8d MK |
422 | if (sep->se_fd == -1) |
423 | setup(sep); | |
21ed1185 MK |
424 | } |
425 | endconfig(); | |
426 | /* | |
427 | * Purge anything not looked at above. | |
428 | */ | |
fc99bd8d | 429 | omask = sigblock(SIGBLOCK); |
21ed1185 MK |
430 | sepp = &servtab; |
431 | while (sep = *sepp) { | |
432 | if (sep->se_checked) { | |
433 | sepp = &sep->se_next; | |
434 | continue; | |
435 | } | |
436 | *sepp = sep->se_next; | |
437 | if (sep->se_fd != -1) { | |
5859fc43 MK |
438 | FD_CLR(sep->se_fd, &allsock); |
439 | nsock--; | |
21ed1185 MK |
440 | (void) close(sep->se_fd); |
441 | } | |
9a0fbd5b MK |
442 | if (debug) |
443 | print_service("FREE", sep); | |
21ed1185 MK |
444 | freeconfig(sep); |
445 | free((char *)sep); | |
446 | } | |
447 | (void) sigsetmask(omask); | |
448 | } | |
449 | ||
fc99bd8d MK |
450 | retry() |
451 | { | |
452 | register struct servtab *sep; | |
453 | ||
454 | timingout = 0; | |
455 | for (sep = servtab; sep; sep = sep->se_next) | |
456 | if (sep->se_fd == -1) | |
457 | setup(sep); | |
458 | } | |
459 | ||
460 | setup(sep) | |
461 | register struct servtab *sep; | |
462 | { | |
463 | int on = 1; | |
464 | ||
465 | if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) { | |
466 | syslog(LOG_ERR, "%s/%s: socket: %m", | |
467 | sep->se_service, sep->se_proto); | |
468 | return; | |
469 | } | |
470 | #define turnon(fd, opt) \ | |
471 | setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) | |
472 | if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && | |
473 | turnon(sep->se_fd, SO_DEBUG) < 0) | |
474 | syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); | |
475 | if (turnon(sep->se_fd, SO_REUSEADDR) < 0) | |
476 | syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); | |
477 | #undef turnon | |
478 | if (bind(sep->se_fd, &sep->se_ctrladdr, | |
479 | sizeof (sep->se_ctrladdr)) < 0) { | |
480 | syslog(LOG_ERR, "%s/%s: bind: %m", | |
481 | sep->se_service, sep->se_proto); | |
482 | (void) close(sep->se_fd); | |
483 | sep->se_fd = -1; | |
484 | if (!timingout) { | |
485 | timingout = 1; | |
486 | alarm(RETRYTIME); | |
487 | } | |
488 | return; | |
489 | } | |
490 | if (sep->se_socktype == SOCK_STREAM) | |
491 | listen(sep->se_fd, 10); | |
492 | FD_SET(sep->se_fd, &allsock); | |
493 | nsock++; | |
494 | if (sep->se_fd > maxsock) | |
495 | maxsock = sep->se_fd; | |
496 | } | |
497 | ||
21ed1185 MK |
498 | struct servtab * |
499 | enter(cp) | |
500 | struct servtab *cp; | |
501 | { | |
502 | register struct servtab *sep; | |
92732149 | 503 | long omask; |
21ed1185 MK |
504 | char *strdup(); |
505 | ||
506 | sep = (struct servtab *)malloc(sizeof (*sep)); | |
507 | if (sep == (struct servtab *)0) { | |
6a3125d9 | 508 | syslog(LOG_ERR, "Out of memory."); |
21ed1185 MK |
509 | exit(-1); |
510 | } | |
511 | *sep = *cp; | |
512 | sep->se_fd = -1; | |
fc99bd8d | 513 | omask = sigblock(SIGBLOCK); |
21ed1185 MK |
514 | sep->se_next = servtab; |
515 | servtab = sep; | |
516 | sigsetmask(omask); | |
517 | return (sep); | |
518 | } | |
519 | ||
520 | FILE *fconfig = NULL; | |
521 | struct servtab serv; | |
522 | char line[256]; | |
523 | char *skip(), *nextline(); | |
524 | ||
525 | setconfig() | |
526 | { | |
527 | ||
528 | if (fconfig != NULL) { | |
2a6b82aa | 529 | fseek(fconfig, 0L, L_SET); |
21ed1185 MK |
530 | return (1); |
531 | } | |
532 | fconfig = fopen(CONFIG, "r"); | |
533 | return (fconfig != NULL); | |
534 | } | |
535 | ||
536 | endconfig() | |
537 | { | |
538 | ||
539 | if (fconfig == NULL) | |
540 | return; | |
541 | fclose(fconfig); | |
542 | fconfig = NULL; | |
543 | } | |
544 | ||
545 | struct servtab * | |
546 | getconfigent() | |
547 | { | |
548 | register struct servtab *sep = &serv; | |
549 | char *cp, *arg; | |
550 | int argc; | |
551 | ||
b1b30605 | 552 | more: |
21ed1185 MK |
553 | while ((cp = nextline(fconfig)) && *cp == '#') |
554 | ; | |
555 | if (cp == NULL) | |
556 | return ((struct servtab *)0); | |
557 | sep->se_service = strdup(skip(&cp)); | |
558 | arg = skip(&cp); | |
559 | if (strcmp(arg, "stream") == 0) | |
560 | sep->se_socktype = SOCK_STREAM; | |
561 | else if (strcmp(arg, "dgram") == 0) | |
562 | sep->se_socktype = SOCK_DGRAM; | |
563 | else if (strcmp(arg, "rdm") == 0) | |
564 | sep->se_socktype = SOCK_RDM; | |
565 | else if (strcmp(arg, "seqpacket") == 0) | |
566 | sep->se_socktype = SOCK_SEQPACKET; | |
567 | else if (strcmp(arg, "raw") == 0) | |
568 | sep->se_socktype = SOCK_RAW; | |
569 | else | |
570 | sep->se_socktype = -1; | |
571 | sep->se_proto = strdup(skip(&cp)); | |
572 | arg = skip(&cp); | |
573 | sep->se_wait = strcmp(arg, "wait") == 0; | |
7d5eb6c4 | 574 | sep->se_user = strdup(skip(&cp)); |
21ed1185 | 575 | sep->se_server = strdup(skip(&cp)); |
b1b30605 MK |
576 | if (strcmp(sep->se_server, "internal") == 0) { |
577 | register struct biltin *bi; | |
578 | ||
579 | for (bi = biltins; bi->bi_service; bi++) | |
580 | if (bi->bi_socktype == sep->se_socktype && | |
581 | strcmp(bi->bi_service, sep->se_service) == 0) | |
582 | break; | |
583 | if (bi->bi_service == 0) { | |
584 | syslog(LOG_ERR, "internal service %s unknown\n", | |
585 | sep->se_service); | |
586 | goto more; | |
587 | } | |
588 | sep->se_bi = bi; | |
589 | sep->se_wait = bi->bi_wait; | |
9a0fbd5b MK |
590 | } else |
591 | sep->se_bi = NULL; | |
21ed1185 MK |
592 | argc = 0; |
593 | for (arg = skip(&cp); cp; arg = skip(&cp)) | |
594 | if (argc < MAXARGV) | |
595 | sep->se_argv[argc++] = strdup(arg); | |
596 | while (argc <= MAXARGV) | |
597 | sep->se_argv[argc++] = NULL; | |
598 | return (sep); | |
599 | } | |
600 | ||
601 | freeconfig(cp) | |
602 | register struct servtab *cp; | |
603 | { | |
604 | int i; | |
605 | ||
606 | if (cp->se_service) | |
607 | free(cp->se_service); | |
608 | if (cp->se_proto) | |
609 | free(cp->se_proto); | |
2a6b82aa JL |
610 | if (cp->se_user) |
611 | free(cp->se_user); | |
21ed1185 MK |
612 | if (cp->se_server) |
613 | free(cp->se_server); | |
614 | for (i = 0; i < MAXARGV; i++) | |
615 | if (cp->se_argv[i]) | |
616 | free(cp->se_argv[i]); | |
617 | } | |
618 | ||
619 | char * | |
620 | skip(cpp) | |
621 | char **cpp; | |
622 | { | |
623 | register char *cp = *cpp; | |
624 | char *start; | |
625 | ||
626 | again: | |
627 | while (*cp == ' ' || *cp == '\t') | |
628 | cp++; | |
629 | if (*cp == '\0') { | |
630 | char c; | |
631 | ||
632 | c = getc(fconfig); | |
633 | ungetc(c, fconfig); | |
634 | if (c == ' ' || c == '\t') | |
635 | if (cp = nextline(fconfig)) | |
636 | goto again; | |
637 | *cpp = (char *)0; | |
638 | return ((char *)0); | |
639 | } | |
640 | start = cp; | |
641 | while (*cp && *cp != ' ' && *cp != '\t') | |
642 | cp++; | |
643 | if (*cp != '\0') | |
644 | *cp++ = '\0'; | |
645 | *cpp = cp; | |
646 | return (start); | |
647 | } | |
648 | ||
649 | char * | |
650 | nextline(fd) | |
651 | FILE *fd; | |
652 | { | |
653 | char *cp; | |
654 | ||
2a6b82aa | 655 | if (fgets(line, sizeof (line), fd) == NULL) |
21ed1185 MK |
656 | return ((char *)0); |
657 | cp = index(line, '\n'); | |
658 | if (cp) | |
659 | *cp = '\0'; | |
660 | return (line); | |
661 | } | |
662 | ||
663 | char * | |
664 | strdup(cp) | |
665 | char *cp; | |
666 | { | |
667 | char *new; | |
668 | ||
669 | if (cp == NULL) | |
670 | cp = ""; | |
2a6b82aa | 671 | new = malloc((unsigned)(strlen(cp) + 1)); |
21ed1185 | 672 | if (new == (char *)0) { |
6a3125d9 | 673 | syslog(LOG_ERR, "Out of memory."); |
21ed1185 MK |
674 | exit(-1); |
675 | } | |
676 | strcpy(new, cp); | |
677 | return (new); | |
678 | } | |
b1b30605 MK |
679 | |
680 | setproctitle(a, s) | |
681 | char *a; | |
682 | int s; | |
683 | { | |
684 | int size; | |
685 | register char *cp; | |
686 | struct sockaddr_in sin; | |
687 | char buf[80]; | |
688 | ||
689 | cp = Argv[0]; | |
690 | size = sizeof(sin); | |
691 | if (getpeername(s, &sin, &size) == 0) | |
692 | sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr)); | |
693 | else | |
694 | sprintf(buf, "-%s", a); | |
695 | strncpy(cp, buf, LastArg - cp); | |
696 | cp += strlen(cp); | |
697 | while (cp < LastArg) | |
698 | *cp++ = ' '; | |
699 | } | |
700 | ||
701 | /* | |
702 | * Internet services provided internally by inetd: | |
703 | */ | |
704 | ||
705 | /* ARGSUSED */ | |
706 | echo_stream(s, sep) /* Echo service -- echo data back */ | |
707 | int s; | |
708 | struct servtab *sep; | |
709 | { | |
710 | char buffer[BUFSIZ]; | |
711 | int i; | |
712 | ||
0ee95e67 | 713 | setproctitle(sep->se_service, s); |
b1b30605 MK |
714 | while ((i = read(s, buffer, sizeof(buffer))) > 0 && |
715 | write(s, buffer, i) > 0) | |
716 | ; | |
717 | exit(0); | |
718 | } | |
719 | ||
720 | /* ARGSUSED */ | |
721 | echo_dg(s, sep) /* Echo service -- echo data back */ | |
722 | int s; | |
723 | struct servtab *sep; | |
724 | { | |
725 | char buffer[BUFSIZ]; | |
726 | int i, size; | |
727 | struct sockaddr sa; | |
728 | ||
729 | size = sizeof(sa); | |
730 | if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0) | |
731 | return; | |
732 | (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); | |
733 | } | |
734 | ||
735 | /* ARGSUSED */ | |
736 | discard_stream(s, sep) /* Discard service -- ignore data */ | |
737 | int s; | |
738 | struct servtab *sep; | |
739 | { | |
740 | char buffer[BUFSIZ]; | |
741 | ||
0ee95e67 | 742 | setproctitle(sep->se_service, s); |
b1b30605 MK |
743 | while (1) { |
744 | while (read(s, buffer, sizeof(buffer)) > 0) | |
745 | ; | |
746 | if (errno != EINTR) | |
747 | break; | |
748 | } | |
749 | exit(0); | |
750 | } | |
751 | ||
752 | /* ARGSUSED */ | |
753 | discard_dg(s, sep) /* Discard service -- ignore data */ | |
754 | int s; | |
755 | struct servtab *sep; | |
756 | { | |
757 | char buffer[BUFSIZ]; | |
758 | ||
759 | (void) read(s, buffer, sizeof(buffer)); | |
760 | } | |
761 | ||
762 | #include <ctype.h> | |
763 | #define LINESIZ 72 | |
764 | char ring[128]; | |
765 | char *endring; | |
766 | ||
767 | initring() | |
768 | { | |
769 | register int i; | |
770 | ||
771 | endring = ring; | |
772 | ||
773 | for (i = 0; i <= 128; ++i) | |
774 | if (isprint(i)) | |
775 | *endring++ = i; | |
776 | } | |
777 | ||
778 | /* ARGSUSED */ | |
779 | chargen_stream(s, sep) /* Character generator */ | |
780 | int s; | |
781 | struct servtab *sep; | |
782 | { | |
92732149 KB |
783 | register char *rs; |
784 | int len; | |
b1b30605 | 785 | char text[LINESIZ+2]; |
b1b30605 | 786 | |
0ee95e67 | 787 | setproctitle(sep->se_service, s); |
92732149 KB |
788 | |
789 | if (!endring) { | |
b1b30605 | 790 | initring(); |
92732149 KB |
791 | rs = ring; |
792 | } | |
b1b30605 | 793 | |
92732149 KB |
794 | text[LINESIZ] = '\r'; |
795 | text[LINESIZ + 1] = '\n'; | |
796 | for (rs = ring;;) { | |
797 | if ((len = endring - rs) >= LINESIZ) | |
798 | bcopy(rs, text, LINESIZ); | |
799 | else { | |
800 | bcopy(rs, text, len); | |
801 | bcopy(ring, text + len, LINESIZ - len); | |
b1b30605 | 802 | } |
92732149 KB |
803 | if (++rs == endring) |
804 | rs = ring; | |
805 | if (write(s, text, sizeof(text)) != sizeof(text)) | |
b1b30605 MK |
806 | break; |
807 | } | |
808 | exit(0); | |
809 | } | |
810 | ||
811 | /* ARGSUSED */ | |
812 | chargen_dg(s, sep) /* Character generator */ | |
813 | int s; | |
814 | struct servtab *sep; | |
815 | { | |
b1b30605 | 816 | struct sockaddr sa; |
92732149 KB |
817 | static char *rs; |
818 | int len, size; | |
819 | char text[LINESIZ+2]; | |
b1b30605 | 820 | |
92732149 | 821 | if (endring == 0) { |
b1b30605 | 822 | initring(); |
92732149 KB |
823 | rs = ring; |
824 | } | |
b1b30605 MK |
825 | |
826 | size = sizeof(sa); | |
827 | if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0) | |
828 | return; | |
b1b30605 | 829 | |
92732149 KB |
830 | if ((len = endring - rs) >= LINESIZ) |
831 | bcopy(rs, text, LINESIZ); | |
832 | else { | |
833 | bcopy(rs, text, len); | |
834 | bcopy(ring, text + len, LINESIZ - len); | |
835 | } | |
836 | if (++rs == endring) | |
837 | rs = ring; | |
838 | text[LINESIZ] = '\r'; | |
839 | text[LINESIZ + 1] = '\n'; | |
b1b30605 MK |
840 | (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa)); |
841 | } | |
842 | ||
843 | /* | |
844 | * Return a machine readable date and time, in the form of the | |
845 | * number of seconds since midnight, Jan 1, 1900. Since gettimeofday | |
846 | * returns the number of seconds since midnight, Jan 1, 1970, | |
847 | * we must add 2208988800 seconds to this figure to make up for | |
848 | * some seventy years Bell Labs was asleep. | |
849 | */ | |
b1b30605 MK |
850 | |
851 | long | |
852 | machtime() | |
853 | { | |
854 | struct timeval tv; | |
855 | ||
2a6b82aa | 856 | if (gettimeofday(&tv, (struct timezone *)0) < 0) { |
b1b30605 | 857 | fprintf(stderr, "Unable to get time of day\n"); |
2a6b82aa | 858 | return (0L); |
b1b30605 MK |
859 | } |
860 | return (htonl((long)tv.tv_sec + 2208988800)); | |
861 | } | |
862 | ||
863 | /* ARGSUSED */ | |
864 | machtime_stream(s, sep) | |
865 | int s; | |
866 | struct servtab *sep; | |
867 | { | |
868 | long result; | |
869 | ||
870 | result = machtime(); | |
871 | (void) write(s, (char *) &result, sizeof(result)); | |
872 | } | |
873 | ||
874 | /* ARGSUSED */ | |
875 | machtime_dg(s, sep) | |
876 | int s; | |
877 | struct servtab *sep; | |
878 | { | |
879 | long result; | |
880 | struct sockaddr sa; | |
881 | int size; | |
882 | ||
883 | size = sizeof(sa); | |
2a6b82aa | 884 | if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0) |
b1b30605 MK |
885 | return; |
886 | result = machtime(); | |
887 | (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa)); | |
888 | } | |
889 | ||
890 | /* ARGSUSED */ | |
891 | daytime_stream(s, sep) /* Return human-readable time of day */ | |
892 | int s; | |
893 | struct servtab *sep; | |
894 | { | |
895 | char buffer[256]; | |
896 | time_t time(), clock; | |
897 | char *ctime(); | |
898 | ||
899 | clock = time((time_t *) 0); | |
900 | ||
92732149 | 901 | sprintf(buffer, "%.24s\r\n", ctime(&clock)); |
2a6b82aa | 902 | (void) write(s, buffer, strlen(buffer)); |
b1b30605 MK |
903 | } |
904 | ||
905 | /* ARGSUSED */ | |
906 | daytime_dg(s, sep) /* Return human-readable time of day */ | |
907 | int s; | |
908 | struct servtab *sep; | |
909 | { | |
910 | char buffer[256]; | |
911 | time_t time(), clock; | |
912 | struct sockaddr sa; | |
913 | int size; | |
914 | char *ctime(); | |
915 | ||
916 | clock = time((time_t *) 0); | |
917 | ||
918 | size = sizeof(sa); | |
919 | if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) | |
920 | return; | |
92732149 | 921 | sprintf(buffer, "%.24s\r\n", ctime(&clock)); |
b1b30605 MK |
922 | (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa)); |
923 | } | |
9a0fbd5b MK |
924 | |
925 | /* | |
926 | * print_service: | |
927 | * Dump relevant information to stderr | |
928 | */ | |
929 | print_service(action, sep) | |
930 | char *action; | |
931 | struct servtab *sep; | |
932 | { | |
933 | fprintf(stderr, | |
934 | "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n", | |
935 | action, sep->se_service, sep->se_proto, | |
936 | sep->se_wait, sep->se_user, sep->se_bi, sep->se_server); | |
937 | } |