diff options
Diffstat (limited to 'networking/inetd.c')
-rw-r--r-- | networking/inetd.c | 1767 |
1 files changed, 1767 insertions, 0 deletions
diff --git a/networking/inetd.c b/networking/inetd.c new file mode 100644 index 000000000..ec7b2e8f7 --- /dev/null +++ b/networking/inetd.c | |||
@@ -0,0 +1,1767 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* $Slackware: inetd.c 1.79s 2001/02/06 13:18:00 volkerdi Exp $ */ | ||
3 | /* $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $ */ | ||
4 | /* $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $ */ | ||
5 | /* Busybox port by Vladimir Oleynik (C) 2001-2005 <dzo@simtreas.ru> */ | ||
6 | /* | ||
7 | * Copyright (c) 1983,1991 The Regents of the University of California. | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * Redistribution and use in source and binary forms, with or without | ||
11 | * modification, are permitted provided that the following conditions | ||
12 | * are met: | ||
13 | * 1. Redistributions of source code must retain the above copyright | ||
14 | * notice, this list of conditions and the following disclaimer. | ||
15 | * 2. Redistributions in binary form must reproduce the above copyright | ||
16 | * notice, this list of conditions and the following disclaimer in the | ||
17 | * documentation and/or other materials provided with the distribution. | ||
18 | * 3. All advertising materials mentioning features or use of this software | ||
19 | * must display the following acknowledgement: | ||
20 | * This product includes software developed by the University of | ||
21 | * California, Berkeley and its contributors. | ||
22 | * 4. Neither the name of the University nor the names of its contributors | ||
23 | * may be used to endorse or promote products derived from this software | ||
24 | * without specific prior written permission. | ||
25 | * | ||
26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
36 | * SUCH DAMAGE. | ||
37 | */ | ||
38 | |||
39 | /* | ||
40 | * Inetd - Internet super-server | ||
41 | * | ||
42 | * This program invokes all internet services as needed. | ||
43 | * connection-oriented services are invoked each time a | ||
44 | * connection is made, by creating a process. This process | ||
45 | * is passed the connection as file descriptor 0 and is | ||
46 | * expected to do a getpeername to find out the source host | ||
47 | * and port. | ||
48 | * | ||
49 | * Datagram oriented services are invoked when a datagram | ||
50 | * arrives; a process is created and passed a pending message | ||
51 | * on file descriptor 0. Datagram servers may either connect | ||
52 | * to their peer, freeing up the original socket for inetd | ||
53 | * to receive further messages on, or ``take over the socket'', | ||
54 | * processing all arriving datagrams and, eventually, timing | ||
55 | * out. The first type of server is said to be ``multi-threaded''; | ||
56 | * the second type of server ``single-threaded''. | ||
57 | * | ||
58 | * Inetd uses a configuration file which is read at startup | ||
59 | * and, possibly, at some later time in response to a hangup signal. | ||
60 | * The configuration file is ``free format'' with fields given in the | ||
61 | * order shown below. Continuation lines for an entry must begin with | ||
62 | * a space or tab. All fields must be present in each entry. | ||
63 | * | ||
64 | * service name must be in /etc/services | ||
65 | * socket type stream/dgram/raw/rdm/seqpacket | ||
66 | * protocol must be in /etc/protocols | ||
67 | * wait/nowait[.max] single-threaded/multi-threaded, max # | ||
68 | * user[.group] or user[:group] user/group to run daemon as | ||
69 | * server program full path name | ||
70 | * server program arguments maximum of MAXARGS (20) | ||
71 | * | ||
72 | * For RPC services | ||
73 | * service name/version must be in /etc/rpc | ||
74 | * socket type stream/dgram/raw/rdm/seqpacket | ||
75 | * protocol must be in /etc/protocols | ||
76 | * wait/nowait[.max] single-threaded/multi-threaded | ||
77 | * user[.group] or user[:group] user to run daemon as | ||
78 | * server program full path name | ||
79 | * server program arguments maximum of MAXARGS (20) | ||
80 | * | ||
81 | * For non-RPC services, the "service name" can be of the form | ||
82 | * hostaddress:servicename, in which case the hostaddress is used | ||
83 | * as the host portion of the address to listen on. If hostaddress | ||
84 | * consists of a single `*' character, INADDR_ANY is used. | ||
85 | * | ||
86 | * A line can also consist of just | ||
87 | * hostaddress: | ||
88 | * where hostaddress is as in the preceding paragraph. Such a line must | ||
89 | * have no further fields; the specified hostaddress is remembered and | ||
90 | * used for all further lines that have no hostaddress specified, | ||
91 | * until the next such line (or EOF). (This is why * is provided to | ||
92 | * allow explicit specification of INADDR_ANY.) A line | ||
93 | * *: | ||
94 | * is implicitly in effect at the beginning of the file. | ||
95 | * | ||
96 | * The hostaddress specifier may (and often will) contain dots; | ||
97 | * the service name must not. | ||
98 | * | ||
99 | * For RPC services, host-address specifiers are accepted and will | ||
100 | * work to some extent; however, because of limitations in the | ||
101 | * portmapper interface, it will not work to try to give more than | ||
102 | * one line for any given RPC service, even if the host-address | ||
103 | * specifiers are different. | ||
104 | * | ||
105 | * Comment lines are indicated by a `#' in column 1. | ||
106 | */ | ||
107 | |||
108 | /* | ||
109 | * Here's the scoop concerning the user[.:]group feature: | ||
110 | * | ||
111 | * 1) set-group-option off. | ||
112 | * | ||
113 | * a) user = root: NO setuid() or setgid() is done | ||
114 | * | ||
115 | * b) other: setgid(primary group as found in passwd) | ||
116 | * initgroups(name, primary group) | ||
117 | * setuid() | ||
118 | * | ||
119 | * 2) set-group-option on. | ||
120 | * | ||
121 | * a) user = root: setgid(specified group) | ||
122 | * NO initgroups() | ||
123 | * NO setuid() | ||
124 | * | ||
125 | * b) other: setgid(specified group) | ||
126 | * initgroups(name, specified group) | ||
127 | * setuid() | ||
128 | * | ||
129 | */ | ||
130 | |||
131 | #include "busybox.h" | ||
132 | #include <syslog.h> | ||
133 | #include <sys/un.h> | ||
134 | |||
135 | //#define CONFIG_FEATURE_INETD_RPC | ||
136 | //#define CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO | ||
137 | //#define CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD | ||
138 | //#define CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME | ||
139 | //#define CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME | ||
140 | //#define CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
141 | //#define CONFIG_FEATURE_IPV6 | ||
142 | |||
143 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
144 | #include <rpc/rpc.h> | ||
145 | #include <rpc/pmap_clnt.h> | ||
146 | #endif | ||
147 | |||
148 | #define _PATH_INETDCONF "/etc/inetd.conf" | ||
149 | #define _PATH_INETDPID "/var/run/inetd.pid" | ||
150 | |||
151 | |||
152 | #define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ | ||
153 | #define RETRYTIME (60*10) /* retry after bind or server fail */ | ||
154 | |||
155 | #ifndef RLIMIT_NOFILE | ||
156 | #define RLIMIT_NOFILE RLIMIT_OFILE | ||
157 | #endif | ||
158 | |||
159 | #ifndef OPEN_MAX | ||
160 | #define OPEN_MAX 64 | ||
161 | #endif | ||
162 | |||
163 | /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */ | ||
164 | #define FD_MARGIN (8) | ||
165 | static rlim_t rlim_ofile_cur = OPEN_MAX; | ||
166 | static struct rlimit rlim_ofile; | ||
167 | |||
168 | |||
169 | /* Check unsupporting builtin */ | ||
170 | #if defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \ | ||
171 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \ | ||
172 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME || \ | ||
173 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME || \ | ||
174 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
175 | # define INETD_FEATURE_ENABLED | ||
176 | #endif | ||
177 | |||
178 | #if defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \ | ||
179 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \ | ||
180 | defined CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
181 | # define INETD_SETPROCTITLE | ||
182 | #endif | ||
183 | |||
184 | typedef struct servtab { | ||
185 | char *se_hostaddr; /* host address to listen on */ | ||
186 | char *se_service; /* name of service */ | ||
187 | int se_socktype; /* type of socket to use */ | ||
188 | int se_family; /* address family */ | ||
189 | char *se_proto; /* protocol used */ | ||
190 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
191 | int se_rpcprog; /* rpc program number */ | ||
192 | int se_rpcversl; /* rpc program lowest version */ | ||
193 | int se_rpcversh; /* rpc program highest version */ | ||
194 | #define isrpcservice(sep) ((sep)->se_rpcversl != 0) | ||
195 | #else | ||
196 | #define isrpcservice(sep) 0 | ||
197 | #endif | ||
198 | pid_t se_wait; /* single threaded server */ | ||
199 | short se_checked; /* looked at during merge */ | ||
200 | char *se_user; /* user name to run as */ | ||
201 | char *se_group; /* group name to run as */ | ||
202 | #ifdef INETD_FEATURE_ENABLED | ||
203 | const struct builtin *se_bi; /* if built-in, description */ | ||
204 | #endif | ||
205 | char *se_server; /* server program */ | ||
206 | #define MAXARGV 20 | ||
207 | char *se_argv[MAXARGV + 1]; /* program arguments */ | ||
208 | int se_fd; /* open descriptor */ | ||
209 | union { | ||
210 | struct sockaddr se_un_ctrladdr; | ||
211 | struct sockaddr_in se_un_ctrladdr_in; | ||
212 | #ifdef CONFIG_FEATURE_IPV6 | ||
213 | struct sockaddr_in6 se_un_ctrladdr_in6; | ||
214 | #endif | ||
215 | struct sockaddr_un se_un_ctrladdr_un; | ||
216 | } se_un; /* bound address */ | ||
217 | #define se_ctrladdr se_un.se_un_ctrladdr | ||
218 | #define se_ctrladdr_in se_un.se_un_ctrladdr_in | ||
219 | #define se_ctrladdr_in6 se_un.se_un_ctrladdr_in6 | ||
220 | #define se_ctrladdr_un se_un.se_un_ctrladdr_un | ||
221 | int se_ctrladdr_size; | ||
222 | int se_max; /* max # of instances of this service */ | ||
223 | int se_count; /* number started since se_time */ | ||
224 | struct timeval se_time; /* start of se_count */ | ||
225 | struct servtab *se_next; | ||
226 | } servtab_t; | ||
227 | |||
228 | static servtab_t *servtab; | ||
229 | |||
230 | #ifdef INETD_FEATURE_ENABLED | ||
231 | struct builtin { | ||
232 | const char *bi_service; /* internally provided service name */ | ||
233 | int bi_socktype; /* type of socket supported */ | ||
234 | short bi_fork; /* 1 if should fork before call */ | ||
235 | short bi_wait; /* 1 if should wait for child */ | ||
236 | void (*bi_fn) (int, servtab_t *); | ||
237 | }; | ||
238 | |||
239 | /* Echo received data */ | ||
240 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO | ||
241 | static void echo_stream(int, servtab_t *); | ||
242 | static void echo_dg(int, servtab_t *); | ||
243 | #endif | ||
244 | /* Internet /dev/null */ | ||
245 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD | ||
246 | static void discard_stream(int, servtab_t *); | ||
247 | static void discard_dg(int, servtab_t *); | ||
248 | #endif | ||
249 | /* Return 32 bit time since 1900 */ | ||
250 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME | ||
251 | static void machtime_stream(int, servtab_t *); | ||
252 | static void machtime_dg(int, servtab_t *); | ||
253 | #endif | ||
254 | /* Return human-readable time */ | ||
255 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME | ||
256 | static void daytime_stream(int, servtab_t *); | ||
257 | static void daytime_dg(int, servtab_t *); | ||
258 | #endif | ||
259 | /* Familiar character generator */ | ||
260 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
261 | static void chargen_stream(int, servtab_t *); | ||
262 | static void chargen_dg(int, servtab_t *); | ||
263 | #endif | ||
264 | |||
265 | static const struct builtin builtins[] = { | ||
266 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO | ||
267 | /* Echo received data */ | ||
268 | {"echo", SOCK_STREAM, 1, 0, echo_stream,}, | ||
269 | {"echo", SOCK_DGRAM, 0, 0, echo_dg,}, | ||
270 | #endif | ||
271 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD | ||
272 | /* Internet /dev/null */ | ||
273 | {"discard", SOCK_STREAM, 1, 0, discard_stream,}, | ||
274 | {"discard", SOCK_DGRAM, 0, 0, discard_dg,}, | ||
275 | #endif | ||
276 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME | ||
277 | /* Return 32 bit time since 1900 */ | ||
278 | {"time", SOCK_STREAM, 0, 0, machtime_stream,}, | ||
279 | {"time", SOCK_DGRAM, 0, 0, machtime_dg,}, | ||
280 | #endif | ||
281 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME | ||
282 | /* Return human-readable time */ | ||
283 | {"daytime", SOCK_STREAM, 0, 0, daytime_stream,}, | ||
284 | {"daytime", SOCK_DGRAM, 0, 0, daytime_dg,}, | ||
285 | #endif | ||
286 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
287 | /* Familiar character generator */ | ||
288 | {"chargen", SOCK_STREAM, 1, 0, chargen_stream,}, | ||
289 | {"chargen", SOCK_DGRAM, 0, 0, chargen_dg,}, | ||
290 | #endif | ||
291 | {NULL, 0, 0, 0, NULL} | ||
292 | }; | ||
293 | #endif /* INETD_FEATURE_ENABLED */ | ||
294 | |||
295 | static int global_queuelen = 128; | ||
296 | static int nsock, maxsock; | ||
297 | static fd_set allsock; | ||
298 | static int toomany; | ||
299 | static int timingout; | ||
300 | static struct servent *sp; | ||
301 | static uid_t uid; | ||
302 | |||
303 | static char *CONFIG = _PATH_INETDCONF; | ||
304 | |||
305 | static FILE *fconfig; | ||
306 | static char line[1024]; | ||
307 | static char *defhost; | ||
308 | |||
309 | /* xstrdup(NULL) returns NULL, but this one | ||
310 | * will return newly-allocated "" if called with NULL arg | ||
311 | * TODO: audit whether this makes any real difference | ||
312 | */ | ||
313 | static char *xxstrdup(char *cp) | ||
314 | { | ||
315 | return xstrdup(cp ? cp : ""); | ||
316 | } | ||
317 | |||
318 | static int setconfig(void) | ||
319 | { | ||
320 | free(defhost); | ||
321 | defhost = xstrdup("*"); | ||
322 | if (fconfig != NULL) { | ||
323 | fseek(fconfig, 0L, SEEK_SET); | ||
324 | return 1; | ||
325 | } | ||
326 | fconfig = fopen(CONFIG, "r"); | ||
327 | return (fconfig != NULL); | ||
328 | } | ||
329 | |||
330 | static void endconfig(void) | ||
331 | { | ||
332 | if (fconfig) { | ||
333 | (void) fclose(fconfig); | ||
334 | fconfig = NULL; | ||
335 | } | ||
336 | free(defhost); | ||
337 | defhost = 0; | ||
338 | } | ||
339 | |||
340 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
341 | static void register_rpc(servtab_t *sep) | ||
342 | { | ||
343 | int n; | ||
344 | struct sockaddr_in ir_sin; | ||
345 | struct protoent *pp; | ||
346 | socklen_t size; | ||
347 | |||
348 | if ((pp = getprotobyname(sep->se_proto + 4)) == NULL) { | ||
349 | bb_perror_msg("%s: getproto", sep->se_proto); | ||
350 | return; | ||
351 | } | ||
352 | size = sizeof ir_sin; | ||
353 | if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) { | ||
354 | bb_perror_msg("%s/%s: getsockname", | ||
355 | sep->se_service, sep->se_proto); | ||
356 | return; | ||
357 | } | ||
358 | |||
359 | for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) { | ||
360 | (void) pmap_unset(sep->se_rpcprog, n); | ||
361 | if (!pmap_set(sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port))) | ||
362 | bb_perror_msg("%s %s: pmap_set: %u %u %u %u", | ||
363 | sep->se_service, sep->se_proto, | ||
364 | sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port)); | ||
365 | } | ||
366 | } | ||
367 | |||
368 | static void unregister_rpc(servtab_t *sep) | ||
369 | { | ||
370 | int n; | ||
371 | |||
372 | for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) { | ||
373 | if (!pmap_unset(sep->se_rpcprog, n)) | ||
374 | bb_error_msg("pmap_unset(%u, %u)", sep->se_rpcprog, n); | ||
375 | } | ||
376 | } | ||
377 | #endif /* CONFIG_FEATURE_INETD_RPC */ | ||
378 | |||
379 | static void freeconfig(servtab_t *cp) | ||
380 | { | ||
381 | int i; | ||
382 | |||
383 | free(cp->se_hostaddr); | ||
384 | free(cp->se_service); | ||
385 | free(cp->se_proto); | ||
386 | free(cp->se_user); | ||
387 | free(cp->se_group); | ||
388 | free(cp->se_server); | ||
389 | for (i = 0; i < MAXARGV; i++) | ||
390 | free(cp->se_argv[i]); | ||
391 | } | ||
392 | |||
393 | static int bump_nofile (void) | ||
394 | { | ||
395 | #define FD_CHUNK 32 | ||
396 | |||
397 | struct rlimit rl; | ||
398 | |||
399 | if (getrlimit (RLIMIT_NOFILE, &rl) < 0) { | ||
400 | bb_perror_msg("getrlimit"); | ||
401 | return -1; | ||
402 | } | ||
403 | rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK); | ||
404 | rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK); | ||
405 | if (rl.rlim_cur <= rlim_ofile_cur) { | ||
406 | bb_error_msg("bump_nofile: cannot extend file limit, max = %d", | ||
407 | (int) rl.rlim_cur); | ||
408 | return -1; | ||
409 | } | ||
410 | |||
411 | if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { | ||
412 | bb_perror_msg("setrlimit"); | ||
413 | return -1; | ||
414 | } | ||
415 | |||
416 | rlim_ofile_cur = rl.rlim_cur; | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static void setup(servtab_t *sep) | ||
421 | { | ||
422 | int r; | ||
423 | |||
424 | sep->se_fd = socket(sep->se_family, sep->se_socktype, 0); | ||
425 | if (sep->se_fd < 0) { | ||
426 | bb_perror_msg("%s/%s: socket", sep->se_service, sep->se_proto); | ||
427 | return; | ||
428 | } | ||
429 | if (setsockopt_reuseaddr(sep->se_fd) < 0) | ||
430 | bb_perror_msg("setsockopt(SO_REUSEADDR)"); | ||
431 | |||
432 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
433 | if (isrpcservice(sep)) { | ||
434 | struct passwd *pwd; | ||
435 | |||
436 | /* | ||
437 | * for RPC services, attempt to use a reserved port | ||
438 | * if they are going to be running as root. | ||
439 | * | ||
440 | * Also, zero out the port for all RPC services; let bind() | ||
441 | * find one. | ||
442 | */ | ||
443 | sep->se_ctrladdr_in.sin_port = 0; | ||
444 | if (sep->se_user && (pwd = getpwnam(sep->se_user)) && | ||
445 | pwd->pw_uid == 0 && uid == 0) | ||
446 | r = bindresvport(sep->se_fd, &sep->se_ctrladdr_in); | ||
447 | else { | ||
448 | r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size); | ||
449 | if (r == 0) { | ||
450 | socklen_t len = sep->se_ctrladdr_size; | ||
451 | int saveerrno = errno; | ||
452 | |||
453 | /* update se_ctrladdr_in.sin_port */ | ||
454 | r = getsockname(sep->se_fd, &sep->se_ctrladdr, &len); | ||
455 | if (r <= 0) | ||
456 | errno = saveerrno; | ||
457 | } | ||
458 | } | ||
459 | } else | ||
460 | #endif | ||
461 | r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size); | ||
462 | if (r < 0) { | ||
463 | bb_perror_msg("%s/%s (%d): bind", | ||
464 | sep->se_service, sep->se_proto, sep->se_ctrladdr.sa_family); | ||
465 | close(sep->se_fd); | ||
466 | sep->se_fd = -1; | ||
467 | if (!timingout) { | ||
468 | timingout = 1; | ||
469 | alarm(RETRYTIME); | ||
470 | } | ||
471 | return; | ||
472 | } | ||
473 | if (sep->se_socktype == SOCK_STREAM) | ||
474 | listen(sep->se_fd, global_queuelen); | ||
475 | |||
476 | FD_SET(sep->se_fd, &allsock); | ||
477 | nsock++; | ||
478 | if (sep->se_fd > maxsock) { | ||
479 | maxsock = sep->se_fd; | ||
480 | if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN) | ||
481 | bump_nofile(); | ||
482 | } | ||
483 | } | ||
484 | |||
485 | static char *nextline(void) | ||
486 | { | ||
487 | char *cp; | ||
488 | FILE *fd = fconfig; | ||
489 | |||
490 | if (fgets(line, sizeof(line), fd) == NULL) | ||
491 | return NULL; | ||
492 | cp = strchr(line, '\n'); | ||
493 | if (cp) | ||
494 | *cp = '\0'; | ||
495 | return line; | ||
496 | } | ||
497 | |||
498 | static char *skip(char **cpp) /* int report; */ | ||
499 | { | ||
500 | char *cp = *cpp; | ||
501 | char *start; | ||
502 | |||
503 | /* erp: */ | ||
504 | if (*cpp == NULL) { | ||
505 | /* if (report) */ | ||
506 | /* bb_error_msg("syntax error in inetd config file"); */ | ||
507 | return NULL; | ||
508 | } | ||
509 | |||
510 | again: | ||
511 | while (*cp == ' ' || *cp == '\t') | ||
512 | cp++; | ||
513 | if (*cp == '\0') { | ||
514 | int c; | ||
515 | |||
516 | c = getc(fconfig); | ||
517 | (void) ungetc(c, fconfig); | ||
518 | if (c == ' ' || c == '\t') | ||
519 | if ((cp = nextline())) | ||
520 | goto again; | ||
521 | *cpp = NULL; | ||
522 | /* goto erp; */ | ||
523 | return NULL; | ||
524 | } | ||
525 | start = cp; | ||
526 | while (*cp && *cp != ' ' && *cp != '\t') | ||
527 | cp++; | ||
528 | if (*cp != '\0') | ||
529 | *cp++ = '\0'; | ||
530 | /* if ((*cpp = cp) == NULL) */ | ||
531 | /* goto erp; */ | ||
532 | |||
533 | *cpp = cp; | ||
534 | return start; | ||
535 | } | ||
536 | |||
537 | static servtab_t *new_servtab(void) | ||
538 | { | ||
539 | return xmalloc(sizeof(servtab_t)); | ||
540 | } | ||
541 | |||
542 | static servtab_t *dupconfig(servtab_t *sep) | ||
543 | { | ||
544 | servtab_t *newtab; | ||
545 | int argc; | ||
546 | |||
547 | newtab = new_servtab(); | ||
548 | memset(newtab, 0, sizeof(servtab_t)); | ||
549 | newtab->se_service = xstrdup(sep->se_service); | ||
550 | newtab->se_socktype = sep->se_socktype; | ||
551 | newtab->se_family = sep->se_family; | ||
552 | newtab->se_proto = xstrdup(sep->se_proto); | ||
553 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
554 | newtab->se_rpcprog = sep->se_rpcprog; | ||
555 | newtab->se_rpcversl = sep->se_rpcversl; | ||
556 | newtab->se_rpcversh = sep->se_rpcversh; | ||
557 | #endif | ||
558 | newtab->se_wait = sep->se_wait; | ||
559 | newtab->se_user = xstrdup(sep->se_user); | ||
560 | newtab->se_group = xstrdup(sep->se_group); | ||
561 | #ifdef INETD_FEATURE_ENABLED | ||
562 | newtab->se_bi = sep->se_bi; | ||
563 | #endif | ||
564 | newtab->se_server = xstrdup(sep->se_server); | ||
565 | |||
566 | for (argc = 0; argc <= MAXARGV; argc++) | ||
567 | newtab->se_argv[argc] = xstrdup(sep->se_argv[argc]); | ||
568 | newtab->se_max = sep->se_max; | ||
569 | |||
570 | return newtab; | ||
571 | } | ||
572 | |||
573 | static servtab_t *getconfigent(void) | ||
574 | { | ||
575 | servtab_t *sep; | ||
576 | int argc; | ||
577 | char *cp, *arg; | ||
578 | char *hostdelim; | ||
579 | servtab_t *nsep; | ||
580 | servtab_t *psep; | ||
581 | |||
582 | sep = new_servtab(); | ||
583 | |||
584 | /* memset(sep, 0, sizeof *sep); */ | ||
585 | more: | ||
586 | /* freeconfig(sep); */ | ||
587 | |||
588 | while ((cp = nextline()) && *cp == '#') /* skip comment line */; | ||
589 | if (cp == NULL) { | ||
590 | /* free(sep); */ | ||
591 | return NULL; | ||
592 | } | ||
593 | |||
594 | memset((char *) sep, 0, sizeof *sep); | ||
595 | arg = skip(&cp); | ||
596 | if (arg == NULL) { | ||
597 | /* A blank line. */ | ||
598 | goto more; | ||
599 | } | ||
600 | |||
601 | /* Check for a host name. */ | ||
602 | hostdelim = strrchr(arg, ':'); | ||
603 | if (hostdelim) { | ||
604 | *hostdelim = '\0'; | ||
605 | sep->se_hostaddr = xstrdup(arg); | ||
606 | arg = hostdelim + 1; | ||
607 | /* | ||
608 | * If the line is of the form `host:', then just change the | ||
609 | * default host for the following lines. | ||
610 | */ | ||
611 | if (*arg == '\0') { | ||
612 | arg = skip(&cp); | ||
613 | if (cp == NULL) { | ||
614 | free(defhost); | ||
615 | defhost = sep->se_hostaddr; | ||
616 | goto more; | ||
617 | } | ||
618 | } | ||
619 | } else | ||
620 | sep->se_hostaddr = xxstrdup(defhost); | ||
621 | |||
622 | sep->se_service = xxstrdup(arg); | ||
623 | arg = skip(&cp); | ||
624 | |||
625 | if (strcmp(arg, "stream") == 0) | ||
626 | sep->se_socktype = SOCK_STREAM; | ||
627 | else if (strcmp(arg, "dgram") == 0) | ||
628 | sep->se_socktype = SOCK_DGRAM; | ||
629 | else if (strcmp(arg, "rdm") == 0) | ||
630 | sep->se_socktype = SOCK_RDM; | ||
631 | else if (strcmp(arg, "seqpacket") == 0) | ||
632 | sep->se_socktype = SOCK_SEQPACKET; | ||
633 | else if (strcmp(arg, "raw") == 0) | ||
634 | sep->se_socktype = SOCK_RAW; | ||
635 | else | ||
636 | sep->se_socktype = -1; | ||
637 | |||
638 | sep->se_proto = xxstrdup(skip(&cp)); | ||
639 | |||
640 | if (strcmp(sep->se_proto, "unix") == 0) { | ||
641 | sep->se_family = AF_UNIX; | ||
642 | } else { | ||
643 | sep->se_family = AF_INET; | ||
644 | if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') | ||
645 | #ifdef CONFIG_FEATURE_IPV6 | ||
646 | sep->se_family = AF_INET6; | ||
647 | #else | ||
648 | bb_error_msg("%s: IPV6 not supported", sep->se_proto); | ||
649 | #endif | ||
650 | if (strncmp(sep->se_proto, "rpc/", 4) == 0) { | ||
651 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
652 | char *p, *ccp; | ||
653 | long l; | ||
654 | |||
655 | p = strchr(sep->se_service, '/'); | ||
656 | if (p == 0) { | ||
657 | bb_error_msg("%s: no rpc version", sep->se_service); | ||
658 | goto more; | ||
659 | } | ||
660 | *p++ = '\0'; | ||
661 | l = strtol(p, &ccp, 0); | ||
662 | if (ccp == p || l < 0 || l > INT_MAX) { | ||
663 | badafterall: | ||
664 | bb_error_msg("%s/%s: bad rpc version", sep->se_service, p); | ||
665 | goto more; | ||
666 | } | ||
667 | sep->se_rpcversl = sep->se_rpcversh = l; | ||
668 | if (*ccp == '-') { | ||
669 | p = ccp + 1; | ||
670 | l = strtol(p, &ccp, 0); | ||
671 | if (ccp == p || l < 0 || l > INT_MAX || l < sep->se_rpcversl || *ccp) | ||
672 | goto badafterall; | ||
673 | sep->se_rpcversh = l; | ||
674 | } else if (*ccp != '\0') | ||
675 | goto badafterall; | ||
676 | #else | ||
677 | bb_error_msg("%s: rpc services not supported", sep->se_service); | ||
678 | #endif | ||
679 | } | ||
680 | } | ||
681 | arg = skip(&cp); | ||
682 | if (arg == NULL) | ||
683 | goto more; | ||
684 | |||
685 | { | ||
686 | char *s = strchr(arg, '.'); | ||
687 | if (s) { | ||
688 | *s++ = '\0'; | ||
689 | sep->se_max = xatoi(s); | ||
690 | } else | ||
691 | sep->se_max = toomany; | ||
692 | } | ||
693 | sep->se_wait = strcmp(arg, "wait") == 0; | ||
694 | /* if ((arg = skip(&cp, 1)) == NULL) */ | ||
695 | /* goto more; */ | ||
696 | sep->se_user = xxstrdup(skip(&cp)); | ||
697 | arg = strchr(sep->se_user, '.'); | ||
698 | if (arg == NULL) | ||
699 | arg = strchr(sep->se_user, ':'); | ||
700 | if (arg) { | ||
701 | *arg++ = '\0'; | ||
702 | sep->se_group = xstrdup(arg); | ||
703 | } | ||
704 | /* if ((arg = skip(&cp, 1)) == NULL) */ | ||
705 | /* goto more; */ | ||
706 | |||
707 | sep->se_server = xxstrdup(skip(&cp)); | ||
708 | if (strcmp(sep->se_server, "internal") == 0) { | ||
709 | #ifdef INETD_FEATURE_ENABLED | ||
710 | const struct builtin *bi; | ||
711 | |||
712 | for (bi = builtins; bi->bi_service; bi++) | ||
713 | if (bi->bi_socktype == sep->se_socktype && | ||
714 | strcmp(bi->bi_service, sep->se_service) == 0) | ||
715 | break; | ||
716 | if (bi->bi_service == 0) { | ||
717 | bb_error_msg("internal service %s unknown", sep->se_service); | ||
718 | goto more; | ||
719 | } | ||
720 | sep->se_bi = bi; | ||
721 | sep->se_wait = bi->bi_wait; | ||
722 | #else | ||
723 | bb_perror_msg("internal service %s unknown", sep->se_service); | ||
724 | goto more; | ||
725 | #endif | ||
726 | } | ||
727 | #ifdef INETD_FEATURE_ENABLED | ||
728 | else | ||
729 | sep->se_bi = NULL; | ||
730 | #endif | ||
731 | argc = 0; | ||
732 | for (arg = skip(&cp); cp; arg = skip(&cp)) { | ||
733 | if (argc < MAXARGV) | ||
734 | sep->se_argv[argc++] = xxstrdup(arg); | ||
735 | } | ||
736 | while (argc <= MAXARGV) | ||
737 | sep->se_argv[argc++] = NULL; | ||
738 | |||
739 | /* | ||
740 | * Now that we've processed the entire line, check if the hostname | ||
741 | * specifier was a comma separated list of hostnames. If so | ||
742 | * we'll make new entries for each address. | ||
743 | */ | ||
744 | while ((hostdelim = strrchr(sep->se_hostaddr, ',')) != NULL) { | ||
745 | nsep = dupconfig(sep); | ||
746 | |||
747 | /* | ||
748 | * NULL terminate the hostname field of the existing entry, | ||
749 | * and make a dup for the new entry. | ||
750 | */ | ||
751 | *hostdelim++ = '\0'; | ||
752 | nsep->se_hostaddr = xstrdup(hostdelim); | ||
753 | |||
754 | nsep->se_next = sep->se_next; | ||
755 | sep->se_next = nsep; | ||
756 | } | ||
757 | |||
758 | nsep = sep; | ||
759 | while (nsep != NULL) { | ||
760 | nsep->se_checked = 1; | ||
761 | if (nsep->se_family == AF_INET) { | ||
762 | if (!strcmp(nsep->se_hostaddr, "*")) | ||
763 | nsep->se_ctrladdr_in.sin_addr.s_addr = INADDR_ANY; | ||
764 | else if (!inet_aton(nsep->se_hostaddr, &nsep->se_ctrladdr_in.sin_addr)) { | ||
765 | struct hostent *hp; | ||
766 | |||
767 | hp = gethostbyname(nsep->se_hostaddr); | ||
768 | if (hp == 0) { | ||
769 | bb_error_msg("%s: unknown host", nsep->se_hostaddr); | ||
770 | nsep->se_checked = 0; | ||
771 | goto skip; | ||
772 | } else if (hp->h_addrtype != AF_INET) { | ||
773 | bb_error_msg("%s: address isn't an Internet " | ||
774 | "address", nsep->se_hostaddr); | ||
775 | nsep->se_checked = 0; | ||
776 | goto skip; | ||
777 | } else { | ||
778 | int i = 1; | ||
779 | |||
780 | memmove(&nsep->se_ctrladdr_in.sin_addr, | ||
781 | hp->h_addr_list[0], sizeof(struct in_addr)); | ||
782 | while (hp->h_addr_list[i] != NULL) { | ||
783 | psep = dupconfig(nsep); | ||
784 | psep->se_hostaddr = xxstrdup(nsep->se_hostaddr); | ||
785 | psep->se_checked = 1; | ||
786 | memmove(&psep->se_ctrladdr_in.sin_addr, | ||
787 | hp->h_addr_list[i], sizeof(struct in_addr)); | ||
788 | psep->se_ctrladdr_size = sizeof(psep->se_ctrladdr_in); | ||
789 | i++; | ||
790 | /* Prepend to list, don't want to look up */ | ||
791 | /* its hostname again. */ | ||
792 | psep->se_next = sep; | ||
793 | sep = psep; | ||
794 | } | ||
795 | } | ||
796 | } | ||
797 | } | ||
798 | /* XXX BUG?: is this skip: label supposed to remain? */ | ||
799 | skip: | ||
800 | nsep = nsep->se_next; | ||
801 | } | ||
802 | |||
803 | /* | ||
804 | * Finally, free any entries which failed the gethostbyname | ||
805 | * check. | ||
806 | */ | ||
807 | psep = NULL; | ||
808 | nsep = sep; | ||
809 | while (nsep != NULL) { | ||
810 | servtab_t *tsep; | ||
811 | |||
812 | if (nsep->se_checked == 0) { | ||
813 | tsep = nsep; | ||
814 | if (psep == NULL) { | ||
815 | sep = nsep->se_next; | ||
816 | nsep = sep; | ||
817 | } else { | ||
818 | nsep = nsep->se_next; | ||
819 | psep->se_next = nsep; | ||
820 | } | ||
821 | freeconfig(tsep); | ||
822 | } else { | ||
823 | nsep->se_checked = 0; | ||
824 | psep = nsep; | ||
825 | nsep = nsep->se_next; | ||
826 | } | ||
827 | } | ||
828 | |||
829 | return sep; | ||
830 | } | ||
831 | |||
832 | #define Block_Using_Signals(m) do { \ | ||
833 | sigemptyset(&m); \ | ||
834 | sigaddset(&m, SIGCHLD); \ | ||
835 | sigaddset(&m, SIGHUP); \ | ||
836 | sigaddset(&m, SIGALRM); \ | ||
837 | sigprocmask(SIG_BLOCK, &m, NULL); \ | ||
838 | } while(0) | ||
839 | |||
840 | static servtab_t *enter(servtab_t *cp) | ||
841 | { | ||
842 | servtab_t *sep; | ||
843 | sigset_t omask; | ||
844 | |||
845 | sep = new_servtab(); | ||
846 | *sep = *cp; | ||
847 | sep->se_fd = -1; | ||
848 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
849 | sep->se_rpcprog = -1; | ||
850 | #endif | ||
851 | Block_Using_Signals(omask); | ||
852 | sep->se_next = servtab; | ||
853 | servtab = sep; | ||
854 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
855 | return sep; | ||
856 | } | ||
857 | |||
858 | static int matchconf(servtab_t *old, servtab_t *new) | ||
859 | { | ||
860 | if (strcmp(old->se_service, new->se_service) != 0) | ||
861 | return 0; | ||
862 | |||
863 | if (strcmp(old->se_hostaddr, new->se_hostaddr) != 0) | ||
864 | return 0; | ||
865 | |||
866 | if (strcmp(old->se_proto, new->se_proto) != 0) | ||
867 | return 0; | ||
868 | |||
869 | /* | ||
870 | * If the new servtab is bound to a specific address, check that the | ||
871 | * old servtab is bound to the same entry. If the new service is not | ||
872 | * bound to a specific address then the check of se_hostaddr above | ||
873 | * is sufficient. | ||
874 | */ | ||
875 | |||
876 | if (old->se_family == AF_INET && new->se_family == AF_INET && | ||
877 | memcmp(&old->se_ctrladdr_in.sin_addr, | ||
878 | &new->se_ctrladdr_in.sin_addr, | ||
879 | sizeof(new->se_ctrladdr_in.sin_addr)) != 0) | ||
880 | return 0; | ||
881 | |||
882 | #ifdef CONFIG_FEATURE_IPV6 | ||
883 | if (old->se_family == AF_INET6 && new->se_family == AF_INET6 && | ||
884 | memcmp(&old->se_ctrladdr_in6.sin6_addr, | ||
885 | &new->se_ctrladdr_in6.sin6_addr, | ||
886 | sizeof(new->se_ctrladdr_in6.sin6_addr)) != 0) | ||
887 | return 0; | ||
888 | #endif | ||
889 | return 1; | ||
890 | } | ||
891 | |||
892 | static void config(int sig ATTRIBUTE_UNUSED) | ||
893 | { | ||
894 | servtab_t *sep, *cp, **sepp; | ||
895 | sigset_t omask; | ||
896 | size_t n; | ||
897 | char protoname[10]; | ||
898 | |||
899 | if (!setconfig()) { | ||
900 | bb_perror_msg("%s", CONFIG); | ||
901 | return; | ||
902 | } | ||
903 | for (sep = servtab; sep; sep = sep->se_next) | ||
904 | sep->se_checked = 0; | ||
905 | cp = getconfigent(); | ||
906 | while (cp != NULL) { | ||
907 | for (sep = servtab; sep; sep = sep->se_next) | ||
908 | if (matchconf(sep, cp)) | ||
909 | break; | ||
910 | |||
911 | if (sep != 0) { | ||
912 | int i; | ||
913 | |||
914 | #define SWAP(type, a, b) do {type c=(type)a; a=(type)b; b=(type)c;} while (0) | ||
915 | |||
916 | Block_Using_Signals(omask); | ||
917 | /* | ||
918 | * sep->se_wait may be holding the pid of a daemon | ||
919 | * that we're waiting for. If so, don't overwrite | ||
920 | * it unless the config file explicitly says don't | ||
921 | * wait. | ||
922 | */ | ||
923 | if ( | ||
924 | #ifdef INETD_FEATURE_ENABLED | ||
925 | cp->se_bi == 0 && | ||
926 | #endif | ||
927 | (sep->se_wait == 1 || cp->se_wait == 0)) | ||
928 | sep->se_wait = cp->se_wait; | ||
929 | SWAP(int, cp->se_max, sep->se_max); | ||
930 | SWAP(char *, sep->se_user, cp->se_user); | ||
931 | SWAP(char *, sep->se_group, cp->se_group); | ||
932 | SWAP(char *, sep->se_server, cp->se_server); | ||
933 | for (i = 0; i < MAXARGV; i++) | ||
934 | SWAP(char *, sep->se_argv[i], cp->se_argv[i]); | ||
935 | #undef SWAP | ||
936 | |||
937 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
938 | if (isrpcservice(sep)) | ||
939 | unregister_rpc(sep); | ||
940 | sep->se_rpcversl = cp->se_rpcversl; | ||
941 | sep->se_rpcversh = cp->se_rpcversh; | ||
942 | #endif | ||
943 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
944 | freeconfig(cp); | ||
945 | } else { | ||
946 | sep = enter(cp); | ||
947 | } | ||
948 | sep->se_checked = 1; | ||
949 | |||
950 | switch (sep->se_family) { | ||
951 | case AF_UNIX: | ||
952 | if (sep->se_fd != -1) | ||
953 | break; | ||
954 | (void) unlink(sep->se_service); | ||
955 | n = strlen(sep->se_service); | ||
956 | if (n > sizeof sep->se_ctrladdr_un.sun_path - 1) | ||
957 | n = sizeof sep->se_ctrladdr_un.sun_path - 1; | ||
958 | safe_strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n + 1); | ||
959 | sep->se_ctrladdr_un.sun_family = AF_UNIX; | ||
960 | sep->se_ctrladdr_size = n + sizeof sep->se_ctrladdr_un.sun_family; | ||
961 | setup(sep); | ||
962 | break; | ||
963 | case AF_INET: | ||
964 | sep->se_ctrladdr_in.sin_family = AF_INET; | ||
965 | /* se_ctrladdr_in was set in getconfigent */ | ||
966 | sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in; | ||
967 | |||
968 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
969 | if (isrpcservice(sep)) { | ||
970 | struct rpcent *rp; | ||
971 | // FIXME: atoi_or_else(str, 0) would be handy here | ||
972 | sep->se_rpcprog = atoi(sep->se_service); | ||
973 | if (sep->se_rpcprog == 0) { | ||
974 | rp = getrpcbyname(sep->se_service); | ||
975 | if (rp == 0) { | ||
976 | bb_error_msg("%s: unknown rpc service", sep->se_service); | ||
977 | goto serv_unknown; | ||
978 | } | ||
979 | sep->se_rpcprog = rp->r_number; | ||
980 | } | ||
981 | if (sep->se_fd == -1) | ||
982 | setup(sep); | ||
983 | if (sep->se_fd != -1) | ||
984 | register_rpc(sep); | ||
985 | } else | ||
986 | #endif | ||
987 | { | ||
988 | u_short port = htons(atoi(sep->se_service)); | ||
989 | // FIXME: atoi_or_else(str, 0) would be handy here | ||
990 | if (!port) { | ||
991 | /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname)); | ||
992 | if (isdigit(protoname[strlen(protoname) - 1])) | ||
993 | protoname[strlen(protoname) - 1] = '\0'; | ||
994 | sp = getservbyname(sep->se_service, protoname); | ||
995 | if (sp == 0) { | ||
996 | bb_error_msg("%s/%s: unknown service", | ||
997 | sep->se_service, sep->se_proto); | ||
998 | goto serv_unknown; | ||
999 | } | ||
1000 | port = sp->s_port; | ||
1001 | } | ||
1002 | if (port != sep->se_ctrladdr_in.sin_port) { | ||
1003 | sep->se_ctrladdr_in.sin_port = port; | ||
1004 | if (sep->se_fd != -1) { | ||
1005 | FD_CLR(sep->se_fd, &allsock); | ||
1006 | nsock--; | ||
1007 | (void) close(sep->se_fd); | ||
1008 | } | ||
1009 | sep->se_fd = -1; | ||
1010 | } | ||
1011 | if (sep->se_fd == -1) | ||
1012 | setup(sep); | ||
1013 | } | ||
1014 | break; | ||
1015 | #ifdef CONFIG_FEATURE_IPV6 | ||
1016 | case AF_INET6: | ||
1017 | sep->se_ctrladdr_in6.sin6_family = AF_INET6; | ||
1018 | /* se_ctrladdr_in was set in getconfigent */ | ||
1019 | sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in6; | ||
1020 | |||
1021 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
1022 | if (isrpcservice(sep)) { | ||
1023 | struct rpcent *rp; | ||
1024 | |||
1025 | sep->se_rpcprog = atoi(sep->se_service); | ||
1026 | if (sep->se_rpcprog == 0) { | ||
1027 | rp = getrpcbyname(sep->se_service); | ||
1028 | if (rp == 0) { | ||
1029 | bb_error_msg("%s: unknown rpc service", sep->se_service); | ||
1030 | goto serv_unknown; | ||
1031 | } | ||
1032 | sep->se_rpcprog = rp->r_number; | ||
1033 | } | ||
1034 | if (sep->se_fd == -1) | ||
1035 | setup(sep); | ||
1036 | if (sep->se_fd != -1) | ||
1037 | register_rpc(sep); | ||
1038 | } else | ||
1039 | #endif | ||
1040 | { | ||
1041 | u_short port = htons(atoi(sep->se_service)); | ||
1042 | |||
1043 | if (!port) { | ||
1044 | /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname)); | ||
1045 | if (isdigit(protoname[strlen(protoname) - 1])) | ||
1046 | protoname[strlen(protoname) - 1] = '\0'; | ||
1047 | sp = getservbyname(sep->se_service, protoname); | ||
1048 | if (sp == 0) { | ||
1049 | bb_error_msg("%s/%s: unknown service", | ||
1050 | sep->se_service, sep->se_proto); | ||
1051 | goto serv_unknown; | ||
1052 | } | ||
1053 | port = sp->s_port; | ||
1054 | } | ||
1055 | if (port != sep->se_ctrladdr_in6.sin6_port) { | ||
1056 | sep->se_ctrladdr_in6.sin6_port = port; | ||
1057 | if (sep->se_fd != -1) { | ||
1058 | FD_CLR(sep->se_fd, &allsock); | ||
1059 | nsock--; | ||
1060 | (void) close(sep->se_fd); | ||
1061 | } | ||
1062 | sep->se_fd = -1; | ||
1063 | } | ||
1064 | if (sep->se_fd == -1) | ||
1065 | setup(sep); | ||
1066 | } | ||
1067 | break; | ||
1068 | #endif /* CONFIG_FEATURE_IPV6 */ | ||
1069 | } | ||
1070 | serv_unknown: | ||
1071 | if (cp->se_next != NULL) { | ||
1072 | servtab_t *tmp = cp; | ||
1073 | |||
1074 | cp = cp->se_next; | ||
1075 | free(tmp); | ||
1076 | } else { | ||
1077 | free(cp); | ||
1078 | cp = getconfigent(); | ||
1079 | } | ||
1080 | } | ||
1081 | endconfig(); | ||
1082 | /* | ||
1083 | * Purge anything not looked at above. | ||
1084 | */ | ||
1085 | Block_Using_Signals(omask); | ||
1086 | sepp = &servtab; | ||
1087 | while ((sep = *sepp)) { | ||
1088 | if (sep->se_checked) { | ||
1089 | sepp = &sep->se_next; | ||
1090 | continue; | ||
1091 | } | ||
1092 | *sepp = sep->se_next; | ||
1093 | if (sep->se_fd != -1) { | ||
1094 | FD_CLR(sep->se_fd, &allsock); | ||
1095 | nsock--; | ||
1096 | (void) close(sep->se_fd); | ||
1097 | } | ||
1098 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
1099 | if (isrpcservice(sep)) | ||
1100 | unregister_rpc(sep); | ||
1101 | #endif | ||
1102 | if (sep->se_family == AF_UNIX) | ||
1103 | (void) unlink(sep->se_service); | ||
1104 | freeconfig(sep); | ||
1105 | free(sep); | ||
1106 | } | ||
1107 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
1108 | } | ||
1109 | |||
1110 | |||
1111 | static void reapchild(int sig ATTRIBUTE_UNUSED) | ||
1112 | { | ||
1113 | pid_t pid; | ||
1114 | int save_errno = errno, status; | ||
1115 | servtab_t *sep; | ||
1116 | |||
1117 | for (;;) { | ||
1118 | pid = wait3(&status, WNOHANG, NULL); | ||
1119 | if (pid <= 0) | ||
1120 | break; | ||
1121 | for (sep = servtab; sep; sep = sep->se_next) | ||
1122 | if (sep->se_wait == pid) { | ||
1123 | if (WIFEXITED(status) && WEXITSTATUS(status)) | ||
1124 | bb_error_msg("%s: exit status 0x%x", | ||
1125 | sep->se_server, WEXITSTATUS(status)); | ||
1126 | else if (WIFSIGNALED(status)) | ||
1127 | bb_error_msg("%s: exit signal 0x%x", | ||
1128 | sep->se_server, WTERMSIG(status)); | ||
1129 | sep->se_wait = 1; | ||
1130 | FD_SET(sep->se_fd, &allsock); | ||
1131 | nsock++; | ||
1132 | } | ||
1133 | } | ||
1134 | errno = save_errno; | ||
1135 | } | ||
1136 | |||
1137 | static void retry(int sig ATTRIBUTE_UNUSED) | ||
1138 | { | ||
1139 | servtab_t *sep; | ||
1140 | |||
1141 | timingout = 0; | ||
1142 | for (sep = servtab; sep; sep = sep->se_next) { | ||
1143 | if (sep->se_fd == -1) { | ||
1144 | switch (sep->se_family) { | ||
1145 | case AF_UNIX: | ||
1146 | case AF_INET: | ||
1147 | #ifdef CONFIG_FEATURE_IPV6 | ||
1148 | case AF_INET6: | ||
1149 | #endif | ||
1150 | setup(sep); | ||
1151 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
1152 | if (sep->se_fd != -1 && isrpcservice(sep)) | ||
1153 | register_rpc(sep); | ||
1154 | #endif | ||
1155 | break; | ||
1156 | } | ||
1157 | } | ||
1158 | } | ||
1159 | } | ||
1160 | |||
1161 | static void goaway(int sig ATTRIBUTE_UNUSED) | ||
1162 | { | ||
1163 | servtab_t *sep; | ||
1164 | |||
1165 | /* XXX signal race walking sep list */ | ||
1166 | for (sep = servtab; sep; sep = sep->se_next) { | ||
1167 | if (sep->se_fd == -1) | ||
1168 | continue; | ||
1169 | |||
1170 | switch (sep->se_family) { | ||
1171 | case AF_UNIX: | ||
1172 | (void) unlink(sep->se_service); | ||
1173 | break; | ||
1174 | case AF_INET: | ||
1175 | #ifdef CONFIG_FEATURE_IPV6 | ||
1176 | case AF_INET6: | ||
1177 | #endif | ||
1178 | #ifdef CONFIG_FEATURE_INETD_RPC | ||
1179 | if (sep->se_wait == 1 && isrpcservice(sep)) | ||
1180 | unregister_rpc(sep); /* XXX signal race */ | ||
1181 | #endif | ||
1182 | break; | ||
1183 | } | ||
1184 | (void) close(sep->se_fd); | ||
1185 | } | ||
1186 | (void) unlink(_PATH_INETDPID); | ||
1187 | exit(0); | ||
1188 | } | ||
1189 | |||
1190 | |||
1191 | #ifdef INETD_SETPROCTITLE | ||
1192 | static char **Argv; | ||
1193 | static char *LastArg; | ||
1194 | |||
1195 | static void | ||
1196 | inetd_setproctitle(char *a, int s) | ||
1197 | { | ||
1198 | socklen_t size; | ||
1199 | char *cp; | ||
1200 | struct sockaddr_in prt_sin; | ||
1201 | char buf[80]; | ||
1202 | |||
1203 | cp = Argv[0]; | ||
1204 | size = sizeof(prt_sin); | ||
1205 | (void) snprintf(buf, sizeof buf, "-%s", a); | ||
1206 | if (getpeername(s, (struct sockaddr *) &prt_sin, &size) == 0) { | ||
1207 | char *sa = inet_ntoa(prt_sin.sin_addr); | ||
1208 | |||
1209 | buf[sizeof(buf) - 1 - strlen(sa) - 3] = '\0'; | ||
1210 | strcat(buf, " ["); | ||
1211 | strcat(buf, sa); | ||
1212 | strcat(buf, "]"); | ||
1213 | } | ||
1214 | strncpy(cp, buf, LastArg - cp); | ||
1215 | cp += strlen(cp); | ||
1216 | while (cp < LastArg) | ||
1217 | *cp++ = ' '; | ||
1218 | } | ||
1219 | #endif | ||
1220 | |||
1221 | |||
1222 | int | ||
1223 | inetd_main(int argc, char *argv[]) | ||
1224 | { | ||
1225 | servtab_t *sep; | ||
1226 | struct passwd *pwd; | ||
1227 | struct group *grp = NULL; | ||
1228 | int tmpint; | ||
1229 | struct sigaction sa, sapipe; | ||
1230 | int opt; | ||
1231 | pid_t pid; | ||
1232 | char buf[50]; | ||
1233 | char *stoomany; | ||
1234 | sigset_t omask, wait_mask; | ||
1235 | |||
1236 | #ifdef INETD_SETPROCTITLE | ||
1237 | extern char **environ; | ||
1238 | char **envp = environ; | ||
1239 | |||
1240 | Argv = argv; | ||
1241 | if (envp == 0 || *envp == 0) | ||
1242 | envp = argv; | ||
1243 | while (*envp) | ||
1244 | envp++; | ||
1245 | LastArg = envp[-1] + strlen(envp[-1]); | ||
1246 | #endif | ||
1247 | |||
1248 | openlog(applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON); | ||
1249 | |||
1250 | opt = getopt32(argc, argv, "R:f", &stoomany); | ||
1251 | if(opt & 1) { | ||
1252 | toomany = xatoi_u(stoomany); | ||
1253 | } | ||
1254 | argc -= optind; | ||
1255 | argv += optind; | ||
1256 | |||
1257 | uid = getuid(); | ||
1258 | if (uid != 0) | ||
1259 | CONFIG = NULL; | ||
1260 | if (argc > 0) | ||
1261 | CONFIG = argv[0]; | ||
1262 | if (CONFIG == NULL) | ||
1263 | bb_error_msg_and_die("non-root must specify a config file"); | ||
1264 | |||
1265 | if (!(opt & 2)) { | ||
1266 | #ifdef BB_NOMMU | ||
1267 | /* reexec for vfork() do continue parent */ | ||
1268 | vfork_daemon_rexec(0, 0, argc, argv, "-f"); | ||
1269 | #else | ||
1270 | xdaemon(0, 0); | ||
1271 | #endif | ||
1272 | } else { | ||
1273 | setsid(); | ||
1274 | } | ||
1275 | logmode = LOGMODE_SYSLOG; | ||
1276 | |||
1277 | if (uid == 0) { | ||
1278 | gid_t gid = getgid(); | ||
1279 | |||
1280 | /* If run by hand, ensure groups vector gets trashed */ | ||
1281 | setgroups(1, &gid); | ||
1282 | } | ||
1283 | |||
1284 | { | ||
1285 | FILE *fp = fopen(_PATH_INETDPID, "w"); | ||
1286 | |||
1287 | if (fp != NULL) { | ||
1288 | fprintf(fp, "%u\n", getpid()); | ||
1289 | (void) fclose(fp); | ||
1290 | } | ||
1291 | } | ||
1292 | |||
1293 | if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { | ||
1294 | bb_perror_msg("getrlimit"); | ||
1295 | } else { | ||
1296 | rlim_ofile_cur = rlim_ofile.rlim_cur; | ||
1297 | if (rlim_ofile_cur == RLIM_INFINITY) /* ! */ | ||
1298 | rlim_ofile_cur = OPEN_MAX; | ||
1299 | } | ||
1300 | |||
1301 | memset((char *) &sa, 0, sizeof(sa)); | ||
1302 | sigemptyset(&sa.sa_mask); | ||
1303 | sigaddset(&sa.sa_mask, SIGALRM); | ||
1304 | sigaddset(&sa.sa_mask, SIGCHLD); | ||
1305 | sigaddset(&sa.sa_mask, SIGHUP); | ||
1306 | sa.sa_handler = retry; | ||
1307 | sigaction(SIGALRM, &sa, NULL); | ||
1308 | config(SIGHUP); | ||
1309 | sa.sa_handler = config; | ||
1310 | sigaction(SIGHUP, &sa, NULL); | ||
1311 | sa.sa_handler = reapchild; | ||
1312 | sigaction(SIGCHLD, &sa, NULL); | ||
1313 | sa.sa_handler = goaway; | ||
1314 | sigaction(SIGTERM, &sa, NULL); | ||
1315 | sa.sa_handler = goaway; | ||
1316 | sigaction(SIGINT, &sa, NULL); | ||
1317 | sa.sa_handler = SIG_IGN; | ||
1318 | sigaction(SIGPIPE, &sa, &sapipe); | ||
1319 | memset(&wait_mask, 0, sizeof(wait_mask)); | ||
1320 | { | ||
1321 | /* space for daemons to overwrite environment for ps */ | ||
1322 | #define DUMMYSIZE 100 | ||
1323 | char dummy[DUMMYSIZE]; | ||
1324 | |||
1325 | (void) memset(dummy, 'x', DUMMYSIZE - 1); | ||
1326 | dummy[DUMMYSIZE - 1] = '\0'; | ||
1327 | |||
1328 | (void) setenv("inetd_dummy", dummy, 1); | ||
1329 | } | ||
1330 | |||
1331 | for (;;) { | ||
1332 | int n, ctrl = -1; | ||
1333 | fd_set readable; | ||
1334 | |||
1335 | if (nsock == 0) { | ||
1336 | Block_Using_Signals(omask); | ||
1337 | while (nsock == 0) | ||
1338 | sigsuspend(&wait_mask); | ||
1339 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
1340 | } | ||
1341 | |||
1342 | readable = allsock; | ||
1343 | n = select(maxsock + 1, &readable, NULL, NULL, NULL); | ||
1344 | if (n <= 0) { | ||
1345 | if (n < 0 && errno != EINTR) { | ||
1346 | bb_perror_msg("select"); | ||
1347 | sleep(1); | ||
1348 | } | ||
1349 | continue; | ||
1350 | } | ||
1351 | |||
1352 | for (sep = servtab; n && sep; sep = sep->se_next) { | ||
1353 | if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable)) | ||
1354 | continue; | ||
1355 | |||
1356 | n--; | ||
1357 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { | ||
1358 | ctrl = accept(sep->se_fd, NULL, NULL); | ||
1359 | if (ctrl < 0) { | ||
1360 | if (errno == EINTR) | ||
1361 | continue; | ||
1362 | bb_perror_msg("accept (for %s)", sep->se_service); | ||
1363 | continue; | ||
1364 | } | ||
1365 | if (sep->se_family == AF_INET && sep->se_socktype == SOCK_STREAM) { | ||
1366 | struct sockaddr_in peer; | ||
1367 | socklen_t plen = sizeof(peer); | ||
1368 | |||
1369 | if (getpeername(ctrl, (struct sockaddr *) &peer, &plen) < 0) { | ||
1370 | bb_error_msg("cannot getpeername"); | ||
1371 | close(ctrl); | ||
1372 | continue; | ||
1373 | } | ||
1374 | if (ntohs(peer.sin_port) == 20) { | ||
1375 | /* XXX ftp bounce */ | ||
1376 | close(ctrl); | ||
1377 | continue; | ||
1378 | } | ||
1379 | } | ||
1380 | } else | ||
1381 | ctrl = sep->se_fd; | ||
1382 | |||
1383 | Block_Using_Signals(omask); | ||
1384 | pid = 0; | ||
1385 | #ifdef INETD_FEATURE_ENABLED | ||
1386 | if (sep->se_bi == 0 || sep->se_bi->bi_fork) | ||
1387 | #endif | ||
1388 | { | ||
1389 | if (sep->se_count++ == 0) | ||
1390 | (void) gettimeofday(&sep->se_time, NULL); | ||
1391 | else if (toomany > 0 && sep->se_count >= sep->se_max) { | ||
1392 | struct timeval now; | ||
1393 | |||
1394 | (void) gettimeofday(&now, NULL); | ||
1395 | if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) { | ||
1396 | sep->se_time = now; | ||
1397 | sep->se_count = 1; | ||
1398 | } else { | ||
1399 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) | ||
1400 | close(ctrl); | ||
1401 | if (sep->se_family == AF_INET && | ||
1402 | ntohs(sep->se_ctrladdr_in.sin_port) >= IPPORT_RESERVED) { | ||
1403 | /* | ||
1404 | * Cannot close it -- there are | ||
1405 | * thieves on the system. | ||
1406 | * Simply ignore the connection. | ||
1407 | */ | ||
1408 | --sep->se_count; | ||
1409 | continue; | ||
1410 | } | ||
1411 | bb_error_msg("%s/%s server failing (looping), service terminated", | ||
1412 | sep->se_service, sep->se_proto); | ||
1413 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) | ||
1414 | close(ctrl); | ||
1415 | FD_CLR(sep->se_fd, &allsock); | ||
1416 | (void) close(sep->se_fd); | ||
1417 | sep->se_fd = -1; | ||
1418 | sep->se_count = 0; | ||
1419 | nsock--; | ||
1420 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
1421 | if (!timingout) { | ||
1422 | timingout = 1; | ||
1423 | alarm(RETRYTIME); | ||
1424 | } | ||
1425 | continue; | ||
1426 | } | ||
1427 | } | ||
1428 | pid = fork(); | ||
1429 | } | ||
1430 | if (pid < 0) { | ||
1431 | bb_perror_msg("fork"); | ||
1432 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) | ||
1433 | close(ctrl); | ||
1434 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
1435 | sleep(1); | ||
1436 | continue; | ||
1437 | } | ||
1438 | if (pid && sep->se_wait) { | ||
1439 | sep->se_wait = pid; | ||
1440 | FD_CLR(sep->se_fd, &allsock); | ||
1441 | nsock--; | ||
1442 | } | ||
1443 | sigprocmask(SIG_UNBLOCK, &omask, NULL); | ||
1444 | if (pid == 0) { | ||
1445 | #ifdef INETD_FEATURE_ENABLED | ||
1446 | if (sep->se_bi) { | ||
1447 | (*sep->se_bi->bi_fn)(ctrl, sep); | ||
1448 | } else | ||
1449 | #endif | ||
1450 | { | ||
1451 | pwd = getpwnam(sep->se_user); | ||
1452 | if (pwd == NULL) { | ||
1453 | bb_error_msg("getpwnam: %s: no such user", sep->se_user); | ||
1454 | goto do_exit1; | ||
1455 | } | ||
1456 | if (setsid() < 0) | ||
1457 | bb_perror_msg("%s: setsid", sep->se_service); | ||
1458 | if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) { | ||
1459 | bb_error_msg("getgrnam: %s: no such group", sep->se_group); | ||
1460 | goto do_exit1; | ||
1461 | } | ||
1462 | if (uid != 0) { | ||
1463 | /* a user running private inetd */ | ||
1464 | if (uid != pwd->pw_uid) | ||
1465 | _exit(1); | ||
1466 | } else if (pwd->pw_uid) { | ||
1467 | if (sep->se_group) | ||
1468 | pwd->pw_gid = grp->gr_gid; | ||
1469 | xsetgid((gid_t) pwd->pw_gid); | ||
1470 | initgroups(pwd->pw_name, pwd->pw_gid); | ||
1471 | xsetuid((uid_t) pwd->pw_uid); | ||
1472 | } else if (sep->se_group) { | ||
1473 | xsetgid(grp->gr_gid); | ||
1474 | setgroups(1, &grp->gr_gid); | ||
1475 | } | ||
1476 | dup2(ctrl, 0); | ||
1477 | if (ctrl) close(ctrl); | ||
1478 | dup2(0, 1); | ||
1479 | dup2(0, 2); | ||
1480 | if (rlim_ofile.rlim_cur != rlim_ofile_cur) | ||
1481 | if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) | ||
1482 | bb_perror_msg("setrlimit"); | ||
1483 | closelog(); | ||
1484 | for (tmpint = rlim_ofile_cur - 1; --tmpint > 2;) | ||
1485 | (void) close(tmpint); | ||
1486 | sigaction(SIGPIPE, &sapipe, NULL); | ||
1487 | execv(sep->se_server, sep->se_argv); | ||
1488 | bb_perror_msg("execv %s", sep->se_server); | ||
1489 | do_exit1: | ||
1490 | if (sep->se_socktype != SOCK_STREAM) | ||
1491 | recv(0, buf, sizeof(buf), 0); | ||
1492 | _exit(1); | ||
1493 | } | ||
1494 | } | ||
1495 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) | ||
1496 | close(ctrl); | ||
1497 | } /* for (sep = servtab...) */ | ||
1498 | } /* for(;;) */ | ||
1499 | } | ||
1500 | |||
1501 | /* | ||
1502 | * Internet services provided internally by inetd: | ||
1503 | */ | ||
1504 | #define BUFSIZE 4096 | ||
1505 | |||
1506 | #if defined(CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO) || \ | ||
1507 | defined(CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN) || \ | ||
1508 | defined(CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME) | ||
1509 | static int dg_badinput(struct sockaddr_in *dg_sin) | ||
1510 | { | ||
1511 | if (ntohs(dg_sin->sin_port) < IPPORT_RESERVED) | ||
1512 | return 1; | ||
1513 | if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST)) | ||
1514 | return 1; | ||
1515 | /* XXX compare against broadcast addresses in SIOCGIFCONF list? */ | ||
1516 | return 0; | ||
1517 | } | ||
1518 | #endif | ||
1519 | |||
1520 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO | ||
1521 | /* Echo service -- echo data back */ | ||
1522 | /* ARGSUSED */ | ||
1523 | static void | ||
1524 | echo_stream(int s, servtab_t *sep) | ||
1525 | { | ||
1526 | char buffer[BUFSIZE]; | ||
1527 | int i; | ||
1528 | |||
1529 | inetd_setproctitle(sep->se_service, s); | ||
1530 | while (1) { | ||
1531 | i = read(s, buffer, sizeof(buffer)); | ||
1532 | if (i <= 0) break; | ||
1533 | /* FIXME: this isnt correct - safe_write()? */ | ||
1534 | if (write(s, buffer, i) <= 0) break; | ||
1535 | } | ||
1536 | exit(0); | ||
1537 | } | ||
1538 | |||
1539 | /* Echo service -- echo data back */ | ||
1540 | /* ARGSUSED */ | ||
1541 | static void | ||
1542 | echo_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1543 | { | ||
1544 | char buffer[BUFSIZE]; | ||
1545 | int i; | ||
1546 | socklen_t size; | ||
1547 | /* struct sockaddr_storage ss; */ | ||
1548 | struct sockaddr sa; | ||
1549 | |||
1550 | size = sizeof(sa); | ||
1551 | i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size); | ||
1552 | if (i < 0) | ||
1553 | return; | ||
1554 | if (dg_badinput((struct sockaddr_in *) &sa)) | ||
1555 | return; | ||
1556 | (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); | ||
1557 | } | ||
1558 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO */ | ||
1559 | |||
1560 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD | ||
1561 | /* Discard service -- ignore data */ | ||
1562 | /* ARGSUSED */ | ||
1563 | static void | ||
1564 | discard_stream(int s, servtab_t *sep) | ||
1565 | { | ||
1566 | char buffer[BUFSIZE]; | ||
1567 | |||
1568 | inetd_setproctitle(sep->se_service, s); | ||
1569 | while (1) { | ||
1570 | errno = 0; | ||
1571 | if (read(s, buffer, sizeof(buffer)) <= 0 && errno != EINTR) | ||
1572 | exit(0); | ||
1573 | } | ||
1574 | } | ||
1575 | |||
1576 | /* Discard service -- ignore data */ | ||
1577 | /* ARGSUSED */ | ||
1578 | static void | ||
1579 | discard_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1580 | { | ||
1581 | char buffer[BUFSIZE]; | ||
1582 | |||
1583 | (void) read(s, buffer, sizeof(buffer)); | ||
1584 | } | ||
1585 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */ | ||
1586 | |||
1587 | |||
1588 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN | ||
1589 | #define LINESIZ 72 | ||
1590 | static char ring[128]; | ||
1591 | static char *endring; | ||
1592 | |||
1593 | static void | ||
1594 | initring(void) | ||
1595 | { | ||
1596 | int i; | ||
1597 | |||
1598 | endring = ring; | ||
1599 | |||
1600 | for (i = 0; i <= 128; ++i) | ||
1601 | if (isprint(i)) | ||
1602 | *endring++ = i; | ||
1603 | } | ||
1604 | |||
1605 | /* Character generator */ | ||
1606 | /* ARGSUSED */ | ||
1607 | static void | ||
1608 | chargen_stream(int s, servtab_t *sep) | ||
1609 | { | ||
1610 | char *rs; | ||
1611 | int len; | ||
1612 | char text[LINESIZ + 2]; | ||
1613 | |||
1614 | inetd_setproctitle(sep->se_service, s); | ||
1615 | |||
1616 | if (!endring) { | ||
1617 | initring(); | ||
1618 | rs = ring; | ||
1619 | } | ||
1620 | |||
1621 | text[LINESIZ] = '\r'; | ||
1622 | text[LINESIZ + 1] = '\n'; | ||
1623 | rs = ring; | ||
1624 | for (;;) { | ||
1625 | len = endring - rs; | ||
1626 | if (len >= LINESIZ) | ||
1627 | memmove(text, rs, LINESIZ); | ||
1628 | else { | ||
1629 | memmove(text, rs, len); | ||
1630 | memmove(text + len, ring, LINESIZ - len); | ||
1631 | } | ||
1632 | if (++rs == endring) | ||
1633 | rs = ring; | ||
1634 | if (write(s, text, sizeof(text)) != sizeof(text)) | ||
1635 | break; | ||
1636 | } | ||
1637 | exit(0); | ||
1638 | } | ||
1639 | |||
1640 | /* Character generator */ | ||
1641 | /* ARGSUSED */ | ||
1642 | static void | ||
1643 | chargen_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1644 | { | ||
1645 | /* struct sockaddr_storage ss; */ | ||
1646 | struct sockaddr sa; | ||
1647 | static char *rs; | ||
1648 | int len; | ||
1649 | char text[LINESIZ + 2]; | ||
1650 | socklen_t size; | ||
1651 | |||
1652 | if (endring == 0) { | ||
1653 | initring(); | ||
1654 | rs = ring; | ||
1655 | } | ||
1656 | |||
1657 | size = sizeof(sa); | ||
1658 | if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0) | ||
1659 | return; | ||
1660 | if (dg_badinput((struct sockaddr_in *) &sa)) | ||
1661 | return; | ||
1662 | |||
1663 | if ((len = endring - rs) >= LINESIZ) | ||
1664 | memmove(text, rs, LINESIZ); | ||
1665 | else { | ||
1666 | memmove(text, rs, len); | ||
1667 | memmove(text + len, ring, LINESIZ - len); | ||
1668 | } | ||
1669 | if (++rs == endring) | ||
1670 | rs = ring; | ||
1671 | text[LINESIZ] = '\r'; | ||
1672 | text[LINESIZ + 1] = '\n'; | ||
1673 | (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa)); | ||
1674 | } | ||
1675 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */ | ||
1676 | |||
1677 | |||
1678 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME | ||
1679 | /* | ||
1680 | * Return a machine readable date and time, in the form of the | ||
1681 | * number of seconds since midnight, Jan 1, 1900. Since gettimeofday | ||
1682 | * returns the number of seconds since midnight, Jan 1, 1970, | ||
1683 | * we must add 2208988800 seconds to this figure to make up for | ||
1684 | * some seventy years Bell Labs was asleep. | ||
1685 | */ | ||
1686 | |||
1687 | static u_int machtime(void) | ||
1688 | { | ||
1689 | struct timeval tv; | ||
1690 | |||
1691 | if (gettimeofday(&tv, NULL) < 0) { | ||
1692 | fprintf(stderr, "Unable to get time of day\n"); | ||
1693 | return 0L; | ||
1694 | } | ||
1695 | return htonl((u_int) tv.tv_sec + 2208988800UL); | ||
1696 | } | ||
1697 | |||
1698 | /* ARGSUSED */ | ||
1699 | static void | ||
1700 | machtime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1701 | { | ||
1702 | u_int result; | ||
1703 | |||
1704 | result = machtime(); | ||
1705 | (void) write(s, (char *) &result, sizeof(result)); | ||
1706 | } | ||
1707 | |||
1708 | /* ARGSUSED */ | ||
1709 | static void | ||
1710 | machtime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1711 | { | ||
1712 | u_int result; | ||
1713 | /* struct sockaddr_storage ss; */ | ||
1714 | struct sockaddr sa; | ||
1715 | struct sockaddr_in *dg_sin; | ||
1716 | socklen_t size; | ||
1717 | |||
1718 | size = sizeof(sa); | ||
1719 | if (recvfrom(s, (char *) &result, sizeof(result), 0, &sa, &size) < 0) | ||
1720 | return; | ||
1721 | /* if (dg_badinput((struct sockaddr *)&ss)) */ | ||
1722 | dg_sin = (struct sockaddr_in *) &sa; | ||
1723 | if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST) || | ||
1724 | ntohs(dg_sin->sin_port) < IPPORT_RESERVED / 2) | ||
1725 | return; | ||
1726 | result = machtime(); | ||
1727 | (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa)); | ||
1728 | } | ||
1729 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME */ | ||
1730 | |||
1731 | |||
1732 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME | ||
1733 | /* Return human-readable time of day */ | ||
1734 | /* ARGSUSED */ | ||
1735 | static void daytime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1736 | { | ||
1737 | char buffer[256]; | ||
1738 | time_t t; | ||
1739 | |||
1740 | t = time(NULL); | ||
1741 | |||
1742 | (void) sprintf(buffer, "%.24s\r\n", ctime(&t)); | ||
1743 | (void) write(s, buffer, strlen(buffer)); | ||
1744 | } | ||
1745 | |||
1746 | /* Return human-readable time of day */ | ||
1747 | /* ARGSUSED */ | ||
1748 | void | ||
1749 | daytime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED) | ||
1750 | { | ||
1751 | char buffer[256]; | ||
1752 | time_t t; | ||
1753 | /* struct sockaddr_storage ss; */ | ||
1754 | struct sockaddr sa; | ||
1755 | socklen_t size; | ||
1756 | |||
1757 | t = time(NULL); | ||
1758 | |||
1759 | size = sizeof(sa); | ||
1760 | if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) | ||
1761 | return; | ||
1762 | if (dg_badinput((struct sockaddr_in *) &sa)) | ||
1763 | return; | ||
1764 | (void) sprintf(buffer, "%.24s\r\n", ctime(&t)); | ||
1765 | (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa)); | ||
1766 | } | ||
1767 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */ | ||