3a6c68646d592477481d6aad9b69a73c38be2a87
[oweals/busybox.git] / networking / tcpudp.c
1 /* Based on ipsvd utilities written by Gerrit Pape <pape@smarden.org>
2  * which are released into public domain by the author.
3  * Homepage: http://smarden.sunsite.dk/ipsvd/
4  *
5  * Copyright (C) 2007 Denys Vlasenko.
6  *
7  * Licensed under GPLv2, see file LICENSE in this source tree.
8  */
9
10 /* Based on ipsvd-0.12.1. This tcpsvd accepts all options
11  * which are supported by one from ipsvd-0.12.1, but not all are
12  * functional. See help text at the end of this file for details.
13  *
14  * Code inside "#ifdef SSLSVD" is for sslsvd and is currently unused.
15  *
16  * Busybox version exports TCPLOCALADDR instead of
17  * TCPLOCALIP + TCPLOCALPORT pair. ADDR more closely matches reality
18  * (which is "struct sockaddr_XXX". Port is not a separate entity,
19  * it's just a part of (AF_INET[6]) sockaddr!).
20  *
21  * TCPORIGDSTADDR is Busybox-specific addition.
22  *
23  * udp server is hacked up by reusing TCP code. It has the following
24  * limitation inherent in Unix DGRAM sockets implementation:
25  * - local IP address is retrieved (using recvmsg voodoo) but
26  *   child's socket is not bound to it (bind cannot be called on
27  *   already bound socket). Thus it still can emit outgoing packets
28  *   with wrong source IP...
29  * - don't know how to retrieve ORIGDST for udp.
30  */
31 //config:config TCPSVD
32 //config:       bool "tcpsvd"
33 //config:       default y
34 //config:       help
35 //config:         tcpsvd listens on a TCP port and runs a program for each new
36 //config:         connection.
37 //config:
38 //config:config UDPSVD
39 //config:       bool "udpsvd"
40 //config:       default y
41 //config:       help
42 //config:         udpsvd listens on an UDP port and runs a program for each new
43 //config:         connection.
44
45 //applet:IF_TCPSVD(APPLET_ODDNAME(tcpsvd, tcpudpsvd, BB_DIR_USR_BIN, BB_SUID_DROP, tcpsvd))
46 //applet:IF_UDPSVD(APPLET_ODDNAME(udpsvd, tcpudpsvd, BB_DIR_USR_BIN, BB_SUID_DROP, udpsvd))
47
48 //kbuild:lib-$(CONFIG_TCPSVD) += tcpudp.o tcpudp_perhost.o
49 //kbuild:lib-$(CONFIG_UDPSVD) += tcpudp.o tcpudp_perhost.o
50
51 //usage:#define tcpsvd_trivial_usage
52 //usage:       "[-hEv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] IP PORT PROG"
53 /* with not-implemented options: */
54 /* //usage:    "[-hpEvv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] [-i DIR|-x CDB] [-t SEC] IP PORT PROG" */
55 //usage:#define tcpsvd_full_usage "\n\n"
56 //usage:       "Create TCP socket, bind to IP:PORT and listen for incoming connections.\n"
57 //usage:       "Run PROG for each connection.\n"
58 //usage:     "\n        IP PORT         IP:PORT to listen on"
59 //usage:     "\n        PROG ARGS       Program to run"
60 //usage:     "\n        -u USER[:GRP]   Change to user/group after bind"
61 //usage:     "\n        -c N            Up to N connections simultaneously (default 30)"
62 //usage:     "\n        -b N            Allow backlog of approximately N TCP SYNs (default 20)"
63 //usage:     "\n        -C N[:MSG]      Allow only up to N connections from the same IP:"
64 //usage:     "\n                        new connections from this IP address are closed"
65 //usage:     "\n                        immediately, MSG is written to the peer before close"
66 //usage:     "\n        -E              Don't set up environment"
67 //usage:     "\n        -h              Look up peer's hostname"
68 //usage:     "\n        -l NAME         Local hostname (else look up local hostname in DNS)"
69 //usage:     "\n        -v              Verbose"
70 //usage:     "\n"
71 //usage:     "\nEnvironment if no -E:"
72 //usage:     "\nPROTO='TCP'"
73 //usage:     "\nTCPREMOTEADDR='ip:port'" IF_FEATURE_IPV6(" ('[ip]:port' for IPv6)")
74 //usage:     "\nTCPLOCALADDR='ip:port'"
75 //usage:     "\nTCPORIGDSTADDR='ip:port' of destination before firewall"
76 //usage:     "\n        Useful for REDIRECTed-to-local connections:"
77 //usage:     "\n        iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to 8080"
78 //usage:     "\nTCPCONCURRENCY=num_of_connects_from_this_ip"
79 //usage:     "\nIf -h:"
80 //usage:     "\nTCPLOCALHOST='hostname' (-l NAME is used if specified)"
81 //usage:     "\nTCPREMOTEHOST='hostname'"
82
83 //usage:
84 //usage:#define udpsvd_trivial_usage
85 //usage:       "[-hEv] [-c N] [-u USER] [-l NAME] IP PORT PROG"
86 //usage:#define udpsvd_full_usage "\n\n"
87 //usage:       "Create UDP socket, bind to IP:PORT and wait for incoming packets.\n"
88 //usage:       "Run PROG for each packet, redirecting all further packets with same\n"
89 //usage:       "peer ip:port to it.\n"
90 //usage:     "\n        IP PORT         IP:PORT to listen on"
91 //usage:     "\n        PROG ARGS       Program to run"
92 //usage:     "\n        -u USER[:GRP]   Change to user/group after bind"
93 //usage:     "\n        -c N            Up to N connections simultaneously (default 30)"
94 //usage:     "\n        -E              Don't set up environment"
95 //usage:     "\n        -h              Look up peer's hostname"
96 //usage:     "\n        -l NAME         Local hostname (else look up local hostname in DNS)"
97 //usage:     "\n        -v              Verbose"
98 //usage:     "\n"
99 //usage:     "\nEnvironment if no -E:"
100 //usage:     "\nPROTO='UDP'"
101 //usage:     "\nUDPREMOTEADDR='ip:port'" IF_FEATURE_IPV6(" ('[ip]:port' for IPv6)")
102 //usage:     "\nUDPLOCALADDR='ip:port'"
103 //usage:     "\nIf -h:"
104 //usage:     "\nUDPLOCALHOST='hostname' (-l NAME is used if specified)"
105 //usage:     "\nUDPREMOTEHOST='hostname'"
106
107 #include "libbb.h"
108 #include "common_bufsiz.h"
109
110 #ifdef __linux__
111 /* from linux/netfilter_ipv4.h: */
112 # undef  SO_ORIGINAL_DST
113 # define SO_ORIGINAL_DST 80
114 #endif
115
116 // TODO: move into this file:
117 #include "tcpudp_perhost.h"
118
119 #ifdef SSLSVD
120 #include "matrixSsl.h"
121 #include "ssl_io.h"
122 #endif
123
124 struct globals {
125         unsigned verbose;
126         unsigned max_per_host;
127         unsigned cur_per_host;
128         unsigned cnum;
129         unsigned cmax;
130         char **env_cur;
131         char *env_var[1]; /* actually bigger */
132 } FIX_ALIASING;
133 #define G (*(struct globals*)bb_common_bufsiz1)
134 #define verbose      (G.verbose     )
135 #define max_per_host (G.max_per_host)
136 #define cur_per_host (G.cur_per_host)
137 #define cnum         (G.cnum        )
138 #define cmax         (G.cmax        )
139 #define env_cur      (G.env_cur     )
140 #define env_var      (G.env_var     )
141 #define INIT_G() do { \
142         setup_common_bufsiz(); \
143         cmax = 30; \
144         env_cur = &env_var[0]; \
145 } while (0)
146
147
148 /* We have to be careful about leaking memory in repeated setenv's */
149 static void xsetenv_plain(const char *n, const char *v)
150 {
151         char *var = xasprintf("%s=%s", n, v);
152         *env_cur++ = var;
153         putenv(var);
154 }
155
156 static void xsetenv_proto(const char *proto, const char *n, const char *v)
157 {
158         char *var = xasprintf("%s%s=%s", proto, n, v);
159         *env_cur++ = var;
160         putenv(var);
161 }
162
163 static void undo_xsetenv(void)
164 {
165         char **pp = env_cur = &env_var[0];
166         while (*pp) {
167                 char *var = *pp;
168                 bb_unsetenv_and_free(var);
169                 *pp++ = NULL;
170         }
171 }
172
173 static void sig_term_handler(int sig)
174 {
175         if (verbose)
176                 bb_error_msg("got signal %u, exit", sig);
177         kill_myself_with_sig(sig);
178 }
179
180 /* Little bloated, but tries to give accurate info how child exited.
181  * Makes easier to spot segfaulting children etc... */
182 static void print_waitstat(unsigned pid, int wstat)
183 {
184         unsigned e = 0;
185         const char *cause = "?exit";
186
187         if (WIFEXITED(wstat)) {
188                 cause++;
189                 e = WEXITSTATUS(wstat);
190         } else if (WIFSIGNALED(wstat)) {
191                 cause = "signal";
192                 e = WTERMSIG(wstat);
193         }
194         bb_error_msg("end %d %s %d", pid, cause, e);
195 }
196
197 /* Must match getopt32 in main! */
198 enum {
199         OPT_c = (1 << 0),
200         OPT_C = (1 << 1),
201         OPT_i = (1 << 2),
202         OPT_x = (1 << 3),
203         OPT_u = (1 << 4),
204         OPT_l = (1 << 5),
205         OPT_E = (1 << 6),
206         OPT_b = (1 << 7),
207         OPT_h = (1 << 8),
208         OPT_p = (1 << 9),
209         OPT_t = (1 << 10),
210         OPT_v = (1 << 11),
211         OPT_V = (1 << 12),
212         OPT_U = (1 << 13), /* from here: sslsvd only */
213         OPT_slash = (1 << 14),
214         OPT_Z = (1 << 15),
215         OPT_K = (1 << 16),
216 };
217
218 static void connection_status(void)
219 {
220         /* "only 1 client max" desn't need this */
221         if (cmax > 1)
222                 bb_error_msg("status %u/%u", cnum, cmax);
223 }
224
225 static void sig_child_handler(int sig UNUSED_PARAM)
226 {
227         int wstat;
228         pid_t pid;
229
230         while ((pid = wait_any_nohang(&wstat)) > 0) {
231                 if (max_per_host)
232                         ipsvd_perhost_remove(pid);
233                 if (cnum)
234                         cnum--;
235                 if (verbose)
236                         print_waitstat(pid, wstat);
237         }
238         if (verbose)
239                 connection_status();
240 }
241
242 int tcpudpsvd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
243 int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
244 {
245         char *str_C, *str_t;
246         char *user;
247         struct hcc *hccp;
248         const char *instructs;
249         char *msg_per_host = NULL;
250         unsigned len_per_host = len_per_host; /* gcc */
251 #ifndef SSLSVD
252         struct bb_uidgid_t ugid;
253 #endif
254         bool tcp;
255         uint16_t local_port;
256         char *preset_local_hostname = NULL;
257         char *remote_hostname = remote_hostname; /* for compiler */
258         char *remote_addr = remote_addr; /* for compiler */
259         len_and_sockaddr *lsa;
260         len_and_sockaddr local, remote;
261         socklen_t sa_len;
262         int pid;
263         int sock;
264         int conn;
265         unsigned backlog = 20;
266         unsigned opts;
267
268         INIT_G();
269
270         tcp = (applet_name[0] == 't');
271
272         /* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */
273         opt_complementary = "-3:i--i:ph:vv";
274 #ifdef SSLSVD
275         opts = getopt32(argv, "+c:+C:i:x:u:l:Eb:+hpt:vU:/:Z:K:",
276                 &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
277                 &backlog, &str_t, &ssluser, &root, &cert, &key, &verbose
278         );
279 #else
280         /* "+": stop on first non-option */
281         opts = getopt32(argv, "+c:+C:i:x:u:l:Eb:hpt:v",
282                 &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
283                 &backlog, &str_t, &verbose
284         );
285 #endif
286         if (opts & OPT_C) { /* -C n[:message] */
287                 max_per_host = bb_strtou(str_C, &str_C, 10);
288                 if (str_C[0]) {
289                         if (str_C[0] != ':')
290                                 bb_show_usage();
291                         msg_per_host = str_C + 1;
292                         len_per_host = strlen(msg_per_host);
293                 }
294         }
295         if (max_per_host > cmax)
296                 max_per_host = cmax;
297         if (opts & OPT_u) {
298                 xget_uidgid(&ugid, user);
299         }
300 #ifdef SSLSVD
301         if (opts & OPT_U) ssluser = optarg;
302         if (opts & OPT_slash) root = optarg;
303         if (opts & OPT_Z) cert = optarg;
304         if (opts & OPT_K) key = optarg;
305 #endif
306         argv += optind;
307         if (!argv[0][0] || LONE_CHAR(argv[0], '0'))
308                 argv[0] = (char*)"0.0.0.0";
309
310         /* Per-IP flood protection is not thought-out for UDP */
311         if (!tcp)
312                 max_per_host = 0;
313
314         bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */
315
316 #ifdef SSLSVD
317         sslser = user;
318         client = 0;
319         if ((getuid() == 0) && !(opts & OPT_u)) {
320                 xfunc_exitcode = 100;
321                 bb_error_msg_and_die(bb_msg_you_must_be_root);
322         }
323         if (opts & OPT_u)
324                 if (!uidgid_get(&sslugid, ssluser, 1)) {
325                         if (errno) {
326                                 bb_perror_msg_and_die("can't get user/group: %s", ssluser);
327                         }
328                         bb_error_msg_and_die("unknown user/group %s", ssluser);
329                 }
330         if (!cert) cert = "./cert.pem";
331         if (!key) key = cert;
332         if (matrixSslOpen() < 0)
333                 fatal("can't initialize ssl");
334         if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) {
335                 if (client)
336                         fatal("can't read cert, key, or ca file");
337                 fatal("can't read cert or key file");
338         }
339         if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0)
340                 fatal("can't create ssl session");
341 #endif
342
343         sig_block(SIGCHLD);
344         signal(SIGCHLD, sig_child_handler);
345         bb_signals(BB_FATAL_SIGS, sig_term_handler);
346         signal(SIGPIPE, SIG_IGN);
347
348         if (max_per_host)
349                 ipsvd_perhost_init(cmax);
350
351         local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0);
352         lsa = xhost2sockaddr(argv[0], local_port);
353         argv += 2;
354
355         sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
356         setsockopt_reuseaddr(sock);
357         sa_len = lsa->len; /* I presume sockaddr len stays the same */
358         xbind(sock, &lsa->u.sa, sa_len);
359         if (tcp) {
360                 xlisten(sock, backlog);
361                 close_on_exec_on(sock);
362         } else { /* udp: needed for recv_from_to to work: */
363                 socket_want_pktinfo(sock);
364         }
365         /* ndelay_off(sock); - it is the default I think? */
366
367 #ifndef SSLSVD
368         if (opts & OPT_u) {
369                 /* drop permissions */
370                 xsetgid(ugid.gid);
371                 xsetuid(ugid.uid);
372         }
373 #endif
374
375         if (verbose) {
376                 char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa);
377                 if (opts & OPT_u)
378                         bb_error_msg("listening on %s, starting, uid %u, gid %u", addr,
379                                 (unsigned)ugid.uid, (unsigned)ugid.gid);
380                 else
381                         bb_error_msg("listening on %s, starting", addr);
382                 free(addr);
383         }
384
385         /* Main accept() loop */
386
387  again:
388         hccp = NULL;
389
390  again1:
391         close(0);
392         /* It's important to close(0) _before_ wait loop:
393          * fd#0 can be a shared connection fd.
394          * If kept open by us, peer can't detect PROG closing it.
395          */
396         while (cnum >= cmax)
397                 wait_for_any_sig(); /* expecting SIGCHLD */
398
399  again2:
400         sig_unblock(SIGCHLD);
401         local.len = remote.len = sa_len;
402         if (tcp) {
403                 /* Accept a connection to fd #0 */
404                 conn = accept(sock, &remote.u.sa, &remote.len);
405         } else {
406                 /* In case recv_from_to won't be able to recover local addr.
407                  * Also sets port - recv_from_to is unable to do it. */
408                 local = *lsa;
409                 conn = recv_from_to(sock, NULL, 0, MSG_PEEK,
410                                 &remote.u.sa, &local.u.sa, sa_len);
411         }
412         sig_block(SIGCHLD);
413         if (conn < 0) {
414                 if (errno != EINTR)
415                         bb_perror_msg(tcp ? "accept" : "recv");
416                 goto again2;
417         }
418         xmove_fd(tcp ? conn : sock, 0);
419
420         if (max_per_host) {
421                 /* Drop connection immediately if cur_per_host > max_per_host
422                  * (minimizing load under SYN flood) */
423                 remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa);
424                 cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp);
425                 if (cur_per_host > max_per_host) {
426                         /* ipsvd_perhost_add detected that max is exceeded
427                          * (and did not store ip in connection table) */
428                         free(remote_addr);
429                         if (msg_per_host) {
430                                 /* don't block or test for errors */
431                                 send(0, msg_per_host, len_per_host, MSG_DONTWAIT);
432                         }
433                         goto again1;
434                 }
435                 /* NB: remote_addr is not leaked, it is stored in conn table */
436         }
437
438         if (!tcp) {
439                 /* Voodoo magic: making udp sockets each receive its own
440                  * packets is not trivial, and I still not sure
441                  * I do it 100% right.
442                  * 1) we have to do it before fork()
443                  * 2) order is important - is it right now? */
444
445                 /* Open new non-connected UDP socket for further clients... */
446                 sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
447                 setsockopt_reuseaddr(sock);
448                 /* Make plain write/send work for old socket by supplying default
449                  * destination address. This also restricts incoming packets
450                  * to ones coming from this remote IP. */
451                 xconnect(0, &remote.u.sa, sa_len);
452         /* hole? at this point we have no wildcard udp socket...
453          * can this cause clients to get "port unreachable" icmp?
454          * Yup, time window is very small, but it exists (is it?) */
455                 /* ..."open new socket", continued */
456                 xbind(sock, &lsa->u.sa, sa_len);
457                 socket_want_pktinfo(sock);
458
459                 /* Doesn't work:
460                  * we cannot replace fd #0 - we will lose pending packet
461                  * which is already buffered for us! And we cannot use fd #1
462                  * instead - it will "intercept" all following packets, but child
463                  * does not expect data coming *from fd #1*! */
464 #if 0
465                 /* Make it so that local addr is fixed to localp->u.sa
466                  * and we don't accidentally accept packets to other local IPs. */
467                 /* NB: we possibly bind to the _very_ same_ address & port as the one
468                  * already bound in parent! This seems to work in Linux.
469                  * (otherwise we can move socket to fd #0 only if bind succeeds) */
470                 close(0);
471                 set_nport(&localp->u.sa, htons(local_port));
472                 xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0);
473                 setsockopt_reuseaddr(0); /* crucial */
474                 xbind(0, &localp->u.sa, localp->len);
475 #endif
476         }
477
478         pid = vfork();
479         if (pid == -1) {
480                 bb_perror_msg("vfork");
481                 goto again;
482         }
483
484         if (pid != 0) {
485                 /* Parent */
486                 cnum++;
487                 if (verbose)
488                         connection_status();
489                 if (hccp)
490                         hccp->pid = pid;
491                 /* clean up changes done by vforked child */
492                 undo_xsetenv();
493                 goto again;
494         }
495
496         /* Child: prepare env, log, and exec prog */
497
498         { /* vfork alert! every xmalloc in this block should be freed! */
499                 char *local_hostname = local_hostname; /* for compiler */
500                 char *local_addr = NULL;
501                 char *free_me0 = NULL;
502                 char *free_me1 = NULL;
503                 char *free_me2 = NULL;
504
505                 if (verbose || !(opts & OPT_E)) {
506                         if (!max_per_host) /* remote_addr is not yet known */
507                                 free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa);
508                         if (opts & OPT_h) {
509                                 free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa);
510                                 if (!remote_hostname) {
511                                         bb_error_msg("can't look up hostname for %s", remote_addr);
512                                         remote_hostname = remote_addr;
513                                 }
514                         }
515                         /* Find out local IP peer connected to.
516                          * Errors ignored (I'm not paranoid enough to imagine kernel
517                          * which doesn't know local IP). */
518                         if (tcp)
519                                 getsockname(0, &local.u.sa, &local.len);
520                         /* else: for UDP it is done earlier by parent */
521                         local_addr = xmalloc_sockaddr2dotted(&local.u.sa);
522                         if (opts & OPT_h) {
523                                 local_hostname = preset_local_hostname;
524                                 if (!local_hostname) {
525                                         free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa);
526                                         if (!local_hostname)
527                                                 bb_error_msg_and_die("can't look up hostname for %s", local_addr);
528                                 }
529                                 /* else: local_hostname is not NULL, but is NOT malloced! */
530                         }
531                 }
532                 if (verbose) {
533                         pid = getpid();
534                         if (max_per_host) {
535                                 bb_error_msg("concurrency %s %u/%u",
536                                         remote_addr,
537                                         cur_per_host, max_per_host);
538                         }
539                         bb_error_msg((opts & OPT_h)
540                                 ? "start %u %s-%s (%s-%s)"
541                                 : "start %u %s-%s",
542                                 pid,
543                                 local_addr, remote_addr,
544                                 local_hostname, remote_hostname);
545                 }
546
547                 if (!(opts & OPT_E)) {
548                         /* setup ucspi env */
549                         const char *proto = tcp ? "TCP" : "UDP";
550
551 #ifdef SO_ORIGINAL_DST
552                         /* Extract "original" destination addr:port
553                          * from Linux firewall. Useful when you redirect
554                          * an outbond connection to local handler, and it needs
555                          * to know where it originally tried to connect */
556                         if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) {
557                                 char *addr = xmalloc_sockaddr2dotted(&local.u.sa);
558                                 xsetenv_plain("TCPORIGDSTADDR", addr);
559                                 free(addr);
560                         }
561 #endif
562                         xsetenv_plain("PROTO", proto);
563                         xsetenv_proto(proto, "LOCALADDR", local_addr);
564                         xsetenv_proto(proto, "REMOTEADDR", remote_addr);
565                         if (opts & OPT_h) {
566                                 xsetenv_proto(proto, "LOCALHOST", local_hostname);
567                                 xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
568                         }
569                         //compat? xsetenv_proto(proto, "REMOTEINFO", "");
570                         /* additional */
571                         if (cur_per_host > 0) /* can not be true for udp */
572                                 xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host));
573                 }
574                 free(local_addr);
575                 free(free_me0);
576                 free(free_me1);
577                 free(free_me2);
578         }
579
580         xdup2(0, 1);
581
582         signal(SIGPIPE, SIG_DFL); /* this one was SIG_IGNed */
583         /* Non-ignored signals revert to SIG_DFL on exec anyway */
584         /*signal(SIGCHLD, SIG_DFL);*/
585         sig_unblock(SIGCHLD);
586
587 #ifdef SSLSVD
588         strcpy(id, utoa(pid));
589         ssl_io(0, argv);
590         bb_perror_msg_and_die("can't execute '%s'", argv[0]);
591 #else
592         BB_EXECVP_or_die(argv);
593 #endif
594 }
595
596 /*
597 tcpsvd [-hpEvv] [-c n] [-C n:msg] [-b n] [-u user] [-l name]
598         [-i dir|-x cdb] [ -t sec] host port prog
599
600 tcpsvd creates a TCP/IP socket, binds it to the address host:port,
601 and listens on the socket for incoming connections.
602
603 On each incoming connection, tcpsvd conditionally runs a program,
604 with standard input reading from the socket, and standard output
605 writing to the socket, to handle this connection. tcpsvd keeps
606 listening on the socket for new connections, and can handle
607 multiple connections simultaneously.
608
609 tcpsvd optionally checks for special instructions depending
610 on the IP address or hostname of the client that initiated
611 the connection, see ipsvd-instruct(5).
612
613 host
614     host either is a hostname, or a dotted-decimal IP address,
615     or 0. If host is 0, tcpsvd accepts connections to any local
616     IP address.
617     * busybox accepts IPv6 addresses and host:port pairs too
618       In this case second parameter is ignored
619 port
620     tcpsvd accepts connections to host:port. port may be a name
621     from /etc/services or a number.
622 prog
623     prog consists of one or more arguments. For each connection,
624     tcpsvd normally runs prog, with file descriptor 0 reading from
625     the network, and file descriptor 1 writing to the network.
626     By default it also sets up TCP-related environment variables,
627     see tcp-environ(5)
628 -i dir
629     read instructions for handling new connections from the instructions
630     directory dir. See ipsvd-instruct(5) for details.
631     * ignored by busyboxed version
632 -x cdb
633     read instructions for handling new connections from the constant database
634     cdb. The constant database normally is created from an instructions
635     directory by running ipsvd-cdb(8).
636     * ignored by busyboxed version
637 -t sec
638     timeout. This option only takes effect if the -i option is given.
639     While checking the instructions directory, check the time of last access
640     of the file that matches the clients address or hostname if any, discard
641     and remove the file if it wasn't accessed within the last sec seconds;
642     tcpsvd does not discard or remove a file if the user's write permission
643     is not set, for those files the timeout is disabled. Default is 0,
644     which means that the timeout is disabled.
645     * ignored by busyboxed version
646 -l name
647     local hostname. Do not look up the local hostname in DNS, but use name
648     as hostname. This option must be set if tcpsvd listens on port 53
649     to avoid loops.
650 -u user[:group]
651     drop permissions. Switch user ID to user's UID, and group ID to user's
652     primary GID after creating and binding to the socket. If user is followed
653     by a colon and a group name, the group ID is switched to the GID of group
654     instead. All supplementary groups are removed.
655 -c n
656     concurrency. Handle up to n connections simultaneously. Default is 30.
657     If there are n connections active, tcpsvd defers acceptance of a new
658     connection until an active connection is closed.
659 -C n[:msg]
660     per host concurrency. Allow only up to n connections from the same IP
661     address simultaneously. If there are n active connections from one IP
662     address, new incoming connections from this IP address are closed
663     immediately. If n is followed by :msg, the message msg is written
664     to the client if possible, before closing the connection. By default
665     msg is empty. See ipsvd-instruct(5) for supported escape sequences in msg.
666
667     For each accepted connection, the current per host concurrency is
668     available through the environment variable TCPCONCURRENCY. n and msg
669     can be overwritten by ipsvd(7) instructions, see ipsvd-instruct(5).
670     By default tcpsvd doesn't keep track of connections.
671 -h
672     Look up the client's hostname in DNS.
673 -p
674     paranoid. After looking up the client's hostname in DNS, look up the IP
675     addresses in DNS for that hostname, and forget about the hostname
676     if none of the addresses match the client's IP address. You should
677     set this option if you use hostname based instructions. The -p option
678     implies the -h option.
679     * ignored by busyboxed version
680 -b n
681     backlog. Allow a backlog of approximately n TCP SYNs. On some systems n
682     is silently limited. Default is 20.
683 -E
684     no special environment. Do not set up TCP-related environment variables.
685 -v
686     verbose. Print verbose messsages to standard output.
687 -vv
688     more verbose. Print more verbose messages to standard output.
689     * no difference between -v and -vv in busyboxed version
690 */