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 |
9a0fbd5b | 14 | static char sccsid[] = "@(#)inetd.c 5.7 (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 | } | |
197 | #endif | |
fc99bd8d MK |
198 | openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); |
199 | bzero((char *)&sv, sizeof(sv)); | |
200 | sv.sv_mask = SIGBLOCK; | |
201 | sv.sv_handler = retry; | |
202 | sigvec(SIGALRM, &sv, (struct sigvec *)0); | |
21ed1185 | 203 | config(); |
fc99bd8d MK |
204 | sv.sv_handler = config; |
205 | sigvec(SIGHUP, &sv, (struct sigvec *)0); | |
206 | sv.sv_handler = reapchild; | |
207 | sigvec(SIGCHLD, &sv, (struct sigvec *)0); | |
208 | ||
21ed1185 | 209 | for (;;) { |
fc99bd8d MK |
210 | int s, ctrl, n; |
211 | fd_set readable; | |
212 | ||
213 | while (nsock == 0) | |
214 | sigpause(0); | |
215 | readable = allsock; | |
216 | if ((n = select(maxsock + 1, &readable, (fd_set *)0, | |
217 | (fd_set *)0, (struct timeval *)0)) <= 0) { | |
218 | if (n < 0 && errno != EINTR) | |
219 | syslog(LOG_WARNING, "select: %m\n"); | |
220 | sleep(1); | |
221 | continue; | |
222 | } | |
223 | for (sep = servtab; n && sep; sep = sep->se_next) | |
224 | if (FD_ISSET(sep->se_fd, &readable)) { | |
225 | n--; | |
21ed1185 MK |
226 | if (debug) |
227 | fprintf(stderr, "someone wants %s\n", sep->se_service); | |
5859fc43 | 228 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { |
fc99bd8d MK |
229 | ctrl = accept(sep->se_fd, (struct sockaddr *)0, |
230 | (int *)0); | |
6a3125d9 RC |
231 | if (debug) |
232 | fprintf(stderr, "accept, ctrl %d\n", ctrl); | |
21ed1185 MK |
233 | if (ctrl < 0) { |
234 | if (errno == EINTR) | |
235 | continue; | |
6a3125d9 | 236 | syslog(LOG_WARNING, "accept: %m"); |
21ed1185 MK |
237 | continue; |
238 | } | |
239 | } else | |
240 | ctrl = sep->se_fd; | |
fc99bd8d | 241 | (void) sigblock(SIGBLOCK); |
b1b30605 | 242 | pid = 0; |
fc99bd8d MK |
243 | dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork); |
244 | if (dofork) { | |
245 | if (sep->se_count++ == 0) | |
246 | (void)gettimeofday(&sep->se_time, | |
247 | (struct timezone *)0); | |
248 | else if (sep->se_count >= TOOMANY) { | |
249 | struct timeval now; | |
250 | ||
251 | (void)gettimeofday(&now, (struct timezone *)0); | |
252 | if (now.tv_sec - sep->se_time.tv_sec > | |
253 | CNT_INTVL) { | |
254 | sep->se_time = now; | |
255 | sep->se_count = 1; | |
256 | } else { | |
257 | syslog(LOG_ERR, | |
258 | "%s/%s server failing (looping), service terminated\n", | |
259 | sep->se_service, sep->se_proto); | |
260 | FD_CLR(sep->se_fd, &allsock); | |
261 | (void) close(sep->se_fd); | |
262 | sep->se_fd = -1; | |
263 | sep->se_count = 0; | |
264 | nsock--; | |
265 | sigsetmask(0); | |
266 | if (!timingout) { | |
267 | timingout = 1; | |
268 | alarm(RETRYTIME); | |
269 | } | |
270 | continue; | |
271 | } | |
272 | } | |
b1b30605 | 273 | pid = fork(); |
fc99bd8d | 274 | } |
21ed1185 | 275 | if (pid < 0) { |
5859fc43 | 276 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) |
21ed1185 | 277 | close(ctrl); |
fc99bd8d | 278 | sigsetmask(0); |
21ed1185 MK |
279 | sleep(1); |
280 | continue; | |
281 | } | |
fc99bd8d | 282 | if (pid && sep->se_wait) { |
21ed1185 | 283 | sep->se_wait = pid; |
fc99bd8d | 284 | FD_CLR(sep->se_fd, &allsock); |
5859fc43 | 285 | nsock--; |
21ed1185 MK |
286 | } |
287 | sigsetmask(0); | |
288 | if (pid == 0) { | |
289 | #ifdef DEBUG | |
fc99bd8d MK |
290 | int tt; |
291 | ||
292 | if (dofork && (tt = open("/dev/tty", O_RDWR)) > 0) { | |
21ed1185 MK |
293 | ioctl(tt, TIOCNOTTY, 0); |
294 | close(tt); | |
295 | } | |
296 | #endif | |
fc99bd8d | 297 | if (dofork) |
b1b30605 MK |
298 | for (i = getdtablesize(); --i > 2; ) |
299 | if (i != ctrl) | |
300 | close(i); | |
301 | if (sep->se_bi) | |
302 | (*sep->se_bi->bi_fn)(ctrl, sep); | |
303 | else { | |
304 | dup2(ctrl, 0); | |
305 | close(ctrl); | |
306 | dup2(0, 1); | |
307 | dup2(0, 2); | |
308 | if ((pwd = getpwnam(sep->se_user)) == NULL) { | |
309 | syslog(LOG_ERR, | |
310 | "getpwnam: %s: No such user", | |
311 | sep->se_user); | |
fc99bd8d MK |
312 | if (sep->se_socktype != SOCK_STREAM) |
313 | recv(0, buf, sizeof (buf), 0); | |
b1b30605 MK |
314 | _exit(1); |
315 | } | |
316 | if (pwd->pw_uid) { | |
2a6b82aa | 317 | (void) setgid((gid_t)pwd->pw_gid); |
b1b30605 | 318 | initgroups(pwd->pw_name, pwd->pw_gid); |
2a6b82aa | 319 | (void) setuid((uid_t)pwd->pw_uid); |
b1b30605 MK |
320 | } |
321 | if (debug) | |
322 | fprintf(stderr, "%d execl %s\n", | |
323 | getpid(), sep->se_server); | |
324 | execv(sep->se_server, sep->se_argv); | |
325 | if (sep->se_socktype != SOCK_STREAM) | |
326 | recv(0, buf, sizeof (buf), 0); | |
327 | syslog(LOG_ERR, "execv %s: %m", sep->se_server); | |
328 | _exit(1); | |
7d5eb6c4 | 329 | } |
21ed1185 | 330 | } |
5859fc43 | 331 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) |
21ed1185 | 332 | close(ctrl); |
fc99bd8d | 333 | } |
21ed1185 MK |
334 | } |
335 | } | |
336 | ||
337 | reapchild() | |
338 | { | |
339 | union wait status; | |
340 | int pid; | |
341 | register struct servtab *sep; | |
342 | ||
343 | for (;;) { | |
2a6b82aa | 344 | pid = wait3(&status, WNOHANG, (struct rusage *)0); |
21ed1185 MK |
345 | if (pid <= 0) |
346 | break; | |
347 | if (debug) | |
348 | fprintf(stderr, "%d reaped\n", pid); | |
349 | for (sep = servtab; sep; sep = sep->se_next) | |
350 | if (sep->se_wait == pid) { | |
351 | if (status.w_status) | |
6a3125d9 RC |
352 | syslog(LOG_WARNING, |
353 | "%s: exit status 0x%x", | |
21ed1185 MK |
354 | sep->se_server, status); |
355 | if (debug) | |
356 | fprintf(stderr, "restored %s, fd %d\n", | |
357 | sep->se_service, sep->se_fd); | |
5859fc43 MK |
358 | FD_SET(sep->se_fd, &allsock); |
359 | nsock++; | |
21ed1185 MK |
360 | sep->se_wait = 1; |
361 | } | |
362 | } | |
363 | } | |
364 | ||
365 | config() | |
366 | { | |
367 | register struct servtab *sep, *cp, **sepp; | |
368 | struct servtab *getconfigent(), *enter(); | |
fc99bd8d | 369 | int omask; |
21ed1185 MK |
370 | |
371 | if (!setconfig()) { | |
6a3125d9 | 372 | syslog(LOG_ERR, "%s: %m", CONFIG); |
21ed1185 MK |
373 | return; |
374 | } | |
375 | for (sep = servtab; sep; sep = sep->se_next) | |
376 | sep->se_checked = 0; | |
377 | while (cp = getconfigent()) { | |
378 | for (sep = servtab; sep; sep = sep->se_next) | |
379 | if (strcmp(sep->se_service, cp->se_service) == 0 && | |
380 | strcmp(sep->se_proto, cp->se_proto) == 0) | |
381 | break; | |
382 | if (sep != 0) { | |
383 | int i; | |
384 | ||
fc99bd8d MK |
385 | omask = sigblock(SIGBLOCK); |
386 | if (cp->se_bi == 0) | |
387 | sep->se_wait = cp->se_wait; | |
21ed1185 | 388 | #define SWAP(a, b) { char *c = a; a = b; b = c; } |
2a6b82aa JL |
389 | if (cp->se_user) |
390 | SWAP(sep->se_user, cp->se_user); | |
21ed1185 MK |
391 | if (cp->se_server) |
392 | SWAP(sep->se_server, cp->se_server); | |
393 | for (i = 0; i < MAXARGV; i++) | |
394 | SWAP(sep->se_argv[i], cp->se_argv[i]); | |
395 | sigsetmask(omask); | |
396 | freeconfig(cp); | |
9a0fbd5b MK |
397 | if (debug) |
398 | print_service("REDO", sep); | |
399 | } else { | |
21ed1185 | 400 | sep = enter(cp); |
9a0fbd5b MK |
401 | if (debug) |
402 | print_service("ADD ", sep); | |
403 | } | |
21ed1185 | 404 | sep->se_checked = 1; |
21ed1185 MK |
405 | sp = getservbyname(sep->se_service, sep->se_proto); |
406 | if (sp == 0) { | |
6a3125d9 | 407 | syslog(LOG_ERR, "%s/%s: unknown service", |
21ed1185 MK |
408 | sep->se_service, sep->se_proto); |
409 | continue; | |
410 | } | |
fc99bd8d MK |
411 | if (sp->s_port != sep->se_ctrladdr.sin_port) { |
412 | sep->se_ctrladdr.sin_port = sp->s_port; | |
413 | if (sep->se_fd != -1) | |
414 | (void) close(sep->se_fd); | |
415 | sep->se_fd = -1; | |
21ed1185 | 416 | } |
fc99bd8d MK |
417 | if (sep->se_fd == -1) |
418 | setup(sep); | |
21ed1185 MK |
419 | } |
420 | endconfig(); | |
421 | /* | |
422 | * Purge anything not looked at above. | |
423 | */ | |
fc99bd8d | 424 | omask = sigblock(SIGBLOCK); |
21ed1185 MK |
425 | sepp = &servtab; |
426 | while (sep = *sepp) { | |
427 | if (sep->se_checked) { | |
428 | sepp = &sep->se_next; | |
429 | continue; | |
430 | } | |
431 | *sepp = sep->se_next; | |
432 | if (sep->se_fd != -1) { | |
5859fc43 MK |
433 | FD_CLR(sep->se_fd, &allsock); |
434 | nsock--; | |
21ed1185 MK |
435 | (void) close(sep->se_fd); |
436 | } | |
9a0fbd5b MK |
437 | if (debug) |
438 | print_service("FREE", sep); | |
21ed1185 MK |
439 | freeconfig(sep); |
440 | free((char *)sep); | |
441 | } | |
442 | (void) sigsetmask(omask); | |
443 | } | |
444 | ||
fc99bd8d MK |
445 | retry() |
446 | { | |
447 | register struct servtab *sep; | |
448 | ||
449 | timingout = 0; | |
450 | for (sep = servtab; sep; sep = sep->se_next) | |
451 | if (sep->se_fd == -1) | |
452 | setup(sep); | |
453 | } | |
454 | ||
455 | setup(sep) | |
456 | register struct servtab *sep; | |
457 | { | |
458 | int on = 1; | |
459 | ||
460 | if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) { | |
461 | syslog(LOG_ERR, "%s/%s: socket: %m", | |
462 | sep->se_service, sep->se_proto); | |
463 | return; | |
464 | } | |
465 | #define turnon(fd, opt) \ | |
466 | setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) | |
467 | if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && | |
468 | turnon(sep->se_fd, SO_DEBUG) < 0) | |
469 | syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); | |
470 | if (turnon(sep->se_fd, SO_REUSEADDR) < 0) | |
471 | syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); | |
472 | #undef turnon | |
473 | if (bind(sep->se_fd, &sep->se_ctrladdr, | |
474 | sizeof (sep->se_ctrladdr)) < 0) { | |
475 | syslog(LOG_ERR, "%s/%s: bind: %m", | |
476 | sep->se_service, sep->se_proto); | |
477 | (void) close(sep->se_fd); | |
478 | sep->se_fd = -1; | |
479 | if (!timingout) { | |
480 | timingout = 1; | |
481 | alarm(RETRYTIME); | |
482 | } | |
483 | return; | |
484 | } | |
485 | if (sep->se_socktype == SOCK_STREAM) | |
486 | listen(sep->se_fd, 10); | |
487 | FD_SET(sep->se_fd, &allsock); | |
488 | nsock++; | |
489 | if (sep->se_fd > maxsock) | |
490 | maxsock = sep->se_fd; | |
491 | } | |
492 | ||
21ed1185 MK |
493 | struct servtab * |
494 | enter(cp) | |
495 | struct servtab *cp; | |
496 | { | |
497 | register struct servtab *sep; | |
5859fc43 | 498 | int omask; |
21ed1185 MK |
499 | char *strdup(); |
500 | ||
501 | sep = (struct servtab *)malloc(sizeof (*sep)); | |
502 | if (sep == (struct servtab *)0) { | |
6a3125d9 | 503 | syslog(LOG_ERR, "Out of memory."); |
21ed1185 MK |
504 | exit(-1); |
505 | } | |
506 | *sep = *cp; | |
507 | sep->se_fd = -1; | |
fc99bd8d | 508 | omask = sigblock(SIGBLOCK); |
21ed1185 MK |
509 | sep->se_next = servtab; |
510 | servtab = sep; | |
511 | sigsetmask(omask); | |
512 | return (sep); | |
513 | } | |
514 | ||
515 | FILE *fconfig = NULL; | |
516 | struct servtab serv; | |
517 | char line[256]; | |
518 | char *skip(), *nextline(); | |
519 | ||
520 | setconfig() | |
521 | { | |
522 | ||
523 | if (fconfig != NULL) { | |
2a6b82aa | 524 | fseek(fconfig, 0L, L_SET); |
21ed1185 MK |
525 | return (1); |
526 | } | |
527 | fconfig = fopen(CONFIG, "r"); | |
528 | return (fconfig != NULL); | |
529 | } | |
530 | ||
531 | endconfig() | |
532 | { | |
533 | ||
534 | if (fconfig == NULL) | |
535 | return; | |
536 | fclose(fconfig); | |
537 | fconfig = NULL; | |
538 | } | |
539 | ||
540 | struct servtab * | |
541 | getconfigent() | |
542 | { | |
543 | register struct servtab *sep = &serv; | |
544 | char *cp, *arg; | |
545 | int argc; | |
546 | ||
b1b30605 | 547 | more: |
21ed1185 MK |
548 | while ((cp = nextline(fconfig)) && *cp == '#') |
549 | ; | |
550 | if (cp == NULL) | |
551 | return ((struct servtab *)0); | |
552 | sep->se_service = strdup(skip(&cp)); | |
553 | arg = skip(&cp); | |
554 | if (strcmp(arg, "stream") == 0) | |
555 | sep->se_socktype = SOCK_STREAM; | |
556 | else if (strcmp(arg, "dgram") == 0) | |
557 | sep->se_socktype = SOCK_DGRAM; | |
558 | else if (strcmp(arg, "rdm") == 0) | |
559 | sep->se_socktype = SOCK_RDM; | |
560 | else if (strcmp(arg, "seqpacket") == 0) | |
561 | sep->se_socktype = SOCK_SEQPACKET; | |
562 | else if (strcmp(arg, "raw") == 0) | |
563 | sep->se_socktype = SOCK_RAW; | |
564 | else | |
565 | sep->se_socktype = -1; | |
566 | sep->se_proto = strdup(skip(&cp)); | |
567 | arg = skip(&cp); | |
568 | sep->se_wait = strcmp(arg, "wait") == 0; | |
7d5eb6c4 | 569 | sep->se_user = strdup(skip(&cp)); |
21ed1185 | 570 | sep->se_server = strdup(skip(&cp)); |
b1b30605 MK |
571 | if (strcmp(sep->se_server, "internal") == 0) { |
572 | register struct biltin *bi; | |
573 | ||
574 | for (bi = biltins; bi->bi_service; bi++) | |
575 | if (bi->bi_socktype == sep->se_socktype && | |
576 | strcmp(bi->bi_service, sep->se_service) == 0) | |
577 | break; | |
578 | if (bi->bi_service == 0) { | |
579 | syslog(LOG_ERR, "internal service %s unknown\n", | |
580 | sep->se_service); | |
581 | goto more; | |
582 | } | |
583 | sep->se_bi = bi; | |
584 | sep->se_wait = bi->bi_wait; | |
9a0fbd5b MK |
585 | } else |
586 | sep->se_bi = NULL; | |
21ed1185 MK |
587 | argc = 0; |
588 | for (arg = skip(&cp); cp; arg = skip(&cp)) | |
589 | if (argc < MAXARGV) | |
590 | sep->se_argv[argc++] = strdup(arg); | |
591 | while (argc <= MAXARGV) | |
592 | sep->se_argv[argc++] = NULL; | |
593 | return (sep); | |
594 | } | |
595 | ||
596 | freeconfig(cp) | |
597 | register struct servtab *cp; | |
598 | { | |
599 | int i; | |
600 | ||
601 | if (cp->se_service) | |
602 | free(cp->se_service); | |
603 | if (cp->se_proto) | |
604 | free(cp->se_proto); | |
2a6b82aa JL |
605 | if (cp->se_user) |
606 | free(cp->se_user); | |
21ed1185 MK |
607 | if (cp->se_server) |
608 | free(cp->se_server); | |
609 | for (i = 0; i < MAXARGV; i++) | |
610 | if (cp->se_argv[i]) | |
611 | free(cp->se_argv[i]); | |
612 | } | |
613 | ||
614 | char * | |
615 | skip(cpp) | |
616 | char **cpp; | |
617 | { | |
618 | register char *cp = *cpp; | |
619 | char *start; | |
620 | ||
621 | again: | |
622 | while (*cp == ' ' || *cp == '\t') | |
623 | cp++; | |
624 | if (*cp == '\0') { | |
625 | char c; | |
626 | ||
627 | c = getc(fconfig); | |
628 | ungetc(c, fconfig); | |
629 | if (c == ' ' || c == '\t') | |
630 | if (cp = nextline(fconfig)) | |
631 | goto again; | |
632 | *cpp = (char *)0; | |
633 | return ((char *)0); | |
634 | } | |
635 | start = cp; | |
636 | while (*cp && *cp != ' ' && *cp != '\t') | |
637 | cp++; | |
638 | if (*cp != '\0') | |
639 | *cp++ = '\0'; | |
640 | *cpp = cp; | |
641 | return (start); | |
642 | } | |
643 | ||
644 | char * | |
645 | nextline(fd) | |
646 | FILE *fd; | |
647 | { | |
648 | char *cp; | |
649 | ||
2a6b82aa | 650 | if (fgets(line, sizeof (line), fd) == NULL) |
21ed1185 MK |
651 | return ((char *)0); |
652 | cp = index(line, '\n'); | |
653 | if (cp) | |
654 | *cp = '\0'; | |
655 | return (line); | |
656 | } | |
657 | ||
658 | char * | |
659 | strdup(cp) | |
660 | char *cp; | |
661 | { | |
662 | char *new; | |
663 | ||
664 | if (cp == NULL) | |
665 | cp = ""; | |
2a6b82aa | 666 | new = malloc((unsigned)(strlen(cp) + 1)); |
21ed1185 | 667 | if (new == (char *)0) { |
6a3125d9 | 668 | syslog(LOG_ERR, "Out of memory."); |
21ed1185 MK |
669 | exit(-1); |
670 | } | |
671 | strcpy(new, cp); | |
672 | return (new); | |
673 | } | |
b1b30605 MK |
674 | |
675 | setproctitle(a, s) | |
676 | char *a; | |
677 | int s; | |
678 | { | |
679 | int size; | |
680 | register char *cp; | |
681 | struct sockaddr_in sin; | |
682 | char buf[80]; | |
683 | ||
684 | cp = Argv[0]; | |
685 | size = sizeof(sin); | |
686 | if (getpeername(s, &sin, &size) == 0) | |
687 | sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr)); | |
688 | else | |
689 | sprintf(buf, "-%s", a); | |
690 | strncpy(cp, buf, LastArg - cp); | |
691 | cp += strlen(cp); | |
692 | while (cp < LastArg) | |
693 | *cp++ = ' '; | |
694 | } | |
695 | ||
696 | /* | |
697 | * Internet services provided internally by inetd: | |
698 | */ | |
699 | ||
700 | /* ARGSUSED */ | |
701 | echo_stream(s, sep) /* Echo service -- echo data back */ | |
702 | int s; | |
703 | struct servtab *sep; | |
704 | { | |
705 | char buffer[BUFSIZ]; | |
706 | int i; | |
707 | ||
708 | setproctitle("echo", s); | |
709 | while ((i = read(s, buffer, sizeof(buffer))) > 0 && | |
710 | write(s, buffer, i) > 0) | |
711 | ; | |
712 | exit(0); | |
713 | } | |
714 | ||
715 | /* ARGSUSED */ | |
716 | echo_dg(s, sep) /* Echo service -- echo data back */ | |
717 | int s; | |
718 | struct servtab *sep; | |
719 | { | |
720 | char buffer[BUFSIZ]; | |
721 | int i, size; | |
722 | struct sockaddr sa; | |
723 | ||
724 | size = sizeof(sa); | |
725 | if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0) | |
726 | return; | |
727 | (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); | |
728 | } | |
729 | ||
730 | /* ARGSUSED */ | |
731 | discard_stream(s, sep) /* Discard service -- ignore data */ | |
732 | int s; | |
733 | struct servtab *sep; | |
734 | { | |
735 | char buffer[BUFSIZ]; | |
736 | ||
737 | setproctitle("discard", s); | |
738 | while (1) { | |
739 | while (read(s, buffer, sizeof(buffer)) > 0) | |
740 | ; | |
741 | if (errno != EINTR) | |
742 | break; | |
743 | } | |
744 | exit(0); | |
745 | } | |
746 | ||
747 | /* ARGSUSED */ | |
748 | discard_dg(s, sep) /* Discard service -- ignore data */ | |
749 | int s; | |
750 | struct servtab *sep; | |
751 | { | |
752 | char buffer[BUFSIZ]; | |
753 | ||
754 | (void) read(s, buffer, sizeof(buffer)); | |
755 | } | |
756 | ||
757 | #include <ctype.h> | |
758 | #define LINESIZ 72 | |
759 | char ring[128]; | |
760 | char *endring; | |
761 | ||
762 | initring() | |
763 | { | |
764 | register int i; | |
765 | ||
766 | endring = ring; | |
767 | ||
768 | for (i = 0; i <= 128; ++i) | |
769 | if (isprint(i)) | |
770 | *endring++ = i; | |
771 | } | |
772 | ||
773 | /* ARGSUSED */ | |
774 | chargen_stream(s, sep) /* Character generator */ | |
775 | int s; | |
776 | struct servtab *sep; | |
777 | { | |
778 | char text[LINESIZ+2]; | |
779 | register int i; | |
780 | register char *rp, *rs, *dp; | |
781 | ||
782 | setproctitle("discard", s); | |
783 | if (endring == 0) | |
784 | initring(); | |
785 | ||
786 | for (rs = ring; ; ++rs) { | |
787 | if (rs >= endring) | |
788 | rs = ring; | |
789 | rp = rs; | |
790 | dp = text; | |
791 | i = MIN(LINESIZ, endring - rp); | |
792 | bcopy(rp, dp, i); | |
793 | dp += i; | |
794 | if ((rp += i) >= endring) | |
795 | rp = ring; | |
796 | if (i < LINESIZ) { | |
797 | i = LINESIZ - i; | |
798 | bcopy(rp, dp, i); | |
799 | dp += i; | |
800 | if ((rp += i) >= endring) | |
801 | rp = ring; | |
802 | } | |
803 | *dp++ = '\r'; | |
804 | *dp++ = '\n'; | |
805 | ||
806 | if (write(s, text, dp - text) != dp - text) | |
807 | break; | |
808 | } | |
809 | exit(0); | |
810 | } | |
811 | ||
812 | /* ARGSUSED */ | |
813 | chargen_dg(s, sep) /* Character generator */ | |
814 | int s; | |
815 | struct servtab *sep; | |
816 | { | |
817 | char text[LINESIZ+2]; | |
818 | register int i; | |
819 | register char *rp; | |
820 | static char *rs = ring; | |
821 | struct sockaddr sa; | |
822 | int size; | |
823 | ||
824 | if (endring == 0) | |
825 | initring(); | |
826 | ||
827 | size = sizeof(sa); | |
828 | if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0) | |
829 | return; | |
830 | rp = rs; | |
831 | if (rs++ >= endring) | |
832 | rs = ring; | |
833 | i = MIN(LINESIZ - 2, endring - rp); | |
834 | bcopy(rp, text, i); | |
835 | if ((rp += i) >= endring) | |
836 | rp = ring; | |
837 | if (i < LINESIZ - 2) { | |
838 | bcopy(rp, text, i); | |
839 | if ((rp += i) >= endring) | |
840 | rp = ring; | |
841 | } | |
842 | text[LINESIZ - 2] = '\r'; | |
843 | text[LINESIZ - 1] = '\n'; | |
844 | ||
845 | (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa)); | |
846 | } | |
847 | ||
848 | /* | |
849 | * Return a machine readable date and time, in the form of the | |
850 | * number of seconds since midnight, Jan 1, 1900. Since gettimeofday | |
851 | * returns the number of seconds since midnight, Jan 1, 1970, | |
852 | * we must add 2208988800 seconds to this figure to make up for | |
853 | * some seventy years Bell Labs was asleep. | |
854 | */ | |
b1b30605 MK |
855 | |
856 | long | |
857 | machtime() | |
858 | { | |
859 | struct timeval tv; | |
860 | ||
2a6b82aa | 861 | if (gettimeofday(&tv, (struct timezone *)0) < 0) { |
b1b30605 | 862 | fprintf(stderr, "Unable to get time of day\n"); |
2a6b82aa | 863 | return (0L); |
b1b30605 MK |
864 | } |
865 | return (htonl((long)tv.tv_sec + 2208988800)); | |
866 | } | |
867 | ||
868 | /* ARGSUSED */ | |
869 | machtime_stream(s, sep) | |
870 | int s; | |
871 | struct servtab *sep; | |
872 | { | |
873 | long result; | |
874 | ||
875 | result = machtime(); | |
876 | (void) write(s, (char *) &result, sizeof(result)); | |
877 | } | |
878 | ||
879 | /* ARGSUSED */ | |
880 | machtime_dg(s, sep) | |
881 | int s; | |
882 | struct servtab *sep; | |
883 | { | |
884 | long result; | |
885 | struct sockaddr sa; | |
886 | int size; | |
887 | ||
888 | size = sizeof(sa); | |
2a6b82aa | 889 | if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0) |
b1b30605 MK |
890 | return; |
891 | result = machtime(); | |
892 | (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa)); | |
893 | } | |
894 | ||
895 | /* ARGSUSED */ | |
896 | daytime_stream(s, sep) /* Return human-readable time of day */ | |
897 | int s; | |
898 | struct servtab *sep; | |
899 | { | |
900 | char buffer[256]; | |
901 | time_t time(), clock; | |
902 | char *ctime(); | |
903 | ||
904 | clock = time((time_t *) 0); | |
905 | ||
906 | sprintf(buffer, "%s\r", ctime(&clock)); | |
2a6b82aa | 907 | (void) write(s, buffer, strlen(buffer)); |
b1b30605 MK |
908 | } |
909 | ||
910 | /* ARGSUSED */ | |
911 | daytime_dg(s, sep) /* Return human-readable time of day */ | |
912 | int s; | |
913 | struct servtab *sep; | |
914 | { | |
915 | char buffer[256]; | |
916 | time_t time(), clock; | |
917 | struct sockaddr sa; | |
918 | int size; | |
919 | char *ctime(); | |
920 | ||
921 | clock = time((time_t *) 0); | |
922 | ||
923 | size = sizeof(sa); | |
924 | if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) | |
925 | return; | |
926 | sprintf(buffer, "%s\r", ctime(&clock)); | |
927 | (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa)); | |
928 | } | |
9a0fbd5b MK |
929 | |
930 | /* | |
931 | * print_service: | |
932 | * Dump relevant information to stderr | |
933 | */ | |
934 | print_service(action, sep) | |
935 | char *action; | |
936 | struct servtab *sep; | |
937 | { | |
938 | fprintf(stderr, | |
939 | "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n", | |
940 | action, sep->se_service, sep->se_proto, | |
941 | sep->se_wait, sep->se_user, sep->se_bi, sep->se_server); | |
942 | } |