1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167#include <syslog.h>
168#include <sys/resource.h>
169#include <sys/socket.h>
170#include <sys/un.h>
171
172#include "libbb.h"
173
174#if ENABLE_FEATURE_INETD_RPC
175# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
176# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
177# endif
178# include <rpc/rpc.h>
179# include <rpc/pmap_clnt.h>
180#endif
181
182#if !BB_MMU
183
184
185#undef ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
186#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 0
187#endif
188
189#define CNT_INTERVAL 60
190#define RETRYTIME 60
191
192
193
194#ifndef RLIMIT_NOFILE
195#define RLIMIT_NOFILE RLIMIT_OFILE
196#endif
197
198#ifndef OPEN_MAX
199#define OPEN_MAX 64
200#endif
201
202
203#define FD_MARGIN 8
204
205#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD \
206 || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO \
207 || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN \
208 || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME \
209 || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
210# define INETD_BUILTINS_ENABLED
211#endif
212
213typedef struct servtab_t {
214
215 int se_fd;
216
217
218 char *se_local_hostname;
219 char *se_service;
220
221 char *se_proto;
222#if ENABLE_FEATURE_INETD_RPC
223 int se_rpcprog;
224 int se_rpcver_lo;
225 int se_rpcver_hi;
226#define is_rpc_service(sep) ((sep)->se_rpcver_lo != 0)
227#else
228#define is_rpc_service(sep) 0
229#endif
230 pid_t se_wait;
231
232 socktype_t se_socktype;
233 family_t se_family;
234
235 smallint se_proto_no;
236 smallint se_checked;
237 unsigned se_max;
238 unsigned se_count;
239 unsigned se_time;
240 char *se_user;
241 char *se_group;
242#ifdef INETD_BUILTINS_ENABLED
243 const struct builtin *se_builtin;
244#endif
245 struct servtab_t *se_next;
246 len_and_sockaddr *se_lsa;
247 char *se_program;
248#define MAXARGV 20
249 char *se_argv[MAXARGV + 1];
250} servtab_t;
251
252#ifdef INETD_BUILTINS_ENABLED
253
254#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
255static void FAST_FUNC echo_stream(int, servtab_t *);
256static void FAST_FUNC echo_dg(int, servtab_t *);
257#endif
258
259#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
260static void FAST_FUNC discard_stream(int, servtab_t *);
261static void FAST_FUNC discard_dg(int, servtab_t *);
262#endif
263
264#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
265static void FAST_FUNC machtime_stream(int, servtab_t *);
266static void FAST_FUNC machtime_dg(int, servtab_t *);
267#endif
268
269#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
270static void FAST_FUNC daytime_stream(int, servtab_t *);
271static void FAST_FUNC daytime_dg(int, servtab_t *);
272#endif
273
274#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
275static void FAST_FUNC chargen_stream(int, servtab_t *);
276static void FAST_FUNC chargen_dg(int, servtab_t *);
277#endif
278
279struct builtin {
280
281 char bi_service7[7];
282 uint8_t bi_fork;
283 void (*bi_stream_fn)(int, servtab_t *) FAST_FUNC;
284 void (*bi_dgram_fn)(int, servtab_t *) FAST_FUNC;
285};
286
287static const struct builtin builtins[] = {
288#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
289 { "echo", 1, echo_stream, echo_dg },
290#endif
291#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
292 { "discard", 1, discard_stream, discard_dg },
293#endif
294#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
295 { "chargen", 1, chargen_stream, chargen_dg },
296#endif
297#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
298 { "time", 0, machtime_stream, machtime_dg },
299#endif
300#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
301 { "daytime", 0, daytime_stream, daytime_dg },
302#endif
303};
304#endif
305
306struct globals {
307 rlim_t rlim_ofile_cur;
308 struct rlimit rlim_ofile;
309 servtab_t *serv_list;
310 int global_queuelen;
311 int maxsock;
312
313
314 int prev_maxsock;
315 unsigned max_concurrency;
316 smallint alarm_armed;
317 uid_t real_uid;
318 const char *config_filename;
319 parser_t *parser;
320 char *default_local_hostname;
321#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
322 char *end_ring;
323 char *ring_pos;
324 char ring[128];
325#endif
326 fd_set allsock;
327
328 char line[256];
329} FIX_ALIASING;
330#define G (*(struct globals*)&bb_common_bufsiz1)
331enum { LINE_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line) };
332struct BUG_G_too_big {
333 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
334};
335#define rlim_ofile_cur (G.rlim_ofile_cur )
336#define rlim_ofile (G.rlim_ofile )
337#define serv_list (G.serv_list )
338#define global_queuelen (G.global_queuelen)
339#define maxsock (G.maxsock )
340#define prev_maxsock (G.prev_maxsock )
341#define max_concurrency (G.max_concurrency)
342#define alarm_armed (G.alarm_armed )
343#define real_uid (G.real_uid )
344#define config_filename (G.config_filename)
345#define parser (G.parser )
346#define default_local_hostname (G.default_local_hostname)
347#define first_ps_byte (G.first_ps_byte )
348#define last_ps_byte (G.last_ps_byte )
349#define end_ring (G.end_ring )
350#define ring_pos (G.ring_pos )
351#define ring (G.ring )
352#define allsock (G.allsock )
353#define line (G.line )
354#define INIT_G() do { \
355 rlim_ofile_cur = OPEN_MAX; \
356 global_queuelen = 128; \
357 config_filename = "/etc/inetd.conf"; \
358} while (0)
359
360#if 1
361# define dbg(...) ((void)0)
362#else
363# define dbg(...) \
364do { \
365 int dbg_fd = open("inetd_debug.log", O_WRONLY | O_CREAT | O_APPEND, 0666); \
366 if (dbg_fd >= 0) { \
367 fdprintf(dbg_fd, "%d: ", getpid()); \
368 fdprintf(dbg_fd, __VA_ARGS__); \
369 close(dbg_fd); \
370 } \
371} while (0)
372#endif
373
374static void maybe_close(int fd)
375{
376 if (fd >= 0) {
377 close(fd);
378 dbg("closed fd:%d\n", fd);
379 }
380}
381
382
383static len_and_sockaddr *xzalloc_lsa(int family)
384{
385 len_and_sockaddr *lsa;
386 int sz;
387
388 sz = sizeof(struct sockaddr_in);
389 if (family == AF_UNIX)
390 sz = sizeof(struct sockaddr_un);
391#if ENABLE_FEATURE_IPV6
392 if (family == AF_INET6)
393 sz = sizeof(struct sockaddr_in6);
394#endif
395 lsa = xzalloc(LSA_LEN_SIZE + sz);
396 lsa->len = sz;
397 lsa->u.sa.sa_family = family;
398 return lsa;
399}
400
401static void rearm_alarm(void)
402{
403 if (!alarm_armed) {
404 alarm_armed = 1;
405 alarm(RETRYTIME);
406 }
407}
408
409static void block_CHLD_HUP_ALRM(sigset_t *m)
410{
411 sigemptyset(m);
412 sigaddset(m, SIGCHLD);
413 sigaddset(m, SIGHUP);
414 sigaddset(m, SIGALRM);
415 sigprocmask(SIG_BLOCK, m, m);
416}
417
418static void restore_sigmask(sigset_t *m)
419{
420 sigprocmask(SIG_SETMASK, m, NULL);
421}
422
423#if ENABLE_FEATURE_INETD_RPC
424static void register_rpc(servtab_t *sep)
425{
426 int n;
427 struct sockaddr_in ir_sin;
428 socklen_t size;
429
430 size = sizeof(ir_sin);
431 if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) {
432 bb_perror_msg("getsockname");
433 return;
434 }
435
436 for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) {
437 pmap_unset(sep->se_rpcprog, n);
438 if (!pmap_set(sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port)))
439 bb_perror_msg("%s %s: pmap_set(%u,%u,%u,%u)",
440 sep->se_service, sep->se_proto,
441 sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port));
442 }
443}
444
445static void unregister_rpc(servtab_t *sep)
446{
447 int n;
448
449 for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) {
450 if (!pmap_unset(sep->se_rpcprog, n))
451 bb_perror_msg("pmap_unset(%u,%u)", sep->se_rpcprog, n);
452 }
453}
454#endif
455
456static void bump_nofile(void)
457{
458 enum { FD_CHUNK = 32 };
459 struct rlimit rl;
460
461
462 getrlimit(RLIMIT_NOFILE, &rl);
463 rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
464 rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);
465 if (rl.rlim_cur <= rlim_ofile_cur) {
466 bb_error_msg("can't extend file limit, max = %d",
467 (int) rl.rlim_cur);
468 return;
469 }
470
471 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
472 bb_perror_msg("setrlimit");
473 return;
474 }
475
476 rlim_ofile_cur = rl.rlim_cur;
477}
478
479static void remove_fd_from_set(int fd)
480{
481 if (fd >= 0) {
482 FD_CLR(fd, &allsock);
483 dbg("stopped listening on fd:%d\n", fd);
484 maxsock = -1;
485 dbg("maxsock:%d\n", maxsock);
486 }
487}
488
489static void add_fd_to_set(int fd)
490{
491 if (fd >= 0) {
492 FD_SET(fd, &allsock);
493 dbg("started listening on fd:%d\n", fd);
494 if (maxsock >= 0 && fd > maxsock) {
495 prev_maxsock = maxsock = fd;
496 dbg("maxsock:%d\n", maxsock);
497 if ((rlim_t)fd > rlim_ofile_cur - FD_MARGIN)
498 bump_nofile();
499 }
500 }
501}
502
503static void recalculate_maxsock(void)
504{
505 int fd = 0;
506
507
508
509 maxsock = 0;
510 while (fd <= prev_maxsock) {
511 if (FD_ISSET(fd, &allsock))
512 maxsock = fd;
513 fd++;
514 }
515 dbg("recalculated maxsock:%d\n", maxsock);
516 prev_maxsock = maxsock;
517 if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN)
518 bump_nofile();
519}
520
521static void prepare_socket_fd(servtab_t *sep)
522{
523 int r, fd;
524
525 fd = socket(sep->se_family, sep->se_socktype, 0);
526 if (fd < 0) {
527 bb_perror_msg("socket");
528 return;
529 }
530 setsockopt_reuseaddr(fd);
531
532#if ENABLE_FEATURE_INETD_RPC
533 if (is_rpc_service(sep)) {
534 struct passwd *pwd;
535
536
537
538 set_nport(&sep->se_lsa->u.sa, 0);
539
540
541
542 if (real_uid == 0 && sep->se_family == AF_INET
543 && (pwd = getpwnam(sep->se_user)) != NULL
544 && pwd->pw_uid == 0
545 ) {
546 r = bindresvport(fd, &sep->se_lsa->u.sin);
547 } else {
548 r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len);
549 }
550 if (r == 0) {
551 int saveerrno = errno;
552
553 getsockname(fd, &sep->se_lsa->u.sa, &sep->se_lsa->len);
554 errno = saveerrno;
555 }
556 } else
557#endif
558 {
559 if (sep->se_family == AF_UNIX) {
560 struct sockaddr_un *sun;
561 sun = (struct sockaddr_un*)&(sep->se_lsa->u.sa);
562 unlink(sun->sun_path);
563 }
564 r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len);
565 }
566 if (r < 0) {
567 bb_perror_msg("%s/%s: bind",
568 sep->se_service, sep->se_proto);
569 close(fd);
570 rearm_alarm();
571 return;
572 }
573
574 if (sep->se_socktype == SOCK_STREAM) {
575 listen(fd, global_queuelen);
576 dbg("new sep->se_fd:%d (stream)\n", fd);
577 } else {
578 dbg("new sep->se_fd:%d (!stream)\n", fd);
579 }
580
581 add_fd_to_set(fd);
582 sep->se_fd = fd;
583}
584
585static int reopen_config_file(void)
586{
587 free(default_local_hostname);
588 default_local_hostname = xstrdup("*");
589 if (parser != NULL)
590 config_close(parser);
591 parser = config_open(config_filename);
592 return (parser != NULL);
593}
594
595static void close_config_file(void)
596{
597 if (parser) {
598 config_close(parser);
599 parser = NULL;
600 }
601}
602
603static void free_servtab_strings(servtab_t *cp)
604{
605 int i;
606
607 free(cp->se_local_hostname);
608 free(cp->se_service);
609 free(cp->se_proto);
610 free(cp->se_user);
611 free(cp->se_group);
612 free(cp->se_lsa);
613 free(cp->se_program);
614 for (i = 0; i < MAXARGV; i++)
615 free(cp->se_argv[i]);
616}
617
618static servtab_t *new_servtab(void)
619{
620 servtab_t *newtab = xzalloc(sizeof(servtab_t));
621 newtab->se_fd = -1;
622 return newtab;
623}
624
625static servtab_t *dup_servtab(servtab_t *sep)
626{
627 servtab_t *newtab;
628 int argc;
629
630 newtab = new_servtab();
631 *newtab = *sep;
632
633 newtab->se_service = xstrdup(newtab->se_service);
634 newtab->se_proto = xstrdup(newtab->se_proto);
635 newtab->se_user = xstrdup(newtab->se_user);
636 newtab->se_group = xstrdup(newtab->se_group);
637 newtab->se_program = xstrdup(newtab->se_program);
638 for (argc = 0; argc <= MAXARGV; argc++)
639 newtab->se_argv[argc] = xstrdup(newtab->se_argv[argc]);
640
641
642
643
644 return newtab;
645}
646
647
648static servtab_t *parse_one_line(void)
649{
650 int argc;
651 char *token[6+MAXARGV];
652 char *p, *arg;
653 char *hostdelim;
654 servtab_t *sep;
655 servtab_t *nsep;
656 new:
657 sep = new_servtab();
658 more:
659 argc = config_read(parser, token, 6+MAXARGV, 1, "# \t", PARSE_NORMAL);
660 if (!argc) {
661 free(sep);
662 return NULL;
663 }
664
665
666
667 arg = token[0];
668 hostdelim = strrchr(arg, ':');
669 if (hostdelim) {
670 *hostdelim = '\0';
671 sep->se_local_hostname = xstrdup(arg);
672 arg = hostdelim + 1;
673 if (*arg == '\0' && argc == 1) {
674
675
676 free(default_local_hostname);
677 default_local_hostname = sep->se_local_hostname;
678 goto more;
679 }
680 } else
681 sep->se_local_hostname = xstrdup(default_local_hostname);
682
683
684 sep->se_service = xstrdup(arg);
685
686
687 if (argc < 6) {
688 parse_err:
689 bb_error_msg("parse error on line %u, line is ignored",
690 parser->lineno);
691 free_servtab_strings(sep);
692
693
694
695 free(sep);
696 goto new;
697 }
698
699 {
700 static const int8_t SOCK_xxx[] ALIGN1 = {
701 -1,
702 SOCK_STREAM, SOCK_DGRAM, SOCK_RDM,
703 SOCK_SEQPACKET, SOCK_RAW
704 };
705 sep->se_socktype = SOCK_xxx[1 + index_in_strings(
706 "stream""\0" "dgram""\0" "rdm""\0"
707 "seqpacket""\0" "raw""\0"
708 , token[1])];
709 }
710
711
712 sep->se_proto = arg = xstrdup(token[2]);
713 if (strcmp(arg, "unix") == 0) {
714 sep->se_family = AF_UNIX;
715 } else {
716 char *six;
717 sep->se_family = AF_INET;
718 six = last_char_is(arg, '6');
719 if (six) {
720#if ENABLE_FEATURE_IPV6
721 *six = '\0';
722 sep->se_family = AF_INET6;
723#else
724 bb_error_msg("%s: no support for IPv6", sep->se_proto);
725 goto parse_err;
726#endif
727 }
728 if (strncmp(arg, "rpc/", 4) == 0) {
729#if ENABLE_FEATURE_INETD_RPC
730 unsigned n;
731 arg += 4;
732 p = strchr(sep->se_service, '/');
733 if (p == NULL) {
734 bb_error_msg("no rpc version: '%s'", sep->se_service);
735 goto parse_err;
736 }
737 *p++ = '\0';
738 n = bb_strtou(p, &p, 10);
739 if (n > INT_MAX) {
740 bad_ver_spec:
741 bb_error_msg("bad rpc version");
742 goto parse_err;
743 }
744 sep->se_rpcver_lo = sep->se_rpcver_hi = n;
745 if (*p == '-') {
746 p++;
747 n = bb_strtou(p, &p, 10);
748 if (n > INT_MAX || (int)n < sep->se_rpcver_lo)
749 goto bad_ver_spec;
750 sep->se_rpcver_hi = n;
751 }
752 if (*p != '\0')
753 goto bad_ver_spec;
754#else
755 bb_error_msg("no support for rpc services");
756 goto parse_err;
757#endif
758 }
759
760 if (strcmp(arg, "tcp") == 0)
761 sep->se_proto_no = IPPROTO_TCP;
762 if (strcmp(arg, "udp") == 0)
763 sep->se_proto_no = IPPROTO_UDP;
764 if (six)
765 *six = '6';
766 if (!sep->se_proto_no)
767 goto parse_err;
768 }
769
770
771 arg = token[3];
772 sep->se_max = max_concurrency;
773 p = strchr(arg, '.');
774 if (p) {
775 *p++ = '\0';
776 sep->se_max = bb_strtou(p, NULL, 10);
777 if (errno)
778 goto parse_err;
779 }
780 sep->se_wait = (arg[0] != 'n' || arg[1] != 'o');
781 if (!sep->se_wait)
782 arg += 2;
783 if (strcmp(arg, "wait") != 0)
784 goto parse_err;
785
786
787 sep->se_user = xstrdup(token[4]);
788 arg = strchr(sep->se_user, '.');
789 if (arg == NULL)
790 arg = strchr(sep->se_user, ':');
791 if (arg) {
792 *arg++ = '\0';
793 sep->se_group = xstrdup(arg);
794 }
795
796
797 sep->se_program = xstrdup(token[5]);
798#ifdef INETD_BUILTINS_ENABLED
799 if (strcmp(sep->se_program, "internal") == 0
800 && strlen(sep->se_service) <= 7
801 && (sep->se_socktype == SOCK_STREAM
802 || sep->se_socktype == SOCK_DGRAM)
803 ) {
804 unsigned i;
805 for (i = 0; i < ARRAY_SIZE(builtins); i++)
806 if (strncmp(builtins[i].bi_service7, sep->se_service, 7) == 0)
807 goto found_bi;
808 bb_error_msg("unknown internal service %s", sep->se_service);
809 goto parse_err;
810 found_bi:
811 sep->se_builtin = &builtins[i];
812
813 if (sep->se_wait != (sep->se_socktype == SOCK_DGRAM))
814 goto parse_err;
815 }
816#endif
817 argc = 0;
818 while ((arg = token[6+argc]) != NULL && argc < MAXARGV)
819 sep->se_argv[argc++] = xstrdup(arg);
820
821
822
823
824 if (argc == 0)
825 sep->se_argv[0] = xstrdup(sep->se_program);
826
827
828 if (sep->se_socktype == SOCK_STREAM) {
829 if (sep->se_proto_no == IPPROTO_UDP)
830 goto parse_err;
831 }
832 if (sep->se_socktype == SOCK_DGRAM) {
833 if (sep->se_proto_no == IPPROTO_TCP)
834 goto parse_err;
835 }
836
837
838
839
840
841
842
843
844 while ((hostdelim = strrchr(sep->se_local_hostname, ',')) != NULL) {
845 nsep = dup_servtab(sep);
846
847
848 *hostdelim++ = '\0';
849 nsep->se_local_hostname = xstrdup(hostdelim);
850 nsep->se_next = sep->se_next;
851 sep->se_next = nsep;
852 }
853
854
855
856
857
858 return sep;
859}
860
861static servtab_t *insert_in_servlist(servtab_t *cp)
862{
863 servtab_t *sep;
864 sigset_t omask;
865
866 sep = new_servtab();
867 *sep = *cp;
868 sep->se_fd = -1;
869#if ENABLE_FEATURE_INETD_RPC
870 sep->se_rpcprog = -1;
871#endif
872 block_CHLD_HUP_ALRM(&omask);
873 sep->se_next = serv_list;
874 serv_list = sep;
875 restore_sigmask(&omask);
876 return sep;
877}
878
879static int same_serv_addr_proto(servtab_t *old, servtab_t *new)
880{
881 if (strcmp(old->se_local_hostname, new->se_local_hostname) != 0)
882 return 0;
883 if (strcmp(old->se_service, new->se_service) != 0)
884 return 0;
885 if (strcmp(old->se_proto, new->se_proto) != 0)
886 return 0;
887 return 1;
888}
889
890static void reread_config_file(int sig UNUSED_PARAM)
891{
892 servtab_t *sep, *cp, **sepp;
893 len_and_sockaddr *lsa;
894 sigset_t omask;
895 unsigned n;
896 uint16_t port;
897 int save_errno = errno;
898
899 if (!reopen_config_file())
900 goto ret;
901 for (sep = serv_list; sep; sep = sep->se_next)
902 sep->se_checked = 0;
903
904 goto first_line;
905 while (1) {
906 if (cp == NULL) {
907 first_line:
908 cp = parse_one_line();
909 if (cp == NULL)
910 break;
911 }
912 for (sep = serv_list; sep; sep = sep->se_next)
913 if (same_serv_addr_proto(sep, cp))
914 goto equal_servtab;
915
916 sep = insert_in_servlist(cp);
917 goto after_check;
918 equal_servtab:
919 {
920 int i;
921
922 block_CHLD_HUP_ALRM(&omask);
923#if ENABLE_FEATURE_INETD_RPC
924 if (is_rpc_service(sep))
925 unregister_rpc(sep);
926 sep->se_rpcver_lo = cp->se_rpcver_lo;
927 sep->se_rpcver_hi = cp->se_rpcver_hi;
928#endif
929 if (cp->se_wait == 0) {
930
931
932
933
934
935 add_fd_to_set(sep->se_fd);
936 }
937 sep->se_wait = cp->se_wait;
938 sep->se_max = cp->se_max;
939
940#define SWAP(type, a, b) do { type c = (type)a; a = (type)b; b = (type)c; } while (0)
941 SWAP(char*, sep->se_user, cp->se_user);
942 SWAP(char*, sep->se_group, cp->se_group);
943 SWAP(char*, sep->se_program, cp->se_program);
944 for (i = 0; i < MAXARGV; i++)
945 SWAP(char*, sep->se_argv[i], cp->se_argv[i]);
946#undef SWAP
947 restore_sigmask(&omask);
948 free_servtab_strings(cp);
949 }
950 after_check:
951
952
953 sep->se_checked = 1;
954
955
956 switch (sep->se_family) {
957 struct sockaddr_un *sun;
958 case AF_UNIX:
959 lsa = xzalloc_lsa(AF_UNIX);
960 sun = (struct sockaddr_un*)&lsa->u.sa;
961 safe_strncpy(sun->sun_path, sep->se_service, sizeof(sun->sun_path));
962 break;
963
964 default:
965 n = bb_strtou(sep->se_service, NULL, 10);
966#if ENABLE_FEATURE_INETD_RPC
967 if (is_rpc_service(sep)) {
968 sep->se_rpcprog = n;
969 if (errno) {
970 struct rpcent *rp = getrpcbyname(sep->se_service);
971 if (rp == NULL) {
972 bb_error_msg("%s: unknown rpc service", sep->se_service);
973 goto next_cp;
974 }
975 sep->se_rpcprog = rp->r_number;
976 }
977 if (sep->se_fd == -1)
978 prepare_socket_fd(sep);
979 if (sep->se_fd != -1)
980 register_rpc(sep);
981 goto next_cp;
982 }
983#endif
984
985 port = htons(n);
986 if (errno || n > 0xffff) {
987 char protoname[4];
988 struct servent *sp;
989
990 safe_strncpy(protoname, sep->se_proto, 4);
991 sp = getservbyname(sep->se_service, protoname);
992 if (sp == NULL) {
993 bb_error_msg("%s/%s: unknown service",
994 sep->se_service, sep->se_proto);
995 goto next_cp;
996 }
997 port = sp->s_port;
998 }
999 if (LONE_CHAR(sep->se_local_hostname, '*')) {
1000 lsa = xzalloc_lsa(sep->se_family);
1001 set_nport(&lsa->u.sa, port);
1002 } else {
1003 lsa = host_and_af2sockaddr(sep->se_local_hostname,
1004 ntohs(port), sep->se_family);
1005 if (!lsa) {
1006 bb_error_msg("%s/%s: unknown host '%s'",
1007 sep->se_service, sep->se_proto,
1008 sep->se_local_hostname);
1009 goto next_cp;
1010 }
1011 }
1012 break;
1013 }
1014
1015
1016 if (sep->se_lsa == NULL
1017 || lsa->len != sep->se_lsa->len
1018 || memcmp(&lsa->u.sa, &sep->se_lsa->u.sa, lsa->len) != 0
1019 ) {
1020 remove_fd_from_set(sep->se_fd);
1021 maybe_close(sep->se_fd);
1022 free(sep->se_lsa);
1023 sep->se_lsa = lsa;
1024 sep->se_fd = -1;
1025 } else {
1026 free(lsa);
1027 }
1028 if (sep->se_fd == -1)
1029 prepare_socket_fd(sep);
1030 next_cp:
1031 sep = cp->se_next;
1032 free(cp);
1033 cp = sep;
1034 }
1035 close_config_file();
1036
1037
1038
1039 block_CHLD_HUP_ALRM(&omask);
1040 sepp = &serv_list;
1041 while ((sep = *sepp) != NULL) {
1042 if (sep->se_checked) {
1043 sepp = &sep->se_next;
1044 continue;
1045 }
1046 *sepp = sep->se_next;
1047 remove_fd_from_set(sep->se_fd);
1048 maybe_close(sep->se_fd);
1049#if ENABLE_FEATURE_INETD_RPC
1050 if (is_rpc_service(sep))
1051 unregister_rpc(sep);
1052#endif
1053 if (sep->se_family == AF_UNIX)
1054 unlink(sep->se_service);
1055 free_servtab_strings(sep);
1056 free(sep);
1057 }
1058 restore_sigmask(&omask);
1059 ret:
1060 errno = save_errno;
1061}
1062
1063static void reap_child(int sig UNUSED_PARAM)
1064{
1065 pid_t pid;
1066 int status;
1067 servtab_t *sep;
1068 int save_errno = errno;
1069
1070 for (;;) {
1071 pid = wait_any_nohang(&status);
1072 if (pid <= 0)
1073 break;
1074 for (sep = serv_list; sep; sep = sep->se_next) {
1075 if (sep->se_wait != pid)
1076 continue;
1077
1078 if (WIFEXITED(status) && WEXITSTATUS(status))
1079 bb_error_msg("%s: exit status %u",
1080 sep->se_program, WEXITSTATUS(status));
1081 else if (WIFSIGNALED(status))
1082 bb_error_msg("%s: exit signal %u",
1083 sep->se_program, WTERMSIG(status));
1084 sep->se_wait = 1;
1085 add_fd_to_set(sep->se_fd);
1086 break;
1087 }
1088 }
1089 errno = save_errno;
1090}
1091
1092static void retry_network_setup(int sig UNUSED_PARAM)
1093{
1094 int save_errno = errno;
1095 servtab_t *sep;
1096
1097 alarm_armed = 0;
1098 for (sep = serv_list; sep; sep = sep->se_next) {
1099 if (sep->se_fd == -1) {
1100 prepare_socket_fd(sep);
1101#if ENABLE_FEATURE_INETD_RPC
1102 if (sep->se_fd != -1 && is_rpc_service(sep))
1103 register_rpc(sep);
1104#endif
1105 }
1106 }
1107 errno = save_errno;
1108}
1109
1110static void clean_up_and_exit(int sig UNUSED_PARAM)
1111{
1112 servtab_t *sep;
1113
1114
1115 for (sep = serv_list; sep; sep = sep->se_next) {
1116 if (sep->se_fd == -1)
1117 continue;
1118
1119 switch (sep->se_family) {
1120 case AF_UNIX:
1121 unlink(sep->se_service);
1122 break;
1123 default:
1124#if ENABLE_FEATURE_INETD_RPC
1125 if (sep->se_wait == 1 && is_rpc_service(sep))
1126 unregister_rpc(sep);
1127#endif
1128 break;
1129 }
1130 if (ENABLE_FEATURE_CLEAN_UP)
1131 close(sep->se_fd);
1132 }
1133 remove_pidfile(CONFIG_PID_FILE_PATH "/inetd.pid");
1134 exit(EXIT_SUCCESS);
1135}
1136
1137int inetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1138int inetd_main(int argc UNUSED_PARAM, char **argv)
1139{
1140 struct sigaction sa, saved_pipe_handler;
1141 servtab_t *sep, *sep2;
1142 struct passwd *pwd;
1143 struct group *grp = grp;
1144 int opt;
1145 pid_t pid;
1146 sigset_t omask;
1147
1148 INIT_G();
1149
1150 real_uid = getuid();
1151 if (real_uid != 0)
1152 config_filename = NULL;
1153
1154 opt_complementary = "R+:q+";
1155 opt = getopt32(argv, "R:feq:", &max_concurrency, &global_queuelen);
1156 argv += optind;
1157
1158 if (argv[0])
1159 config_filename = argv[0];
1160 if (config_filename == NULL)
1161 bb_error_msg_and_die("non-root must specify config file");
1162 if (!(opt & 2))
1163 bb_daemonize_or_rexec(0, argv - optind);
1164 else
1165 bb_sanitize_stdio();
1166 if (!(opt & 4)) {
1167
1168
1169
1170
1171
1172 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1173 logmode = LOGMODE_SYSLOG;
1174 }
1175
1176 if (real_uid == 0) {
1177
1178 gid_t gid = getgid();
1179 setgroups(1, &gid);
1180 }
1181
1182 write_pidfile(CONFIG_PID_FILE_PATH "/inetd.pid");
1183
1184
1185 getrlimit(RLIMIT_NOFILE, &rlim_ofile);
1186 rlim_ofile_cur = rlim_ofile.rlim_cur;
1187 if (rlim_ofile_cur == RLIM_INFINITY)
1188 rlim_ofile_cur = OPEN_MAX;
1189
1190 memset(&sa, 0, sizeof(sa));
1191
1192 sigaddset(&sa.sa_mask, SIGALRM);
1193 sigaddset(&sa.sa_mask, SIGCHLD);
1194 sigaddset(&sa.sa_mask, SIGHUP);
1195
1196
1197 sa.sa_handler = retry_network_setup;
1198 sigaction_set(SIGALRM, &sa);
1199
1200 sa.sa_handler = reread_config_file;
1201 sigaction_set(SIGHUP, &sa);
1202
1203 sa.sa_handler = reap_child;
1204 sigaction_set(SIGCHLD, &sa);
1205
1206 sa.sa_handler = clean_up_and_exit;
1207 sigaction_set(SIGTERM, &sa);
1208 sa.sa_handler = clean_up_and_exit;
1209 sigaction_set(SIGINT, &sa);
1210 sa.sa_handler = SIG_IGN;
1211 sigaction(SIGPIPE, &sa, &saved_pipe_handler);
1212
1213 reread_config_file(SIGHUP);
1214
1215 for (;;) {
1216 int ready_fd_cnt;
1217 int ctrl, accepted_fd, new_udp_fd;
1218 fd_set readable;
1219
1220 if (maxsock < 0)
1221 recalculate_maxsock();
1222
1223 readable = allsock;
1224
1225
1226
1227 ready_fd_cnt = select(maxsock + 1, &readable, NULL, NULL, NULL);
1228 if (ready_fd_cnt < 0) {
1229 if (errno != EINTR) {
1230 bb_perror_msg("select");
1231 sleep(1);
1232 }
1233 continue;
1234 }
1235 dbg("ready_fd_cnt:%d\n", ready_fd_cnt);
1236
1237 for (sep = serv_list; ready_fd_cnt && sep; sep = sep->se_next) {
1238 if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable))
1239 continue;
1240
1241 dbg("ready fd:%d\n", sep->se_fd);
1242 ready_fd_cnt--;
1243 ctrl = sep->se_fd;
1244 accepted_fd = -1;
1245 new_udp_fd = -1;
1246 if (!sep->se_wait) {
1247 if (sep->se_socktype == SOCK_STREAM) {
1248 ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL);
1249 dbg("accepted_fd:%d\n", accepted_fd);
1250 if (ctrl < 0) {
1251 if (errno != EINTR)
1252 bb_perror_msg("accept (for %s)", sep->se_service);
1253 continue;
1254 }
1255 }
1256
1257 if (sep->se_socktype == SOCK_DGRAM
1258 && sep->se_family != AF_UNIX
1259 ) {
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269 new_udp_fd = socket(sep->se_family, SOCK_DGRAM, 0);
1270 dbg("new_udp_fd:%d\n", new_udp_fd);
1271 if (new_udp_fd < 0) {
1272 udp_err:
1273 recv(sep->se_fd, line, LINE_SIZE, MSG_DONTWAIT);
1274 continue;
1275 }
1276 setsockopt_reuseaddr(new_udp_fd);
1277
1278
1279
1280 if (bind(new_udp_fd, &sep->se_lsa->u.sa, sep->se_lsa->len) < 0) {
1281 dbg("bind(new_udp_fd) failed\n");
1282 close(new_udp_fd);
1283 goto udp_err;
1284 }
1285 dbg("bind(new_udp_fd) succeeded\n");
1286 }
1287 }
1288
1289 block_CHLD_HUP_ALRM(&omask);
1290 pid = 0;
1291#ifdef INETD_BUILTINS_ENABLED
1292
1293 if (sep->se_builtin == NULL
1294 || (sep->se_socktype == SOCK_STREAM
1295 && sep->se_builtin->bi_fork))
1296#endif
1297 {
1298 if (sep->se_max != 0) {
1299 if (++sep->se_count == 1)
1300 sep->se_time = monotonic_sec();
1301 else if (sep->se_count >= sep->se_max) {
1302 unsigned now = monotonic_sec();
1303
1304 if (now - sep->se_time <= CNT_INTERVAL) {
1305 bb_error_msg("%s/%s: too many connections, pausing",
1306 sep->se_service, sep->se_proto);
1307 remove_fd_from_set(sep->se_fd);
1308 close(sep->se_fd);
1309 sep->se_fd = -1;
1310 sep->se_count = 0;
1311 rearm_alarm();
1312 restore_sigmask(&omask);
1313 maybe_close(new_udp_fd);
1314 maybe_close(accepted_fd);
1315 continue;
1316 }
1317 sep->se_count = 0;
1318 }
1319 }
1320
1321
1322
1323#ifdef INETD_BUILTINS_ENABLED
1324 if (BB_MMU && sep->se_builtin)
1325 pid = fork();
1326 else
1327#endif
1328 pid = vfork();
1329
1330 if (pid < 0) {
1331 bb_perror_msg("vfork"+1);
1332 sleep(1);
1333 restore_sigmask(&omask);
1334 maybe_close(new_udp_fd);
1335 maybe_close(accepted_fd);
1336 continue;
1337 }
1338 if (pid == 0)
1339 pid--;
1340 }
1341
1342
1343 if (pid > 0) {
1344 if (sep->se_wait) {
1345
1346
1347 sep->se_wait = pid;
1348 remove_fd_from_set(sep->se_fd);
1349 }
1350 if (new_udp_fd >= 0) {
1351
1352
1353 xmove_fd(new_udp_fd, sep->se_fd);
1354 dbg("moved new_udp_fd:%d to sep->se_fd:%d\n", new_udp_fd, sep->se_fd);
1355 }
1356 restore_sigmask(&omask);
1357 maybe_close(accepted_fd);
1358 continue;
1359 }
1360
1361
1362#ifdef INETD_BUILTINS_ENABLED
1363 if (sep->se_builtin) {
1364 if (pid) {
1365 close(sep->se_fd);
1366 dbg("closed sep->se_fd:%d\n", sep->se_fd);
1367 logmode = LOGMODE_NONE;
1368 }
1369 restore_sigmask(&omask);
1370 if (sep->se_socktype == SOCK_STREAM)
1371 sep->se_builtin->bi_stream_fn(ctrl, sep);
1372 else
1373 sep->se_builtin->bi_dgram_fn(ctrl, sep);
1374 if (pid)
1375 _exit(EXIT_FAILURE);
1376 maybe_close(accepted_fd);
1377 continue;
1378 }
1379#endif
1380
1381 setsid();
1382
1383 if (new_udp_fd >= 0) {
1384 len_and_sockaddr *lsa;
1385 int r;
1386
1387 close(new_udp_fd);
1388 dbg("closed new_udp_fd:%d\n", new_udp_fd);
1389 lsa = xzalloc_lsa(sep->se_family);
1390
1391 r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT,
1392 &lsa->u.sa, &lsa->len);
1393 if (r < 0)
1394 goto do_exit1;
1395
1396
1397
1398 connect(ctrl, &lsa->u.sa, lsa->len);
1399 dbg("connected ctrl:%d to remote peer\n", ctrl);
1400 free(lsa);
1401 }
1402
1403 pwd = getpwnam(sep->se_user);
1404 if (pwd == NULL) {
1405 bb_error_msg("%s: no such %s", sep->se_user, "user");
1406 goto do_exit1;
1407 }
1408 if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
1409 bb_error_msg("%s: no such %s", sep->se_group, "group");
1410 goto do_exit1;
1411 }
1412 if (real_uid != 0 && real_uid != pwd->pw_uid) {
1413
1414 bb_error_msg("non-root must run services as himself");
1415 goto do_exit1;
1416 }
1417 if (pwd->pw_uid != 0) {
1418 if (sep->se_group)
1419 pwd->pw_gid = grp->gr_gid;
1420
1421 change_identity(pwd);
1422 } else if (sep->se_group) {
1423 xsetgid(grp->gr_gid);
1424 setgroups(1, &grp->gr_gid);
1425 }
1426 if (rlim_ofile.rlim_cur != rlim_ofile_cur)
1427 if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
1428 bb_perror_msg("setrlimit");
1429
1430
1431
1432
1433
1434 xmove_fd(ctrl, STDIN_FILENO);
1435 xdup2(STDIN_FILENO, STDOUT_FILENO);
1436 dbg("moved ctrl:%d to fd 0,1[,2]\n", ctrl);
1437
1438
1439
1440 if (!sep->se_wait)
1441 xdup2(STDIN_FILENO, STDERR_FILENO);
1442
1443
1444 for (sep2 = serv_list; sep2; sep2 = sep2->se_next)
1445 if (sep2->se_fd != ctrl)
1446 maybe_close(sep2->se_fd);
1447 sigaction_set(SIGPIPE, &saved_pipe_handler);
1448 restore_sigmask(&omask);
1449 dbg("execing:'%s'\n", sep->se_program);
1450 BB_EXECVP(sep->se_program, sep->se_argv);
1451 bb_perror_msg("can't execute '%s'", sep->se_program);
1452 do_exit1:
1453
1454 if (sep->se_socktype != SOCK_STREAM)
1455 recv(0, line, LINE_SIZE, MSG_DONTWAIT);
1456 _exit(EXIT_FAILURE);
1457 }
1458 }
1459}
1460
1461#if !BB_MMU
1462static const char *const cat_args[] = { "cat", NULL };
1463#endif
1464
1465
1466
1467
1468#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
1469
1470
1471static void FAST_FUNC echo_stream(int s, servtab_t *sep UNUSED_PARAM)
1472{
1473#if BB_MMU
1474 while (1) {
1475 ssize_t sz = safe_read(s, line, LINE_SIZE);
1476 if (sz <= 0)
1477 break;
1478 xwrite(s, line, sz);
1479 }
1480#else
1481
1482
1483 xmove_fd(s, STDIN_FILENO);
1484 xdup2(STDIN_FILENO, STDOUT_FILENO);
1485
1486 close(STDERR_FILENO);
1487 xopen(bb_dev_null, O_WRONLY);
1488 BB_EXECVP("cat", (char**)cat_args);
1489
1490#endif
1491}
1492static void FAST_FUNC echo_dg(int s, servtab_t *sep)
1493{
1494 enum { BUFSIZE = 12*1024 };
1495 char *buf = xmalloc(BUFSIZE);
1496 int sz;
1497 len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
1498
1499 lsa->len = sep->se_lsa->len;
1500
1501 sz = recvfrom(s, buf, BUFSIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len);
1502 if (sz > 0)
1503 sendto(s, buf, sz, 0, &lsa->u.sa, lsa->len);
1504 free(buf);
1505}
1506#endif
1507
1508
1509#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
1510
1511
1512static void FAST_FUNC discard_stream(int s, servtab_t *sep UNUSED_PARAM)
1513{
1514#if BB_MMU
1515 while (safe_read(s, line, LINE_SIZE) > 0)
1516 continue;
1517#else
1518
1519
1520 xmove_fd(s, STDIN_FILENO);
1521
1522 close(STDOUT_FILENO);
1523 xopen(bb_dev_null, O_WRONLY);
1524
1525 xdup2(STDOUT_FILENO, STDERR_FILENO);
1526 BB_EXECVP("cat", (char**)cat_args);
1527
1528#endif
1529}
1530
1531static void FAST_FUNC discard_dg(int s, servtab_t *sep UNUSED_PARAM)
1532{
1533
1534 recv(s, line, LINE_SIZE, MSG_DONTWAIT);
1535}
1536#endif
1537
1538
1539#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
1540#define LINESIZ 72
1541static void init_ring(void)
1542{
1543 int i;
1544
1545 end_ring = ring;
1546 for (i = ' '; i < 127; i++)
1547 *end_ring++ = i;
1548}
1549
1550
1551static void FAST_FUNC chargen_stream(int s, servtab_t *sep UNUSED_PARAM)
1552{
1553 char *rs;
1554 int len;
1555 char text[LINESIZ + 2];
1556
1557 if (!end_ring) {
1558 init_ring();
1559 rs = ring;
1560 }
1561
1562 text[LINESIZ] = '\r';
1563 text[LINESIZ + 1] = '\n';
1564 rs = ring;
1565 for (;;) {
1566 len = end_ring - rs;
1567 if (len >= LINESIZ)
1568 memmove(text, rs, LINESIZ);
1569 else {
1570 memmove(text, rs, len);
1571 memmove(text + len, ring, LINESIZ - len);
1572 }
1573 if (++rs == end_ring)
1574 rs = ring;
1575 xwrite(s, text, sizeof(text));
1576 }
1577}
1578
1579static void FAST_FUNC chargen_dg(int s, servtab_t *sep)
1580{
1581 int len;
1582 char text[LINESIZ + 2];
1583 len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
1584
1585
1586
1587 lsa->len = sep->se_lsa->len;
1588 if (recvfrom(s, text, sizeof(text), MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
1589 return;
1590
1591 if (!end_ring) {
1592 init_ring();
1593 ring_pos = ring;
1594 }
1595
1596 len = end_ring - ring_pos;
1597 if (len >= LINESIZ)
1598 memmove(text, ring_pos, LINESIZ);
1599 else {
1600 memmove(text, ring_pos, len);
1601 memmove(text + len, ring, LINESIZ - len);
1602 }
1603 if (++ring_pos == end_ring)
1604 ring_pos = ring;
1605 text[LINESIZ] = '\r';
1606 text[LINESIZ + 1] = '\n';
1607 sendto(s, text, sizeof(text), 0, &lsa->u.sa, lsa->len);
1608}
1609#endif
1610
1611
1612#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
1613
1614
1615
1616
1617
1618
1619
1620static uint32_t machtime(void)
1621{
1622 struct timeval tv;
1623
1624 gettimeofday(&tv, NULL);
1625 return htonl((uint32_t)(tv.tv_sec + 2208988800));
1626}
1627
1628static void FAST_FUNC machtime_stream(int s, servtab_t *sep UNUSED_PARAM)
1629{
1630 uint32_t result;
1631
1632 result = machtime();
1633 full_write(s, &result, sizeof(result));
1634}
1635static void FAST_FUNC machtime_dg(int s, servtab_t *sep)
1636{
1637 uint32_t result;
1638 len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
1639
1640 lsa->len = sep->se_lsa->len;
1641 if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
1642 return;
1643
1644 result = machtime();
1645 sendto(s, &result, sizeof(result), 0, &lsa->u.sa, lsa->len);
1646}
1647#endif
1648
1649
1650#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
1651
1652
1653static void FAST_FUNC daytime_stream(int s, servtab_t *sep UNUSED_PARAM)
1654{
1655 time_t t;
1656
1657 t = time(NULL);
1658 fdprintf(s, "%.24s\r\n", ctime(&t));
1659}
1660static void FAST_FUNC daytime_dg(int s, servtab_t *sep)
1661{
1662 time_t t;
1663 len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
1664
1665 lsa->len = sep->se_lsa->len;
1666 if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
1667 return;
1668
1669 t = time(NULL);
1670 sprintf(line, "%.24s\r\n", ctime(&t));
1671 sendto(s, line, strlen(line), 0, &lsa->u.sa, lsa->len);
1672}
1673#endif
1674