diff options
Diffstat (limited to 'networking/inetd.c')
-rw-r--r-- | networking/inetd.c | 1280 |
1 files changed, 1280 insertions, 0 deletions
diff --git a/networking/inetd.c b/networking/inetd.c new file mode 100644 index 000000000..047358c90 --- /dev/null +++ b/networking/inetd.c | |||
@@ -0,0 +1,1280 @@ | |||
1 | /* | ||
2 | * Copyright (c) 1983,1991 The Regents of the University of California. | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * This code is derived from software contributed to Berkeley by | ||
6 | * David A. Holland. | ||
7 | * | ||
8 | * Busybox port by Vladimir Oleynik (C) 2001-2003 <dzo@simtreas.ru> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | /* | ||
27 | * Inetd - Internet super-server | ||
28 | * | ||
29 | * This program invokes all internet services as needed. | ||
30 | * connection-oriented services are invoked each time a | ||
31 | * connection is made, by creating a process. This process | ||
32 | * is passed the connection as file descriptor 0 and is | ||
33 | * expected to do a getpeername to find out the source host | ||
34 | * and port. | ||
35 | * | ||
36 | * Datagram oriented services are invoked when a datagram | ||
37 | * arrives; a process is created and passed a pending message | ||
38 | * on file descriptor 0. Datagram servers may either connect | ||
39 | * to their peer, freeing up the original socket for inetd | ||
40 | * to receive further messages on, or ``take over the socket'', | ||
41 | * processing all arriving datagrams and, eventually, timing | ||
42 | * out. The first type of server is said to be ``multi-threaded''; | ||
43 | * the second type of server ``single-threaded''. | ||
44 | * | ||
45 | * Inetd uses a configuration file which is read at startup | ||
46 | * and, possibly, at some later time in response to a hangup signal. | ||
47 | * The configuration file is ``free format'' with fields given in the | ||
48 | * order shown below. Continuation lines for an entry must being with | ||
49 | * a space or tab. All fields must be present in each entry. | ||
50 | * | ||
51 | * service name must be in /etc/services | ||
52 | * socket type stream/dgram/raw/rdm/seqpacket | ||
53 | * protocol must be in /etc/protocols | ||
54 | * wait/nowait[.max] single-threaded/multi-threaded, max # | ||
55 | * user[.group] user/group to run daemon as | ||
56 | * server program full path name | ||
57 | * server program arguments maximum of MAXARGS (20) | ||
58 | * | ||
59 | * RPC services unsuported | ||
60 | * | ||
61 | * Comment lines are indicated by a `#' in column 1. | ||
62 | */ | ||
63 | |||
64 | /* | ||
65 | * Here's the scoop concerning the user.group feature: | ||
66 | * | ||
67 | * 1) No group listed. | ||
68 | * | ||
69 | * a) for root: NO setuid() or setgid() is done | ||
70 | * | ||
71 | * b) nonroot: setuid() | ||
72 | * setgid(primary group as found in passwd) | ||
73 | * initgroups(name, primary group) | ||
74 | * | ||
75 | * 2) set-group-option on. | ||
76 | * | ||
77 | * a) for root: NO setuid() | ||
78 | * setgid(specified group) | ||
79 | * setgroups(1, specified group) | ||
80 | * | ||
81 | * b) nonroot: setuid() | ||
82 | * setgid(specified group) | ||
83 | * initgroups(name, specified group) | ||
84 | * | ||
85 | * All supplementary groups are discarded at startup in case inetd was | ||
86 | * run manually. | ||
87 | */ | ||
88 | |||
89 | #define __USE_BSD_SIGNAL | ||
90 | |||
91 | #include "busybox.h" | ||
92 | |||
93 | #include <sys/param.h> | ||
94 | #include <sys/stat.h> | ||
95 | #include <sys/ioctl.h> | ||
96 | #include <sys/socket.h> | ||
97 | #include <sys/un.h> | ||
98 | #include <sys/file.h> | ||
99 | #include <sys/wait.h> | ||
100 | #include <sys/time.h> | ||
101 | #include <sys/resource.h> | ||
102 | |||
103 | #ifndef __linux__ | ||
104 | #ifndef RLIMIT_NOFILE | ||
105 | #define RLIMIT_NOFILE RLIMIT_OFILE | ||
106 | #endif | ||
107 | #endif | ||
108 | |||
109 | #include <sys/param.h> | ||
110 | #include <sys/stat.h> | ||
111 | #include <sys/ioctl.h> | ||
112 | #include <sys/socket.h> | ||
113 | #include <sys/file.h> | ||
114 | #include <sys/wait.h> | ||
115 | #include <sys/time.h> | ||
116 | #include <sys/resource.h> | ||
117 | |||
118 | #include <netinet/in.h> | ||
119 | #include <netinet/ip.h> | ||
120 | #include <arpa/inet.h> | ||
121 | |||
122 | #include <errno.h> | ||
123 | #include <signal.h> | ||
124 | #include <netdb.h> | ||
125 | #include <syslog.h> | ||
126 | #include <pwd.h> | ||
127 | #include <grp.h> | ||
128 | #include <stdio.h> | ||
129 | #include <stdlib.h> | ||
130 | #include <string.h> | ||
131 | #include <getopt.h> | ||
132 | #include <unistd.h> | ||
133 | #include <stdarg.h> | ||
134 | |||
135 | #define _PATH_INETDCONF "/etc/inetd.conf" | ||
136 | #define _PATH_INETDPID "/var/run/inetd.pid" | ||
137 | |||
138 | #ifndef MIN | ||
139 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) | ||
140 | #endif | ||
141 | |||
142 | #define TOOMANY 40 /* don't start more than TOOMANY */ | ||
143 | #define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ | ||
144 | #define RETRYTIME (60*10) /* retry after bind or server fail */ | ||
145 | |||
146 | #ifndef OPEN_MAX | ||
147 | #define OPEN_MAX 64 | ||
148 | #endif | ||
149 | |||
150 | |||
151 | /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */ | ||
152 | #define FD_MARGIN (8) | ||
153 | static int rlim_ofile_cur = OPEN_MAX; | ||
154 | |||
155 | #ifdef RLIMIT_NOFILE | ||
156 | static struct rlimit rlim_ofile; | ||
157 | #endif | ||
158 | |||
159 | |||
160 | static struct servtab { | ||
161 | char *se_service; /* name of service */ | ||
162 | int se_socktype; /* type of socket to use */ | ||
163 | int se_family; /* address family */ | ||
164 | char *se_proto; /* protocol used */ | ||
165 | short se_wait; /* single threaded server */ | ||
166 | short se_checked; /* looked at during merge */ | ||
167 | char *se_user; /* user name to run as */ | ||
168 | char *se_group; /* group name to run as */ | ||
169 | #ifndef INETD_UNSUPPORT_BILTIN | ||
170 | const struct biltin *se_bi; /* if built-in, description */ | ||
171 | #endif | ||
172 | char *se_server; /* server program */ | ||
173 | #define MAXARGV 20 | ||
174 | char *se_argv[MAXARGV+1]; /* program arguments */ | ||
175 | int se_fd; /* open descriptor */ | ||
176 | union { | ||
177 | struct sockaddr se_un_ctrladdr; | ||
178 | struct sockaddr_in se_un_ctrladdr_in; | ||
179 | struct sockaddr_un se_un_ctrladdr_un; | ||
180 | } se_un; /* bound address */ | ||
181 | #define se_ctrladdr se_un.se_un_ctrladdr | ||
182 | #define se_ctrladdr_in se_un.se_un_ctrladdr_in | ||
183 | #define se_ctrladdr_un se_un.se_un_ctrladdr_un | ||
184 | int se_ctrladdr_size; | ||
185 | int se_max; /* max # of instances of this service */ | ||
186 | int se_count; /* number started since se_time */ | ||
187 | struct timeval se_time; /* start of se_count */ | ||
188 | struct servtab *se_next; | ||
189 | } *servtab; | ||
190 | |||
191 | /* Length of socket listen queue. Should be per-service probably. */ | ||
192 | static int global_queuelen = 128; | ||
193 | |||
194 | static int nsock, maxsock; | ||
195 | static fd_set allsock; | ||
196 | static int timingout; | ||
197 | static sigset_t blockmask, emptymask; | ||
198 | |||
199 | |||
200 | #define INETD_UNSUPPORT_BILTIN 1 | ||
201 | |||
202 | /* Echo received data */ | ||
203 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO | ||
204 | #undef INETD_UNSUPPORT_BILTIN | ||
205 | static void echo_stream(int, struct servtab *); | ||
206 | static void echo_dg(int, struct servtab *); | ||
207 | #endif | ||
208 | /* Internet /dev/null */ | ||
209 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD | ||
210 | #undef INETD_UNSUPPORT_BILTIN | ||
211 | static void discard_stream(int, struct servtab *); | ||
212 | static void discard_dg(int, struct servtab *); | ||
213 | #endif | ||
214 | /* Return 32 bit time since 1900 */ | ||
215 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME | ||
216 | #undef INETD_UNSUPPORT_BILTIN | ||
217 | static void machtime_stream(int, struct servtab *); | ||
218 | static void machtime_dg(int, struct servtab *); | ||
219 | #endif | ||
220 | /* Return human-readable time */ | ||
221 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME | ||
222 | #undef INETD_UNSUPPORT_BILTIN | ||
223 | static void daytime_stream(int, struct servtab *); | ||
224 | static void daytime_dg(int, struct servtab *); | ||
225 | #endif | ||
226 | /* Familiar character generator */ | ||
227 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN | ||
228 | #undef INETD_UNSUPPORT_BILTIN | ||
229 | static void chargen_stream(int, struct servtab *); | ||
230 | static void chargen_dg(int, struct servtab *); | ||
231 | #endif | ||
232 | |||
233 | #ifndef INETD_UNSUPPORT_BILTIN | ||
234 | struct biltin { | ||
235 | const char *bi_service; /* internally provided service name */ | ||
236 | int bi_socktype; /* type of socket supported */ | ||
237 | short bi_fork; /* 1 if should fork before call */ | ||
238 | short bi_wait; /* 1 if should wait for child */ | ||
239 | void (*bi_fn)(int, struct servtab *); /* fn which performs it */ | ||
240 | }; | ||
241 | |||
242 | static const struct biltin biltins[] = { | ||
243 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO | ||
244 | /* Echo received data */ | ||
245 | { "echo", SOCK_STREAM, 1, 0, echo_stream, }, | ||
246 | { "echo", SOCK_DGRAM, 0, 0, echo_dg, }, | ||
247 | #endif | ||
248 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD | ||
249 | /* Internet /dev/null */ | ||
250 | { "discard", SOCK_STREAM, 1, 0, discard_stream, }, | ||
251 | { "discard", SOCK_DGRAM, 0, 0, discard_dg, }, | ||
252 | #endif | ||
253 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME | ||
254 | /* Return 32 bit time since 1900 */ | ||
255 | { "time", SOCK_STREAM, 0, 0, machtime_stream, }, | ||
256 | { "time", SOCK_DGRAM, 0, 0, machtime_dg, }, | ||
257 | #endif | ||
258 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME | ||
259 | /* Return human-readable time */ | ||
260 | { "daytime", SOCK_STREAM, 0, 0, daytime_stream, }, | ||
261 | { "daytime", SOCK_DGRAM, 0, 0, daytime_dg, }, | ||
262 | #endif | ||
263 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN | ||
264 | /* Familiar character generator */ | ||
265 | { "chargen", SOCK_STREAM, 1, 0, chargen_stream, }, | ||
266 | { "chargen", SOCK_DGRAM, 0, 0, chargen_dg, }, | ||
267 | #endif | ||
268 | { NULL, 0, 0, 0, NULL } | ||
269 | }; | ||
270 | #endif /* INETD_UNSUPPORT_BILTIN */ | ||
271 | |||
272 | #define NUMINT (sizeof(intab) / sizeof(struct inent)) | ||
273 | static const char *CONFIG = _PATH_INETDCONF; | ||
274 | |||
275 | #define BCOPY(s, d, z) memcpy(d, s, z) | ||
276 | |||
277 | static void | ||
278 | syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) | ||
279 | __attribute__ ((noreturn, format (printf, 2, 3))); | ||
280 | |||
281 | static void | ||
282 | syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) | ||
283 | { | ||
284 | char buf[50]; | ||
285 | va_list p; | ||
286 | |||
287 | va_start(p, msg); | ||
288 | vsyslog(LOG_ERR, msg, p); | ||
289 | if (se_socktype != SOCK_STREAM) | ||
290 | recv(0, buf, sizeof (buf), 0); | ||
291 | _exit(1); | ||
292 | } | ||
293 | |||
294 | static FILE *fconfig; | ||
295 | static char line[256]; | ||
296 | |||
297 | static FILE * | ||
298 | setconfig(void) | ||
299 | { | ||
300 | FILE *f = fconfig; | ||
301 | |||
302 | if (f != NULL) { | ||
303 | fseek(f, 0L, L_SET); | ||
304 | } else { | ||
305 | f = fconfig = fopen(CONFIG, "r"); | ||
306 | if(f == NULL) | ||
307 | syslog(LOG_ERR, "%s: %m", CONFIG); | ||
308 | } | ||
309 | return f; | ||
310 | } | ||
311 | |||
312 | static char * | ||
313 | nextline(void) | ||
314 | { | ||
315 | char *cp; | ||
316 | FILE *fd = fconfig; | ||
317 | |||
318 | if (fgets(line, sizeof (line), fd) == NULL) | ||
319 | return ((char *)0); | ||
320 | cp = strchr(line, '\n'); | ||
321 | if (cp) | ||
322 | *cp = '\0'; | ||
323 | return (line); | ||
324 | } | ||
325 | |||
326 | static char * | ||
327 | skip(char **cpp) | ||
328 | { | ||
329 | char *cp = *cpp; | ||
330 | char *start; | ||
331 | |||
332 | if (*cpp == NULL) | ||
333 | return ((char *)0); | ||
334 | |||
335 | again: | ||
336 | while (*cp == ' ' || *cp == '\t') | ||
337 | cp++; | ||
338 | if (*cp == '\0') { | ||
339 | int c; | ||
340 | |||
341 | c = getc(fconfig); | ||
342 | (void) ungetc(c, fconfig); | ||
343 | if (c == ' ' || c == '\t') | ||
344 | if ((cp = nextline()) != NULL) | ||
345 | goto again; | ||
346 | *cpp = NULL; | ||
347 | return NULL; | ||
348 | } | ||
349 | start = cp; | ||
350 | while (*cp && *cp != ' ' && *cp != '\t') | ||
351 | cp++; | ||
352 | if (*cp != '\0') | ||
353 | *cp++ = '\0'; | ||
354 | *cpp = cp; | ||
355 | return (start); | ||
356 | } | ||
357 | |||
358 | static char * | ||
359 | newstr(char *cp) | ||
360 | { | ||
361 | cp = strdup(cp ? cp : ""); | ||
362 | if (cp) | ||
363 | return(cp); | ||
364 | |||
365 | syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m"); | ||
366 | } | ||
367 | |||
368 | |||
369 | static struct servtab * | ||
370 | getconfigent(void) | ||
371 | { | ||
372 | static struct servtab serv; | ||
373 | struct servtab *sep = &serv; | ||
374 | int argc; | ||
375 | char *cp, *arg; | ||
376 | |||
377 | more: | ||
378 | while ((cp = nextline()) && *cp == '#') | ||
379 | ; | ||
380 | if (cp == NULL) | ||
381 | return ((struct servtab *)0); | ||
382 | memset((char *)sep, 0, sizeof *sep); | ||
383 | sep->se_service = newstr(skip(&cp)); | ||
384 | arg = skip(&cp); | ||
385 | if (arg == NULL) | ||
386 | goto more; | ||
387 | |||
388 | if (strcmp(arg, "stream") == 0) | ||
389 | sep->se_socktype = SOCK_STREAM; | ||
390 | else if (strcmp(arg, "dgram") == 0) | ||
391 | sep->se_socktype = SOCK_DGRAM; | ||
392 | else if (strcmp(arg, "rdm") == 0) | ||
393 | sep->se_socktype = SOCK_RDM; | ||
394 | else if (strcmp(arg, "seqpacket") == 0) | ||
395 | sep->se_socktype = SOCK_SEQPACKET; | ||
396 | else if (strcmp(arg, "raw") == 0) | ||
397 | sep->se_socktype = SOCK_RAW; | ||
398 | else | ||
399 | sep->se_socktype = -1; | ||
400 | |||
401 | sep->se_proto = newstr(skip(&cp)); | ||
402 | if (strcmp(sep->se_proto, "unix") == 0) { | ||
403 | sep->se_family = AF_UNIX; | ||
404 | } else { | ||
405 | sep->se_family = AF_INET; | ||
406 | if (strncmp(sep->se_proto, "rpc/", 4) == 0) { | ||
407 | syslog(LOG_ERR, "%s: rpc services not suported", | ||
408 | sep->se_service); | ||
409 | goto more; | ||
410 | } | ||
411 | } | ||
412 | arg = skip(&cp); | ||
413 | if (arg == NULL) | ||
414 | goto more; | ||
415 | { | ||
416 | char *s = strchr(arg, '.'); | ||
417 | if (s) { | ||
418 | *s++ = '\0'; | ||
419 | sep->se_max = atoi(s); | ||
420 | } else | ||
421 | sep->se_max = TOOMANY; | ||
422 | } | ||
423 | sep->se_wait = strcmp(arg, "wait") == 0; | ||
424 | sep->se_user = newstr(skip(&cp)); | ||
425 | sep->se_group = strchr(sep->se_user, '.'); | ||
426 | if (sep->se_group) { | ||
427 | *sep->se_group++ = '\0'; | ||
428 | } | ||
429 | sep->se_server = newstr(skip(&cp)); | ||
430 | if (strcmp(sep->se_server, "internal") == 0) { | ||
431 | #ifndef INETD_UNSUPPORT_BILTIN | ||
432 | const struct biltin *bi; | ||
433 | |||
434 | for (bi = biltins; bi->bi_service; bi++) | ||
435 | if (bi->bi_socktype == sep->se_socktype && | ||
436 | strcmp(bi->bi_service, sep->se_service) == 0) | ||
437 | break; | ||
438 | if (bi->bi_service == 0) { | ||
439 | syslog(LOG_ERR, "internal service %s unknown", | ||
440 | sep->se_service); | ||
441 | goto more; | ||
442 | } | ||
443 | sep->se_bi = bi; | ||
444 | sep->se_wait = bi->bi_wait; | ||
445 | #else | ||
446 | syslog(LOG_ERR, "internal service %s unknown", | ||
447 | sep->se_service); | ||
448 | goto more; | ||
449 | #endif | ||
450 | } else | ||
451 | #ifndef INETD_UNSUPPORT_BILTIN | ||
452 | sep->se_bi = NULL | ||
453 | #endif | ||
454 | ; | ||
455 | argc = 0; | ||
456 | for (arg = skip(&cp); cp; arg = skip(&cp)) { | ||
457 | if (argc < MAXARGV) | ||
458 | sep->se_argv[argc++] = newstr(arg); | ||
459 | } | ||
460 | while (argc <= MAXARGV) | ||
461 | sep->se_argv[argc++] = NULL; | ||
462 | return (sep); | ||
463 | } | ||
464 | |||
465 | static void | ||
466 | freeconfig(struct servtab *cp) | ||
467 | { | ||
468 | int i; | ||
469 | |||
470 | free(cp->se_service); | ||
471 | free(cp->se_proto); | ||
472 | free(cp->se_user); | ||
473 | /* Note: se_group is part of the newstr'ed se_user */ | ||
474 | free(cp->se_server); | ||
475 | for (i = 0; i < MAXARGV; i++) | ||
476 | free(cp->se_argv[i]); | ||
477 | } | ||
478 | |||
479 | #ifndef INETD_UNSUPPORT_BILTIN | ||
480 | static char **Argv; | ||
481 | static char *LastArg; | ||
482 | |||
483 | static void | ||
484 | setproctitle(char *a, int s) | ||
485 | { | ||
486 | size_t size; | ||
487 | char *cp; | ||
488 | struct sockaddr_in sn; | ||
489 | char buf[80]; | ||
490 | |||
491 | cp = Argv[0]; | ||
492 | size = sizeof(sn); | ||
493 | if (getpeername(s, (struct sockaddr *)&sn, &size) == 0) | ||
494 | (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr)); | ||
495 | else | ||
496 | (void) sprintf(buf, "-%s", a); | ||
497 | strncpy(cp, buf, LastArg - cp); | ||
498 | cp += strlen(cp); | ||
499 | while (cp < LastArg) | ||
500 | *cp++ = ' '; | ||
501 | } | ||
502 | #endif /* INETD_UNSUPPORT_BILTIN */ | ||
503 | |||
504 | static struct servtab * | ||
505 | enter(struct servtab *cp) | ||
506 | { | ||
507 | struct servtab *sep; | ||
508 | sigset_t oldmask; | ||
509 | |||
510 | sep = (struct servtab *)malloc(sizeof (*sep)); | ||
511 | if (sep == NULL) { | ||
512 | syslog_err_and_discard_dg(SOCK_STREAM, memory_exhausted); | ||
513 | } | ||
514 | *sep = *cp; | ||
515 | sep->se_fd = -1; | ||
516 | sigprocmask(SIG_BLOCK, &blockmask, &oldmask); | ||
517 | sep->se_next = servtab; | ||
518 | servtab = sep; | ||
519 | sigprocmask(SIG_SETMASK, &oldmask, NULL); | ||
520 | return (sep); | ||
521 | } | ||
522 | |||
523 | static int | ||
524 | bump_nofile(void) | ||
525 | { | ||
526 | #ifdef RLIMIT_NOFILE | ||
527 | |||
528 | #define FD_CHUNK 32 | ||
529 | |||
530 | struct rlimit rl; | ||
531 | |||
532 | if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { | ||
533 | syslog(LOG_ERR, "getrlimit: %m"); | ||
534 | return -1; | ||
535 | } | ||
536 | rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK); | ||
537 | if (rl.rlim_cur <= rlim_ofile_cur) { | ||
538 | syslog(LOG_ERR, | ||
539 | "bump_nofile: cannot extend file limit, max = %d", | ||
540 | rl.rlim_cur); | ||
541 | return -1; | ||
542 | } | ||
543 | |||
544 | if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { | ||
545 | syslog(LOG_ERR, "setrlimit: %m"); | ||
546 | return -1; | ||
547 | } | ||
548 | |||
549 | rlim_ofile_cur = rl.rlim_cur; | ||
550 | return 0; | ||
551 | |||
552 | #else | ||
553 | syslog(LOG_ERR, "bump_nofile: cannot extend file limit"); | ||
554 | return -1; | ||
555 | #endif | ||
556 | } | ||
557 | |||
558 | |||
559 | static void | ||
560 | setup(struct servtab *sep) | ||
561 | { | ||
562 | int on = 1; | ||
563 | |||
564 | if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { | ||
565 | syslog(LOG_ERR, "%s/%s: socket: %m", | ||
566 | sep->se_service, sep->se_proto); | ||
567 | return; | ||
568 | } | ||
569 | if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, | ||
570 | sizeof(on)) < 0) | ||
571 | syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); | ||
572 | if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) { | ||
573 | syslog(LOG_ERR, "%s/%s: bind: %m", | ||
574 | sep->se_service, sep->se_proto); | ||
575 | (void) close(sep->se_fd); | ||
576 | sep->se_fd = -1; | ||
577 | if (!timingout) { | ||
578 | timingout = 1; | ||
579 | alarm(RETRYTIME); | ||
580 | } | ||
581 | return; | ||
582 | } | ||
583 | if (sep->se_socktype == SOCK_STREAM) | ||
584 | listen(sep->se_fd, global_queuelen); | ||
585 | |||
586 | FD_SET(sep->se_fd, &allsock); | ||
587 | nsock++; | ||
588 | if (sep->se_fd > maxsock) { | ||
589 | maxsock = sep->se_fd; | ||
590 | if (maxsock > rlim_ofile_cur - FD_MARGIN) | ||
591 | bump_nofile(); | ||
592 | } | ||
593 | } | ||
594 | |||
595 | static void | ||
596 | config(int signum) | ||
597 | { | ||
598 | struct servtab *sep, *cp, **sepp; | ||
599 | sigset_t oldmask; | ||
600 | unsigned n; | ||
601 | |||
602 | (void)signum; | ||
603 | if (setconfig() == NULL) | ||
604 | return; | ||
605 | |||
606 | for (sep = servtab; sep; sep = sep->se_next) | ||
607 | sep->se_checked = 0; | ||
608 | while ((cp = getconfigent()) != NULL) { | ||
609 | for (sep = servtab; sep; sep = sep->se_next) | ||
610 | if (strcmp(sep->se_service, cp->se_service) == 0 && | ||
611 | strcmp(sep->se_proto, cp->se_proto) == 0) | ||
612 | break; | ||
613 | if (sep != 0) { | ||
614 | int i; | ||
615 | |||
616 | #define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;} | ||
617 | |||
618 | sigprocmask(SIG_BLOCK, &emptymask, &oldmask); | ||
619 | /* | ||
620 | * sep->se_wait may be holding the pid of a daemon | ||
621 | * that we're waiting for. If so, don't overwrite | ||
622 | * it unless the config file explicitly says don't | ||
623 | * wait. | ||
624 | */ | ||
625 | if ( | ||
626 | #ifndef INETD_UNSUPPORT_BILTIN | ||
627 | cp->se_bi == 0 && | ||
628 | #endif | ||
629 | (sep->se_wait == 1 || cp->se_wait == 0)) | ||
630 | sep->se_wait = cp->se_wait; | ||
631 | if (cp->se_max != sep->se_max) | ||
632 | SWAP(int, cp->se_max, sep->se_max); | ||
633 | if (cp->se_user) | ||
634 | SWAP(char *, sep->se_user, cp->se_user); | ||
635 | if (cp->se_group) | ||
636 | SWAP(char *, sep->se_group, cp->se_group); | ||
637 | if (cp->se_server) | ||
638 | SWAP(char *, sep->se_server, cp->se_server); | ||
639 | for (i = 0; i < MAXARGV; i++) | ||
640 | SWAP(char *, sep->se_argv[i], cp->se_argv[i]); | ||
641 | #undef SWAP | ||
642 | sigprocmask(SIG_SETMASK, &oldmask, NULL); | ||
643 | freeconfig(cp); | ||
644 | } else { | ||
645 | sep = enter(cp); | ||
646 | } | ||
647 | sep->se_checked = 1; | ||
648 | |||
649 | switch (sep->se_family) { | ||
650 | case AF_UNIX: | ||
651 | if (sep->se_fd != -1) | ||
652 | break; | ||
653 | (void)unlink(sep->se_service); | ||
654 | n = strlen(sep->se_service); | ||
655 | if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1) | ||
656 | n = sizeof(sep->se_ctrladdr_un.sun_path) - 1; | ||
657 | strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n); | ||
658 | sep->se_ctrladdr_un.sun_family = AF_UNIX; | ||
659 | sep->se_ctrladdr_size = n + | ||
660 | sizeof sep->se_ctrladdr_un.sun_family; | ||
661 | setup(sep); | ||
662 | break; | ||
663 | case AF_INET: | ||
664 | sep->se_ctrladdr_in.sin_family = AF_INET; | ||
665 | sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in; | ||
666 | { | ||
667 | u_short port = htons(atoi(sep->se_service)); | ||
668 | |||
669 | if (!port) { | ||
670 | struct servent *sp; | ||
671 | sp = getservbyname(sep->se_service, | ||
672 | sep->se_proto); | ||
673 | if (sp == 0) { | ||
674 | syslog(LOG_ERR, | ||
675 | "%s/%s: unknown service", | ||
676 | sep->se_service, sep->se_proto); | ||
677 | continue; | ||
678 | } | ||
679 | port = sp->s_port; | ||
680 | } | ||
681 | if (port != sep->se_ctrladdr_in.sin_port) { | ||
682 | sep->se_ctrladdr_in.sin_port = port; | ||
683 | if (sep->se_fd != -1) { | ||
684 | FD_CLR(sep->se_fd, &allsock); | ||
685 | nsock--; | ||
686 | (void) close(sep->se_fd); | ||
687 | } | ||
688 | sep->se_fd = -1; | ||
689 | } | ||
690 | if (sep->se_fd == -1) | ||
691 | setup(sep); | ||
692 | } | ||
693 | } | ||
694 | } | ||
695 | if (fconfig) { | ||
696 | (void) fclose(fconfig); | ||
697 | fconfig = NULL; | ||
698 | } | ||
699 | /* | ||
700 | * Purge anything not looked at above. | ||
701 | */ | ||
702 | sigprocmask(SIG_SETMASK, &blockmask, &oldmask); | ||
703 | sepp = &servtab; | ||
704 | while ((sep = *sepp) != NULL) { | ||
705 | if (sep->se_checked) { | ||
706 | sepp = &sep->se_next; | ||
707 | continue; | ||
708 | } | ||
709 | *sepp = sep->se_next; | ||
710 | if (sep->se_fd != -1) { | ||
711 | FD_CLR(sep->se_fd, &allsock); | ||
712 | nsock--; | ||
713 | (void) close(sep->se_fd); | ||
714 | } | ||
715 | if (sep->se_family == AF_UNIX) | ||
716 | (void)unlink(sep->se_service); | ||
717 | freeconfig(sep); | ||
718 | free((char *)sep); | ||
719 | } | ||
720 | sigprocmask(SIG_SETMASK, &oldmask, NULL); | ||
721 | } | ||
722 | |||
723 | |||
724 | |||
725 | static void | ||
726 | reapchild(int signum) | ||
727 | { | ||
728 | int status; | ||
729 | int pid; | ||
730 | struct servtab *sep; | ||
731 | |||
732 | (void)signum; | ||
733 | for (;;) { | ||
734 | pid = wait3(&status, WNOHANG, (struct rusage *)0); | ||
735 | if (pid <= 0) | ||
736 | break; | ||
737 | for (sep = servtab; sep; sep = sep->se_next) | ||
738 | if (sep->se_wait == pid) { | ||
739 | if (WIFEXITED(status) && WEXITSTATUS(status)) | ||
740 | syslog(LOG_WARNING, | ||
741 | "%s: exit status 0x%x", | ||
742 | sep->se_server, WEXITSTATUS(status)); | ||
743 | else if (WIFSIGNALED(status)) | ||
744 | syslog(LOG_WARNING, | ||
745 | "%s: exit signal 0x%x", | ||
746 | sep->se_server, WTERMSIG(status)); | ||
747 | sep->se_wait = 1; | ||
748 | FD_SET(sep->se_fd, &allsock); | ||
749 | nsock++; | ||
750 | } | ||
751 | } | ||
752 | } | ||
753 | |||
754 | static void | ||
755 | retry(int signum) | ||
756 | { | ||
757 | struct servtab *sep; | ||
758 | |||
759 | (void)signum; | ||
760 | timingout = 0; | ||
761 | for (sep = servtab; sep; sep = sep->se_next) { | ||
762 | if (sep->se_fd == -1) { | ||
763 | switch (sep->se_family) { | ||
764 | case AF_UNIX: | ||
765 | case AF_INET: | ||
766 | setup(sep); | ||
767 | break; | ||
768 | } | ||
769 | } | ||
770 | } | ||
771 | } | ||
772 | |||
773 | static void | ||
774 | goaway(int signum) | ||
775 | { | ||
776 | struct servtab *sep; | ||
777 | |||
778 | (void)signum; | ||
779 | for (sep = servtab; sep; sep = sep->se_next) | ||
780 | if (sep->se_fd != -1 && sep->se_family == AF_UNIX) | ||
781 | (void)unlink(sep->se_service); | ||
782 | (void)unlink(_PATH_INETDPID); | ||
783 | exit(0); | ||
784 | } | ||
785 | |||
786 | |||
787 | |||
788 | extern int | ||
789 | inetd_main(int argc, char *argv[]) | ||
790 | { | ||
791 | struct servtab *sep; | ||
792 | struct passwd *pwd; | ||
793 | struct group *grp = NULL; | ||
794 | struct sigaction sa; | ||
795 | int ch, pid; | ||
796 | gid_t gid; | ||
797 | |||
798 | #ifdef INETD_UNSUPPORT_BILTIN | ||
799 | # define dofork 1 | ||
800 | #else | ||
801 | int dofork; | ||
802 | extern char **environ; | ||
803 | #endif | ||
804 | |||
805 | gid = getgid(); | ||
806 | setgroups(1, &gid); | ||
807 | |||
808 | #ifndef INETD_UNSUPPORT_BILTIN | ||
809 | Argv = argv; | ||
810 | if (environ == 0 || *environ == 0) | ||
811 | environ = argv; | ||
812 | while (*environ) | ||
813 | environ++; | ||
814 | LastArg = environ[-1] + strlen(environ[-1]); | ||
815 | #endif | ||
816 | |||
817 | while ((ch = getopt(argc, argv, "q:")) != EOF) | ||
818 | switch(ch) { | ||
819 | case 'q': | ||
820 | global_queuelen = atoi(optarg); | ||
821 | if (global_queuelen < 8) global_queuelen=8; | ||
822 | break; | ||
823 | default: | ||
824 | show_usage(); // "[-q len] [conf]" | ||
825 | } | ||
826 | argc -= optind; | ||
827 | argv += optind; | ||
828 | |||
829 | if (argc > 0) | ||
830 | CONFIG = argv[0]; | ||
831 | |||
832 | daemon(0, 0); | ||
833 | openlog(applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON); | ||
834 | { | ||
835 | FILE *fp; | ||
836 | |||
837 | if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) { | ||
838 | fprintf(fp, "%u\n", getpid()); | ||
839 | (void)fclose(fp); | ||
840 | } | ||
841 | } | ||
842 | |||
843 | #ifdef RLIMIT_NOFILE | ||
844 | if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { | ||
845 | syslog(LOG_ERR, "getrlimit: %m"); | ||
846 | } else { | ||
847 | rlim_ofile_cur = rlim_ofile.rlim_cur; | ||
848 | if (rlim_ofile_cur == RLIM_INFINITY) /* ! */ | ||
849 | rlim_ofile_cur = OPEN_MAX; | ||
850 | } | ||
851 | #endif | ||
852 | |||
853 | config(0); | ||
854 | |||
855 | sigemptyset(&emptymask); | ||
856 | sigemptyset(&blockmask); | ||
857 | sigaddset(&blockmask, SIGCHLD); | ||
858 | sigaddset(&blockmask, SIGHUP); | ||
859 | sigaddset(&blockmask, SIGALRM); | ||
860 | |||
861 | memset(&sa, 0, sizeof(sa)); | ||
862 | sa.sa_mask = blockmask; | ||
863 | sa.sa_handler = retry; | ||
864 | sigaction(SIGALRM, &sa, NULL); | ||
865 | sa.sa_handler = config; | ||
866 | sigaction(SIGHUP, &sa, NULL); | ||
867 | sa.sa_handler = reapchild; | ||
868 | sigaction(SIGCHLD, &sa, NULL); | ||
869 | sa.sa_handler = goaway; | ||
870 | sigaction(SIGTERM, &sa, NULL); | ||
871 | sa.sa_handler = goaway; | ||
872 | sigaction(SIGINT, &sa, NULL); | ||
873 | sa.sa_handler = SIG_IGN; | ||
874 | sigaction(SIGPIPE, &sa, NULL); | ||
875 | |||
876 | { | ||
877 | /* space for daemons to overwrite environment for ps */ | ||
878 | #define DUMMYSIZE 100 | ||
879 | char dummy[DUMMYSIZE]; | ||
880 | |||
881 | (void)memset(dummy, 'x', DUMMYSIZE - 1); | ||
882 | dummy[DUMMYSIZE - 1] = '\0'; | ||
883 | |||
884 | (void)setenv("inetd_dummy", dummy, 1); | ||
885 | } | ||
886 | |||
887 | for (;;) { | ||
888 | int n, ctrl; | ||
889 | fd_set readable; | ||
890 | |||
891 | if (nsock == 0) { | ||
892 | sigprocmask(SIG_BLOCK, &blockmask, NULL); | ||
893 | while (nsock == 0) | ||
894 | sigsuspend(&emptymask); | ||
895 | sigprocmask(SIG_SETMASK, &emptymask, NULL); | ||
896 | } | ||
897 | readable = allsock; | ||
898 | if ((n = select(maxsock + 1, &readable, (fd_set *)0, | ||
899 | (fd_set *)0, (struct timeval *)0)) <= 0) { | ||
900 | if (n < 0 && errno != EINTR) | ||
901 | syslog(LOG_WARNING, "select: %m"); | ||
902 | sleep(1); | ||
903 | continue; | ||
904 | } | ||
905 | for (sep = servtab; n && sep; sep = sep->se_next) | ||
906 | if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { | ||
907 | n--; | ||
908 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { | ||
909 | /* Fixed AGC */ | ||
910 | fcntl(sep->se_fd, F_SETFL, O_NDELAY); | ||
911 | /* --------- */ | ||
912 | ctrl = accept(sep->se_fd, NULL, NULL); | ||
913 | fcntl(sep->se_fd, F_SETFL, 0); | ||
914 | if (ctrl < 0) { | ||
915 | if (errno == EINTR || errno == EWOULDBLOCK) | ||
916 | continue; | ||
917 | syslog(LOG_WARNING, "accept (for %s): %m", | ||
918 | sep->se_service); | ||
919 | continue; | ||
920 | } | ||
921 | } else | ||
922 | ctrl = sep->se_fd; | ||
923 | sigprocmask(SIG_BLOCK, &blockmask, NULL); | ||
924 | pid = 0; | ||
925 | #ifndef INETD_UNSUPPORT_BILTIN | ||
926 | dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork); | ||
927 | #endif | ||
928 | if (dofork) { | ||
929 | if (sep->se_count++ == 0) | ||
930 | (void)gettimeofday(&sep->se_time, | ||
931 | (struct timezone *)0); | ||
932 | else if (sep->se_count >= sep->se_max) { | ||
933 | struct timeval now; | ||
934 | |||
935 | (void)gettimeofday(&now, (struct timezone *)0); | ||
936 | if (now.tv_sec - sep->se_time.tv_sec > | ||
937 | CNT_INTVL) { | ||
938 | sep->se_time = now; | ||
939 | sep->se_count = 1; | ||
940 | } else { | ||
941 | syslog(LOG_ERR, | ||
942 | "%s/%s server failing (looping), service terminated", | ||
943 | sep->se_service, sep->se_proto); | ||
944 | FD_CLR(sep->se_fd, &allsock); | ||
945 | (void) close(sep->se_fd); | ||
946 | sep->se_fd = -1; | ||
947 | sep->se_count = 0; | ||
948 | nsock--; | ||
949 | sigprocmask(SIG_SETMASK, &emptymask, | ||
950 | NULL); | ||
951 | if (!timingout) { | ||
952 | timingout = 1; | ||
953 | alarm(RETRYTIME); | ||
954 | } | ||
955 | continue; | ||
956 | } | ||
957 | } | ||
958 | pid = fork(); | ||
959 | } | ||
960 | if (pid < 0) { | ||
961 | syslog(LOG_ERR, "fork: %m"); | ||
962 | if (sep->se_socktype == SOCK_STREAM) | ||
963 | close(ctrl); | ||
964 | sigprocmask(SIG_SETMASK, &emptymask, NULL); | ||
965 | sleep(1); | ||
966 | continue; | ||
967 | } | ||
968 | if (pid && sep->se_wait) { | ||
969 | sep->se_wait = pid; | ||
970 | FD_CLR(sep->se_fd, &allsock); | ||
971 | nsock--; | ||
972 | } | ||
973 | sigprocmask(SIG_SETMASK, &emptymask, NULL); | ||
974 | if (pid == 0) { | ||
975 | #ifndef INETD_UNSUPPORT_BILTIN | ||
976 | if (sep->se_bi) | ||
977 | (*sep->se_bi->bi_fn)(ctrl, sep); | ||
978 | else | ||
979 | #endif | ||
980 | { | ||
981 | if ((pwd = getpwnam(sep->se_user)) == NULL) { | ||
982 | syslog_err_and_discard_dg( | ||
983 | sep->se_socktype, | ||
984 | "getpwnam: %s: No such user", | ||
985 | sep->se_user); | ||
986 | } | ||
987 | if (sep->se_group && | ||
988 | (grp = getgrnam(sep->se_group)) == NULL) { | ||
989 | syslog_err_and_discard_dg( | ||
990 | sep->se_socktype, | ||
991 | "getgrnam: %s: No such group", | ||
992 | sep->se_group); | ||
993 | } | ||
994 | /* | ||
995 | * Ok. There are four cases here: | ||
996 | * 1. nonroot user, no group specified | ||
997 | * 2. nonroot user, some group specified | ||
998 | * 3. root user, no group specified | ||
999 | * 4. root user, some group specified | ||
1000 | * In cases 2 and 4 we setgid to the specified | ||
1001 | * group. In cases 1 and 2 we run initgroups | ||
1002 | * to run with the groups of the given user. | ||
1003 | * In case 4 we do setgroups to run with the | ||
1004 | * given group. In case 3 we do nothing. | ||
1005 | */ | ||
1006 | if (pwd->pw_uid) { | ||
1007 | if (sep->se_group) | ||
1008 | pwd->pw_gid = grp->gr_gid; | ||
1009 | setgid((gid_t)pwd->pw_gid); | ||
1010 | initgroups(pwd->pw_name, pwd->pw_gid); | ||
1011 | setuid((uid_t)pwd->pw_uid); | ||
1012 | } else if (sep->se_group) { | ||
1013 | setgid((gid_t)grp->gr_gid); | ||
1014 | setgroups(1, &grp->gr_gid); | ||
1015 | } | ||
1016 | dup2(ctrl, 0); | ||
1017 | close(ctrl); | ||
1018 | dup2(0, 1); | ||
1019 | dup2(0, 2); | ||
1020 | #ifdef RLIMIT_NOFILE | ||
1021 | if (rlim_ofile.rlim_cur != rlim_ofile_cur) { | ||
1022 | if (setrlimit(RLIMIT_NOFILE, | ||
1023 | &rlim_ofile) < 0) | ||
1024 | syslog(LOG_ERR,"setrlimit: %m"); | ||
1025 | } | ||
1026 | #endif | ||
1027 | for (ctrl = rlim_ofile_cur-1; --ctrl > 2; ) | ||
1028 | (void)close(ctrl); | ||
1029 | |||
1030 | memset(&sa, 0, sizeof(sa)); | ||
1031 | sa.sa_handler = SIG_DFL; | ||
1032 | sigaction(SIGPIPE, &sa, NULL); | ||
1033 | |||
1034 | execv(sep->se_server, sep->se_argv); | ||
1035 | syslog_err_and_discard_dg(sep->se_socktype, | ||
1036 | "execv %s: %m", sep->se_server); | ||
1037 | } | ||
1038 | } | ||
1039 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) | ||
1040 | close(ctrl); | ||
1041 | } | ||
1042 | } | ||
1043 | } | ||
1044 | |||
1045 | |||
1046 | /* | ||
1047 | * Internet services provided internally by inetd: | ||
1048 | */ | ||
1049 | #define BUFSIZE 4096 | ||
1050 | |||
1051 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO | ||
1052 | /* Echo service -- echo data back */ | ||
1053 | static void | ||
1054 | echo_stream(int s, struct servtab *sep) | ||
1055 | { | ||
1056 | char buffer[BUFSIZE]; | ||
1057 | int i; | ||
1058 | |||
1059 | setproctitle(sep->se_service, s); | ||
1060 | while ((i = read(s, buffer, sizeof(buffer))) > 0 && | ||
1061 | write(s, buffer, i) > 0) | ||
1062 | ; | ||
1063 | exit(0); | ||
1064 | } | ||
1065 | |||
1066 | /* Echo service -- echo data back */ | ||
1067 | static void | ||
1068 | echo_dg(int s, struct servtab *sep) | ||
1069 | { | ||
1070 | char buffer[BUFSIZE]; | ||
1071 | int i; | ||
1072 | size_t size; | ||
1073 | struct sockaddr sa; | ||
1074 | |||
1075 | (void)sep; | ||
1076 | |||
1077 | size = sizeof(sa); | ||
1078 | if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0) | ||
1079 | return; | ||
1080 | (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); | ||
1081 | } | ||
1082 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */ | ||
1083 | |||
1084 | |||
1085 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD | ||
1086 | /* Discard service -- ignore data */ | ||
1087 | static void | ||
1088 | discard_stream(int s, struct servtab *sep) | ||
1089 | { | ||
1090 | char buffer[BUFSIZE]; | ||
1091 | |||
1092 | setproctitle(sep->se_service, s); | ||
1093 | while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) || | ||
1094 | errno == EINTR) | ||
1095 | ; | ||
1096 | exit(0); | ||
1097 | } | ||
1098 | |||
1099 | /* Discard service -- ignore data */ | ||
1100 | static void | ||
1101 | discard_dg(int s, struct servtab *sep) | ||
1102 | { | ||
1103 | char buffer[BUFSIZE]; | ||
1104 | (void)sep; | ||
1105 | read(s, buffer, sizeof(buffer)); | ||
1106 | } | ||
1107 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */ | ||
1108 | |||
1109 | |||
1110 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN | ||
1111 | #include <ctype.h> | ||
1112 | #define LINESIZ 72 | ||
1113 | static char ring[128]; | ||
1114 | static char *endring; | ||
1115 | |||
1116 | static void | ||
1117 | initring(void) | ||
1118 | { | ||
1119 | int i; | ||
1120 | |||
1121 | endring = ring; | ||
1122 | |||
1123 | for (i = 0; i <= 128; ++i) | ||
1124 | if (isprint(i)) | ||
1125 | *endring++ = i; | ||
1126 | } | ||
1127 | |||
1128 | /* Character generator */ | ||
1129 | static void | ||
1130 | chargen_stream(int s, struct servtab *sep) | ||
1131 | { | ||
1132 | char *rs; | ||
1133 | int len; | ||
1134 | char text[LINESIZ+2]; | ||
1135 | |||
1136 | setproctitle(sep->se_service, s); | ||
1137 | |||
1138 | if (!endring) { | ||
1139 | initring(); | ||
1140 | rs = ring; | ||
1141 | } | ||
1142 | |||
1143 | text[LINESIZ] = '\r'; | ||
1144 | text[LINESIZ + 1] = '\n'; | ||
1145 | for (rs = ring;;) { | ||
1146 | if ((len = endring - rs) >= LINESIZ) | ||
1147 | BCOPY(rs, text, LINESIZ); | ||
1148 | else { | ||
1149 | BCOPY(rs, text, len); | ||
1150 | BCOPY(ring, text + len, LINESIZ - len); | ||
1151 | } | ||
1152 | if (++rs == endring) | ||
1153 | rs = ring; | ||
1154 | if (write(s, text, sizeof(text)) != sizeof(text)) | ||
1155 | break; | ||
1156 | } | ||
1157 | exit(0); | ||
1158 | } | ||
1159 | |||
1160 | /* Character generator */ | ||
1161 | static void | ||
1162 | chargen_dg(int s, struct servtab *sep) | ||
1163 | { | ||
1164 | struct sockaddr sa; | ||
1165 | static char *rs; | ||
1166 | size_t len, size; | ||
1167 | char text[LINESIZ+2]; | ||
1168 | |||
1169 | (void)sep; | ||
1170 | |||
1171 | if (endring == 0) { | ||
1172 | initring(); | ||
1173 | rs = ring; | ||
1174 | } | ||
1175 | |||
1176 | size = sizeof(sa); | ||
1177 | if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0) | ||
1178 | return; | ||
1179 | |||
1180 | if ((len = endring - rs) >= LINESIZ) | ||
1181 | BCOPY(rs, text, LINESIZ); | ||
1182 | else { | ||
1183 | BCOPY(rs, text, len); | ||
1184 | BCOPY(ring, text + len, LINESIZ - len); | ||
1185 | } | ||
1186 | if (++rs == endring) | ||
1187 | rs = ring; | ||
1188 | text[LINESIZ] = '\r'; | ||
1189 | text[LINESIZ + 1] = '\n'; | ||
1190 | (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa)); | ||
1191 | } | ||
1192 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */ | ||
1193 | |||
1194 | |||
1195 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME | ||
1196 | /* | ||
1197 | * Return a machine readable date and time, in the form of the | ||
1198 | * number of seconds since midnight, Jan 1, 1900. Since gettimeofday | ||
1199 | * returns the number of seconds since midnight, Jan 1, 1970, | ||
1200 | * we must add 2208988800 seconds to this figure to make up for | ||
1201 | * some seventy years Bell Labs was asleep. | ||
1202 | */ | ||
1203 | |||
1204 | static long | ||
1205 | machtime(void) | ||
1206 | { | ||
1207 | struct timeval tv; | ||
1208 | |||
1209 | if (gettimeofday(&tv, (struct timezone *)0) < 0) { | ||
1210 | fprintf(stderr, "Unable to get time of day\n"); | ||
1211 | return (0L); | ||
1212 | } | ||
1213 | return (htonl((long)tv.tv_sec + 2208988800UL)); | ||
1214 | } | ||
1215 | |||
1216 | static void | ||
1217 | machtime_stream(int s, struct servtab *sep) | ||
1218 | { | ||
1219 | long result; | ||
1220 | (void)sep; | ||
1221 | |||
1222 | result = machtime(); | ||
1223 | write(s, (char *) &result, sizeof(result)); | ||
1224 | } | ||
1225 | |||
1226 | static void | ||
1227 | machtime_dg(int s, struct servtab *sep) | ||
1228 | { | ||
1229 | long result; | ||
1230 | struct sockaddr sa; | ||
1231 | size_t size; | ||
1232 | (void)sep; | ||
1233 | |||
1234 | size = sizeof(sa); | ||
1235 | if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0) | ||
1236 | return; | ||
1237 | result = machtime(); | ||
1238 | (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa)); | ||
1239 | } | ||
1240 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */ | ||
1241 | |||
1242 | |||
1243 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME | ||
1244 | /* Return human-readable time of day */ | ||
1245 | static int | ||
1246 | human_readable_time_sprintf(char *buffer) | ||
1247 | { | ||
1248 | time_t clocc = time(NULL); | ||
1249 | |||
1250 | return sprintf(buffer, "%.24s\r\n", ctime(&clocc)); | ||
1251 | } | ||
1252 | |||
1253 | static void | ||
1254 | daytime_stream(int s, struct servtab *sep) | ||
1255 | { | ||
1256 | char buffer[256]; | ||
1257 | size_t st = human_readable_time_sprintf(buffer); | ||
1258 | |||
1259 | (void)sep; | ||
1260 | |||
1261 | write(s, buffer, st); | ||
1262 | } | ||
1263 | |||
1264 | /* Return human-readable time of day */ | ||
1265 | static void | ||
1266 | daytime_dg(int s, struct servtab *sep) | ||
1267 | { | ||
1268 | char buffer[256]; | ||
1269 | struct sockaddr sa; | ||
1270 | size_t size; | ||
1271 | |||
1272 | (void)sep; | ||
1273 | |||
1274 | size = sizeof(sa); | ||
1275 | if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) | ||
1276 | return; | ||
1277 | size = human_readable_time_sprintf(buffer); | ||
1278 | sendto(s, buffer, size, 0, &sa, sizeof(sa)); | ||
1279 | } | ||
1280 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */ | ||