blob: 9f098c32ebbda91cf6a802441afa54565eaaf0ed [file] [log] [blame]
Denis Vlasenkob933ac12007-04-03 12:09:46 +00001/* 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 Denis Vlasenko.
6 *
7 * Licensed under GPLv2, see file LICENSE in this tarball for details.
8 */
9
Denis Vlasenko02fd6682007-04-03 23:23:10 +000010/* Based on ipsvd 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 * Output of verbose mode matches original (modulo bugs and
17 * unimplemented stuff). Unnatural splitting of IP and PORT
18 * is retained (personally I prefer one-value "IP:PORT" notation -
19 * it is a natural string representation of struct sockaddr_XX).
20 *
21 * TCPORIGDST{IP,PORT} 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:
Denis Vlasenko64a15122007-04-04 10:16:15 +000025 * - local IP address is retrieved (using recvmsg voodoo) but
Denis Vlasenko02fd6682007-04-03 23:23:10 +000026 * child's socket is not bound to it (bind cannot be called on
Denis Vlasenko64a15122007-04-04 10:16:15 +000027 * already bound socket). Thus it still can emit outgoing packets
Denis Vlasenko02fd6682007-04-03 23:23:10 +000028 * with wrong sorce IP...
29 * - don't know how to retrieve ORIGDST for udp.
30 */
31
32#include <limits.h>
33#include <linux/netfilter_ipv4.h> /* wants <limits.h> */
Denis Vlasenkob933ac12007-04-03 12:09:46 +000034
35#include "busybox.h"
36
Denis Vlasenko02fd6682007-04-03 23:23:10 +000037#include "udp_io.c"
38#include "ipsvd_perhost.h"
39
40#ifdef SSLSVD
41#include "matrixSsl.h"
42#include "ssl_io.h"
43#endif
44
Denis Vlasenkob933ac12007-04-03 12:09:46 +000045static unsigned verbose;
Denis Vlasenko02fd6682007-04-03 23:23:10 +000046static unsigned max_per_host;
47static unsigned cur_per_host;
48static unsigned cnum;
49static unsigned cmax = 30;
50
51static void xsetenv_proto(const char *proto, const char *n, const char *v)
52{
53 putenv(xasprintf("%s%s=%s", proto, n, v));
54}
Denis Vlasenkob933ac12007-04-03 12:09:46 +000055
56static void sig_term_handler(int sig)
57{
58 if (verbose)
59 printf("%s: info: sigterm received, exit\n", applet_name);
60 exit(0);
61}
62
63/* Little bloated, but tries to give accurate info how child exited.
64 * Makes easier to spot segfaulting children etc... */
65static void print_waitstat(unsigned pid, int wstat)
66{
67 unsigned e = 0;
68 const char *cause = "?exit";
69
70 if (WIFEXITED(wstat)) {
71 cause++;
72 e = WEXITSTATUS(wstat);
73 } else if (WIFSIGNALED(wstat)) {
74 cause = "signal";
75 e = WTERMSIG(wstat);
76 }
77 printf("%s: info: end %d %s %d\n", applet_name, pid, cause, e);
78}
79
Denis Vlasenkob933ac12007-04-03 12:09:46 +000080/* Must match getopt32 in main! */
81enum {
82 OPT_c = (1 << 0),
83 OPT_C = (1 << 1),
84 OPT_i = (1 << 2),
85 OPT_x = (1 << 3),
86 OPT_u = (1 << 4),
87 OPT_l = (1 << 5),
88 OPT_E = (1 << 6),
89 OPT_b = (1 << 7),
90 OPT_h = (1 << 8),
91 OPT_p = (1 << 9),
92 OPT_t = (1 << 10),
93 OPT_v = (1 << 11),
94 OPT_V = (1 << 12),
95 OPT_U = (1 << 13), /* from here: sslsvd only */
96 OPT_slash = (1 << 14),
97 OPT_Z = (1 << 15),
98 OPT_K = (1 << 16),
99};
100
101static void connection_status(void)
102{
Denis Vlasenko64a15122007-04-04 10:16:15 +0000103 /* "only 1 client max" don't need this */
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000104 if (cmax > 1)
105 printf("%s: info: status %u/%u\n", applet_name, cnum, cmax);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000106}
107
108static void sig_child_handler(int sig)
109{
110 int wstat;
111 int pid;
112
113 while ((pid = wait_nohang(&wstat)) > 0) {
114 if (max_per_host)
115 ipsvd_perhost_remove(pid);
116 if (cnum)
117 cnum--;
118 if (verbose)
119 print_waitstat(pid, wstat);
120 }
121 if (verbose)
122 connection_status();
123}
124
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000125int tcpudpsvd_main(int argc, char **argv);
126int tcpudpsvd_main(int argc, char **argv)
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000127{
128 char *str_c, *str_C, *str_b, *str_t;
129 char *user;
130 struct hcc *hccp;
131 const char *instructs;
132 char *msg_per_host = NULL;
133 unsigned len_per_host = len_per_host; /* gcc */
Denis Vlasenko64a15122007-04-04 10:16:15 +0000134#ifndef SSLSVD
135 struct bb_uidgid_t ugid;
136#endif
137 bool need_hostnames, need_remote_ip, tcp;
138 uint16_t local_port;
139 char *local_hostname = NULL;
140 char *remote_hostname = (char*)""; /* "" used if no -h */
141 char *local_addr = local_addr; /* gcc */
142 char *remote_addr = remote_addr; /* gcc */
143 char *remote_ip = remote_addr; /* gcc */
144 len_and_sockaddr *lsa;
145 len_and_sockaddr local, remote;
146 socklen_t sa_len;
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000147 int pid;
148 int sock;
149 int conn;
150 unsigned backlog = 20;
Denis Vlasenko64a15122007-04-04 10:16:15 +0000151
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000152 tcp = (applet_name[0] == 't');
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000153
154 /* 3+ args, -i at most once, -p implies -h, -v is counter */
155 opt_complementary = "-3:?:i--i:ph:vv";
156#ifdef SSLSVD
Denis Vlasenko64a15122007-04-04 10:16:15 +0000157 getopt32(argc, argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000158 &str_c, &str_C, &instructs, &instructs, &user, &local_hostname,
159 &str_b, &str_t, &ssluser, &root, &cert, &key, &verbose
160 );
161#else
Denis Vlasenko64a15122007-04-04 10:16:15 +0000162 getopt32(argc, argv, "+c:C:i:x:u:l:Eb:hpt:v",
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000163 &str_c, &str_C, &instructs, &instructs, &user, &local_hostname,
164 &str_b, &str_t, &verbose
165 );
166#endif
167 if (option_mask32 & OPT_c)
168 cmax = xatou_range(str_c, 1, INT_MAX);
169 if (option_mask32 & OPT_C) { /* -C n[:message] */
170 max_per_host = bb_strtou(str_C, &str_C, 10);
171 if (str_C[0]) {
172 if (str_C[0] != ':')
173 bb_show_usage();
174 msg_per_host = str_C + 1;
175 len_per_host = strlen(msg_per_host);
176 }
177 }
178 if (max_per_host > cmax)
179 max_per_host = cmax;
180 if (option_mask32 & OPT_u) {
181 if (!get_uidgid(&ugid, user, 1))
182 bb_error_msg_and_die("unknown user/group: %s", user);
183 }
184 if (option_mask32 & OPT_b)
185 backlog = xatou(str_b);
186#ifdef SSLSVD
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000187 if (option_mask32 & OPT_U) ssluser = optarg;
188 if (option_mask32 & OPT_slash) root = optarg;
189 if (option_mask32 & OPT_Z) cert = optarg;
190 if (option_mask32 & OPT_K) key = optarg;
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000191#endif
192 argv += optind;
193 if (!argv[0][0] || LONE_CHAR(argv[0], '0'))
194 argv[0] = (char*)"0.0.0.0";
195
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000196 /* Per-IP flood protection is not thought-out for UDP */
197 if (!tcp)
198 max_per_host = 0;
199
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000200 /* stdout is used for logging, don't buffer */
201 setlinebuf(stdout);
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000202 bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000203
204 need_hostnames = verbose || !(option_mask32 & OPT_E);
205 need_remote_ip = max_per_host || need_hostnames;
206
207#ifdef SSLSVD
208 sslser = user;
209 client = 0;
210 if ((getuid() == 0) && !(option_mask32 & OPT_u)) {
211 xfunc_exitcode = 100;
212 bb_error_msg_and_die("fatal: -U ssluser must be set when running as root");
213 }
214 if (option_mask32 & OPT_u)
215 if (!uidgid_get(&sslugid, ssluser, 1)) {
216 if (errno) {
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000217 bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser);
218 }
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000219 bb_error_msg_and_die("fatal: unknown user/group '%s'", ssluser);
220 }
221 if (!cert) cert = "./cert.pem";
222 if (!key) key = cert;
223 if (matrixSslOpen() < 0)
224 fatal("cannot initialize ssl");
225 if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) {
226 if (client)
227 fatal("cannot read cert, key, or ca file");
228 fatal("cannot read cert or key file");
229 }
230 if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0)
231 fatal("cannot create ssl session");
232#endif
233
234 sig_block(SIGCHLD);
235 signal(SIGCHLD, sig_child_handler);
236 signal(SIGTERM, sig_term_handler);
237 signal(SIGPIPE, SIG_IGN);
238
239 if (max_per_host)
240 ipsvd_perhost_init(cmax);
241
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000242 local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000243 lsa = xhost2sockaddr(argv[0], local_port);
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000244 sock = xsocket(lsa->sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
245 setsockopt_reuseaddr(sock);
Denis Vlasenko64a15122007-04-04 10:16:15 +0000246 sa_len = lsa->len; /* I presume sockaddr len stays the same */
247 xbind(sock, &lsa->sa, sa_len);
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000248 if (tcp)
249 xlisten(sock, backlog);
250 else /* udp: needed for recv_from_to to work: */
251 socket_want_pktinfo(sock);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000252 /* ndelay_off(sock); - it is the default I think? */
253
254#ifndef SSLSVD
255 if (option_mask32 & OPT_u) {
256 /* drop permissions */
257 xsetgid(ugid.gid);
258 xsetuid(ugid.uid);
259 }
260#endif
261
262 if (verbose) {
Denis Vlasenko64a15122007-04-04 10:16:15 +0000263 char *addr = xmalloc_sockaddr2dotted(&lsa->sa, sa_len);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000264 printf("%s: info: listening on %s", applet_name, addr);
265 free(addr);
266#ifndef SSLSVD
267 if (option_mask32 & OPT_u)
268 printf(", uid %u, gid %u",
269 (unsigned)ugid.uid, (unsigned)ugid.gid);
270#endif
271 puts(", starting");
272 }
273
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000274 /* Main accept() loop */
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000275
276 again:
277 hccp = NULL;
278
279 while (cnum >= cmax)
280 sig_pause(); /* wait for any signal (expecting SIGCHLD) */
281
282 /* Accept a connection to fd #0 */
283 again1:
284 close(0);
285 again2:
286 sig_unblock(SIGCHLD);
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000287 if (tcp) {
Denis Vlasenko64a15122007-04-04 10:16:15 +0000288 remote.len = sa_len;
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000289 conn = accept(sock, &remote.sa, &remote.len);
Denis Vlasenko64a15122007-04-04 10:16:15 +0000290 } else {
291 /* In case we won't be able to recover local below.
292 * Also sets port - recv_from_to is unable to do it. */
293 local = *lsa;
294 conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.sa, &local.sa, sa_len);
295 }
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000296 sig_block(SIGCHLD);
297 if (conn < 0) {
298 if (errno != EINTR)
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000299 bb_perror_msg(tcp ? "accept" : "recv");
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000300 goto again2;
301 }
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000302 xmove_fd(tcp ? conn : sock, 0);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000303
304 if (max_per_host) {
305 /* Drop connection immediately if cur_per_host > max_per_host
306 * (minimizing load under SYN flood) */
Denis Vlasenko64a15122007-04-04 10:16:15 +0000307 remote_ip = xmalloc_sockaddr2dotted_noport(&remote.sa, sa_len);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000308 cur_per_host = ipsvd_perhost_add(remote_ip, max_per_host, &hccp);
309 if (cur_per_host > max_per_host) {
310 /* ipsvd_perhost_add detected that max is exceeded
311 * (and did not store ip in connection table) */
312 free(remote_ip);
313 if (msg_per_host) {
314 /* don't block or test for errors */
315 ndelay_on(0);
316 write(0, msg_per_host, len_per_host);
317 }
318 goto again1;
319 }
320 }
321
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000322 if (!tcp) {
323 /* Voodoo magic: making udp sockets each receive its own
Denis Vlasenko64a15122007-04-04 10:16:15 +0000324 * packets is not trivial, and I still not sure
325 * I do it 100% right.
326 * 1) we have to do it before fork()
327 * 2) order is important - is it right now? */
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000328
Denis Vlasenko64a15122007-04-04 10:16:15 +0000329 /* Make plain write/send work for this socket by supplying default
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000330 * destination address. This also restricts incoming packets
331 * to ones coming from this remote IP. */
Denis Vlasenko64a15122007-04-04 10:16:15 +0000332 xconnect(0, &remote.sa, sa_len);
333 /* hole? at this point we have no wildcard udp socket...
334 * can this cause clients to get "port unreachable" icmp? */
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000335 /* Open new non-connected UDP socket for further clients */
336 sock = xsocket(lsa->sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
337 setsockopt_reuseaddr(sock);
Denis Vlasenko64a15122007-04-04 10:16:15 +0000338 xbind(sock, &lsa->sa, sa_len);
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000339 socket_want_pktinfo(sock);
340 }
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000341
342 pid = fork();
343 if (pid == -1) {
344 bb_perror_msg("fork");
345 goto again;
346 }
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000347
348
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000349 if (pid != 0) {
350 /* parent */
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000351 cnum++;
352 if (verbose)
353 connection_status();
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000354 if (hccp)
355 hccp->pid = pid;
356 goto again;
357 }
358
359 /* Child: prepare env, log, and exec prog */
360
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000361 /* Closing tcp listening socket */
362 if (tcp)
363 close(sock);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000364
Denis Vlasenko64a15122007-04-04 10:16:15 +0000365 if (need_remote_ip)
366 remote_addr = xmalloc_sockaddr2dotted(&remote.sa, sa_len);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000367
368 if (need_hostnames) {
369 if (option_mask32 & OPT_h) {
Denis Vlasenko64a15122007-04-04 10:16:15 +0000370 remote_hostname = xmalloc_sockaddr2host_noport(&remote.sa, sa_len);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000371 if (!remote_hostname) {
Denis Vlasenko64a15122007-04-04 10:16:15 +0000372 bb_error_msg("warning: cannot look up hostname for %s", remote_addr);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000373 remote_hostname = (char*)"";
374 }
375 }
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000376 /* Find out local IP peer connected to.
377 * Errors ignored (I'm not paranoid enough to imagine kernel
378 * which doesn't know local IP). */
Denis Vlasenko64a15122007-04-04 10:16:15 +0000379 if (tcp) {
380 local.len = sa_len;
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000381 getsockname(0, &local.sa, &local.len);
Denis Vlasenko64a15122007-04-04 10:16:15 +0000382 }
383 local_addr = xmalloc_sockaddr2dotted(&local.sa, sa_len);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000384 if (!local_hostname) {
Denis Vlasenko64a15122007-04-04 10:16:15 +0000385 local_hostname = xmalloc_sockaddr2host_noport(&local.sa, sa_len);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000386 if (!local_hostname)
Denis Vlasenko64a15122007-04-04 10:16:15 +0000387 bb_error_msg_and_die("warning: cannot look up hostname for %s"+9, local_addr);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000388 }
389 }
390
391 if (verbose) {
392 pid = getpid();
Denis Vlasenko64a15122007-04-04 10:16:15 +0000393 printf("%s: info: pid %u from %s\n", applet_name, pid, remote_addr);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000394 if (max_per_host)
395 printf("%s: info: concurrency %u %s %u/%u\n",
396 applet_name, pid, remote_ip, cur_per_host, max_per_host);
Denis Vlasenko64a15122007-04-04 10:16:15 +0000397 printf("%s: info: start %u %s:%s :%s:%s\n",
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000398 applet_name, pid,
Denis Vlasenko64a15122007-04-04 10:16:15 +0000399 local_hostname, local_addr,
400 remote_hostname, remote_addr);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000401 }
402
403 if (!(option_mask32 & OPT_E)) {
404 /* setup ucspi env */
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000405 const char *proto = tcp ? "TCP" : "UDP";
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000406
407 /* Extract "original" destination addr:port
408 * from Linux firewall. Useful when you redirect
409 * an outbond connection to local handler, and it needs
410 * to know where it originally tried to connect */
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000411 if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &lsa->sa, &lsa->len) == 0) {
Denis Vlasenko64a15122007-04-04 10:16:15 +0000412 char *addr = xmalloc_sockaddr2dotted(&lsa->sa, sa_len);
413 xsetenv("TCPORIGDSTADDR", addr);
414 free(addr);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000415 }
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000416 xsetenv("PROTO", proto);
Denis Vlasenko64a15122007-04-04 10:16:15 +0000417 xsetenv_proto(proto, "LOCALADDR", local_addr);
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000418 xsetenv_proto(proto, "LOCALHOST", local_hostname);
Denis Vlasenko64a15122007-04-04 10:16:15 +0000419 xsetenv_proto(proto, "REMOTEADDR", remote_addr);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000420 if (option_mask32 & OPT_h) {
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000421 xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000422 }
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000423 xsetenv_proto(proto, "REMOTEINFO", "");
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000424 /* additional */
Denis Vlasenko02fd6682007-04-03 23:23:10 +0000425 if (cur_per_host > 0) /* can not be true for udp */
Denis Vlasenkob933ac12007-04-03 12:09:46 +0000426 xsetenv("TCPCONCURRENCY", utoa(cur_per_host));
427 }
428
429 dup2(0, 1);
430
431 signal(SIGTERM, SIG_DFL);
432 signal(SIGPIPE, SIG_DFL);
433 signal(SIGCHLD, SIG_DFL);
434 sig_unblock(SIGCHLD);
435
436 argv += 2;
437#ifdef SSLSVD
438 strcpy(id, utoa(pid);
439 ssl_io(0, argv);
440#else
441 BB_EXECVP(argv[0], argv);
442#endif
443 bb_perror_msg_and_die("exec '%s'", argv[0]);
444}
445
446/*
447tcpsvd [-hpEvv] [-c n] [-C n:msg] [-b n] [-u user] [-l name]
448 [-i dir|-x cdb] [ -t sec] host port prog
449
450tcpsvd creates a TCP/IP socket, binds it to the address host:port,
451and listens on the socket for incoming connections.
452
453On each incoming connection, tcpsvd conditionally runs a program,
454with standard input reading from the socket, and standard output
455writing to the socket, to handle this connection. tcpsvd keeps
456listening on the socket for new connections, and can handle
457multiple connections simultaneously.
458
459tcpsvd optionally checks for special instructions depending
460on the IP address or hostname of the client that initiated
461the connection, see ipsvd-instruct(5).
462
463host
464 host either is a hostname, or a dotted-decimal IP address,
465 or 0. If host is 0, tcpsvd accepts connections to any local
466 IP address.
467 * busybox accepts IPv6 addresses and host:port pairs too
468 In this case second parameter is ignored
469port
470 tcpsvd accepts connections to host:port. port may be a name
471 from /etc/services or a number.
472prog
473 prog consists of one or more arguments. For each connection,
474 tcpsvd normally runs prog, with file descriptor 0 reading from
475 the network, and file descriptor 1 writing to the network.
476 By default it also sets up TCP-related environment variables,
477 see tcp-environ(5)
478-i dir
479 read instructions for handling new connections from the instructions
480 directory dir. See ipsvd-instruct(5) for details.
481 * ignored by busyboxed version
482-x cdb
483 read instructions for handling new connections from the constant database
484 cdb. The constant database normally is created from an instructions
485 directory by running ipsvd-cdb(8).
486 * ignored by busyboxed version
487-t sec
488 timeout. This option only takes effect if the -i option is given.
489 While checking the instructions directory, check the time of last access
490 of the file that matches the clients address or hostname if any, discard
491 and remove the file if it wasn't accessed within the last sec seconds;
492 tcpsvd does not discard or remove a file if the user's write permission
493 is not set, for those files the timeout is disabled. Default is 0,
494 which means that the timeout is disabled.
495 * ignored by busyboxed version
496-l name
497 local hostname. Do not look up the local hostname in DNS, but use name
498 as hostname. This option must be set if tcpsvd listens on port 53
499 to avoid loops.
500-u user[:group]
501 drop permissions. Switch user ID to user's UID, and group ID to user's
502 primary GID after creating and binding to the socket. If user is followed
503 by a colon and a group name, the group ID is switched to the GID of group
504 instead. All supplementary groups are removed.
505-c n
506 concurrency. Handle up to n connections simultaneously. Default is 30.
507 If there are n connections active, tcpsvd defers acceptance of a new
508 connection until an active connection is closed.
509-C n[:msg]
510 per host concurrency. Allow only up to n connections from the same IP
511 address simultaneously. If there are n active connections from one IP
512 address, new incoming connections from this IP address are closed
513 immediately. If n is followed by :msg, the message msg is written
514 to the client if possible, before closing the connection. By default
515 msg is empty. See ipsvd-instruct(5) for supported escape sequences in msg.
516
517 For each accepted connection, the current per host concurrency is
518 available through the environment variable TCPCONCURRENCY. n and msg
519 can be overwritten by ipsvd(7) instructions, see ipsvd-instruct(5).
520 By default tcpsvd doesn't keep track of connections.
521-h
522 Look up the client's hostname in DNS.
523-p
524 paranoid. After looking up the client's hostname in DNS, look up the IP
525 addresses in DNS for that hostname, and forget about the hostname
526 if none of the addresses match the client's IP address. You should
527 set this option if you use hostname based instructions. The -p option
528 implies the -h option.
529 * ignored by busyboxed version
530-b n
531 backlog. Allow a backlog of approximately n TCP SYNs. On some systems n
532 is silently limited. Default is 20.
533-E
534 no special environment. Do not set up TCP-related environment variables.
535-v
536 verbose. Print verbose messsages to standard output.
537-vv
538 more verbose. Print more verbose messages to standard output.
539 * no difference between -v and -vv in busyboxed version
540*/