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