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 (14 kb)" 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 (13 kb)" 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 124struct globals { 125 unsigned verbose; 126 unsigned max_per_host; 127 unsigned cur_per_host; 128 unsigned cnum; 129 unsigned cmax; 130 struct hcc *cc; 131 char **env_cur; 132 char *env_var[1]; /* actually bigger */ 133} FIX_ALIASING; 134#define G (*(struct globals*)bb_common_bufsiz1) 135#define verbose (G.verbose ) 136#define max_per_host (G.max_per_host) 137#define cur_per_host (G.cur_per_host) 138#define cnum (G.cnum ) 139#define cmax (G.cmax ) 140#define env_cur (G.env_cur ) 141#define env_var (G.env_var ) 142#define INIT_G() do { \ 143 setup_common_bufsiz(); \ 144 cmax = 30; \ 145 env_cur = &env_var[0]; \ 146} while (0) 147 148 149/* We have to be careful about leaking memory in repeated setenv's */ 150static void xsetenv_plain(const char *n, const char *v) 151{ 152 char *var = xasprintf("%s=%s", n, v); 153 *env_cur++ = var; 154 putenv(var); 155} 156 157static void xsetenv_proto(const char *proto, const char *n, const char *v) 158{ 159 char *var = xasprintf("%s%s=%s", proto, n, v); 160 *env_cur++ = var; 161 putenv(var); 162} 163 164static void undo_xsetenv(void) 165{ 166 char **pp = env_cur = &env_var[0]; 167 while (*pp) { 168 char *var = *pp; 169 bb_unsetenv_and_free(var); 170 *pp++ = NULL; 171 } 172} 173 174static void sig_term_handler(int sig) 175{ 176 if (verbose) 177 bb_error_msg("got signal %u, exit", sig); 178 kill_myself_with_sig(sig); 179} 180 181/* Little bloated, but tries to give accurate info how child exited. 182 * Makes easier to spot segfaulting children etc... */ 183static void print_waitstat(unsigned pid, int wstat) 184{ 185 unsigned e = 0; 186 const char *cause = "?exit"; 187 188 if (WIFEXITED(wstat)) { 189 cause++; 190 e = WEXITSTATUS(wstat); 191 } else if (WIFSIGNALED(wstat)) { 192 cause = "signal"; 193 e = WTERMSIG(wstat); 194 } 195 bb_error_msg("end %d %s %d", pid, cause, e); 196} 197 198/* Must match getopt32 in main! */ 199enum { 200 OPT_c = (1 << 0), 201 OPT_C = (1 << 1), 202 OPT_i = (1 << 2), 203 OPT_x = (1 << 3), 204 OPT_u = (1 << 4), 205 OPT_l = (1 << 5), 206 OPT_E = (1 << 6), 207 OPT_b = (1 << 7), 208 OPT_h = (1 << 8), 209 OPT_p = (1 << 9), 210 OPT_t = (1 << 10), 211 OPT_v = (1 << 11), 212 OPT_V = (1 << 12), 213 OPT_U = (1 << 13), /* from here: sslsvd only */ 214 OPT_slash = (1 << 14), 215 OPT_Z = (1 << 15), 216 OPT_K = (1 << 16), 217}; 218 219static void if_verbose_print_connection_status(void) 220{ 221 if (verbose) { 222 /* "only 1 client max" desn't need this */ 223 if (cmax > 1) 224 bb_error_msg("status %u/%u", cnum, cmax); 225 } 226} 227 228/* SIGCHLD handler is reentrancy-safe because SIGCHLD is unmasked 229 * only over accept() or recvfrom() calls, not over memory allocations 230 * or printouts. Do need to save/restore errno in order not to mangle 231 * these syscalls' error code, if any. 232 */ 233static void sig_child_handler(int sig UNUSED_PARAM) 234{ 235 int wstat; 236 pid_t pid; 237 int sv_errno = errno; 238 239 while ((pid = wait_any_nohang(&wstat)) > 0) { 240 if (max_per_host) 241 ipsvd_perhost_remove(G.cc, pid); 242 if (cnum) 243 cnum--; 244 if (verbose) 245 print_waitstat(pid, wstat); 246 } 247 if_verbose_print_connection_status(); 248 errno = sv_errno; 249} 250 251int tcpudpsvd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 252int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv) 253{ 254 char *str_C, *str_t; 255 char *user; 256 struct hcc *hccp; 257 const char *instructs; 258 char *msg_per_host = NULL; 259 unsigned len_per_host = len_per_host; /* gcc */ 260#ifndef SSLSVD 261 struct bb_uidgid_t ugid; 262#endif 263 bool tcp; 264 uint16_t local_port; 265 char *preset_local_hostname = NULL; 266 char *remote_hostname = remote_hostname; /* for compiler */ 267 char *remote_addr = remote_addr; /* for compiler */ 268 len_and_sockaddr *lsa; 269 len_and_sockaddr local, remote; 270 socklen_t sa_len; 271 int pid; 272 int sock; 273 int conn; 274 unsigned backlog = 20; 275 unsigned opts; 276 277 INIT_G(); 278 279 tcp = (applet_name[0] == 't'); 280 281 /* "+": stop on first non-option */ 282#ifdef SSLSVD 283 opts = getopt32(argv, "^+" 284 "c:+C:i:x:u:l:Eb:+hpt:vU:/:Z:K:" /* -c NUM, -b NUM */ 285 "\0" 286 /* 3+ args, -i at most once, -p implies -h, -v is a counter */ 287 "-3:i--i:ph:vv", 288 &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, 289 &backlog, &str_t, &ssluser, &root, &cert, &key, &verbose 290 ); 291#else 292 opts = getopt32(argv, "^+" 293 "c:+C:i:x:u:l:Eb:+hpt:v" /* -c NUM, -b NUM */ 294 "\0" 295 /* 3+ args, -i at most once, -p implies -h, -v is a counter */ 296 "-3:i--i:ph:vv", 297 &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, 298 &backlog, &str_t, &verbose 299 ); 300#endif 301 if (opts & OPT_C) { /* -C n[:message] */ 302 max_per_host = bb_strtou(str_C, &str_C, 10); 303 if (str_C[0]) { 304 if (str_C[0] != ':') 305 bb_show_usage(); 306 msg_per_host = str_C + 1; 307 len_per_host = strlen(msg_per_host); 308 } 309 } 310 if (max_per_host > cmax) 311 max_per_host = cmax; 312 if (opts & OPT_u) { 313 xget_uidgid(&ugid, user); 314 } 315#ifdef SSLSVD 316 if (opts & OPT_U) ssluser = optarg; 317 if (opts & OPT_slash) root = optarg; 318 if (opts & OPT_Z) cert = optarg; 319 if (opts & OPT_K) key = optarg; 320#endif 321 argv += optind; 322 if (!argv[0][0] || LONE_CHAR(argv[0], '0')) 323 argv[0] = (char*)"0.0.0.0"; 324 325 /* Per-IP flood protection is not thought-out for UDP */ 326 if (!tcp) 327 max_per_host = 0; 328 329 bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */ 330 331#ifdef SSLSVD 332 sslser = user; 333 client = 0; 334 if ((getuid() == 0) && !(opts & OPT_u)) { 335 xfunc_error_retval = 100; 336 bb_simple_error_msg_and_die(bb_msg_you_must_be_root); 337 } 338 if (opts & OPT_u) 339 if (!uidgid_get(&sslugid, ssluser, 1)) { 340 if (errno) { 341 bb_perror_msg_and_die("can't get user/group: %s", ssluser); 342 } 343 bb_error_msg_and_die("unknown user/group %s", ssluser); 344 } 345 if (!cert) cert = "./cert.pem"; 346 if (!key) key = cert; 347 if (matrixSslOpen() < 0) 348 fatal("can't initialize ssl"); 349 if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) { 350 if (client) 351 fatal("can't read cert, key, or ca file"); 352 fatal("can't read cert or key file"); 353 } 354 if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0) 355 fatal("can't create ssl session"); 356#endif 357 358 sig_block(SIGCHLD); 359 signal(SIGCHLD, sig_child_handler); 360 bb_signals(BB_FATAL_SIGS, sig_term_handler); 361 signal(SIGPIPE, SIG_IGN); 362 363 if (max_per_host) 364 G.cc = ipsvd_perhost_init(cmax); 365 366 local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); 367 lsa = xhost2sockaddr(argv[0], local_port); 368 argv += 2; 369 370 sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0); 371 setsockopt_reuseaddr(sock); 372 sa_len = lsa->len; /* I presume sockaddr len stays the same */ 373 xbind(sock, &lsa->u.sa, sa_len); 374 if (tcp) { 375 xlisten(sock, backlog); 376 close_on_exec_on(sock); 377 } else { /* udp: needed for recv_from_to to work: */ 378 socket_want_pktinfo(sock); 379 } 380 /* ndelay_off(sock); - it is the default I think? */ 381 382#ifndef SSLSVD 383 if (opts & OPT_u) { 384 /* drop permissions */ 385 xsetgid(ugid.gid); 386 xsetuid(ugid.uid); 387 } 388#endif 389 390 if (verbose) { 391 char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa); 392 if (opts & OPT_u) 393 bb_error_msg("listening on %s, starting, uid %u, gid %u", addr, 394 (unsigned)ugid.uid, (unsigned)ugid.gid); 395 else 396 bb_error_msg("listening on %s, starting", addr); 397 free(addr); 398 } 399 400 /* Main accept() loop */ 401 402 again: 403 hccp = NULL; 404 405 again1: 406 close(0); 407 /* It's important to close(0) _before_ wait loop: 408 * fd#0 can be a shared connection fd. 409 * If kept open by us, peer can't detect PROG closing it. 410 */ 411 while (cnum >= cmax) 412 wait_for_any_sig(); /* expecting SIGCHLD */ 413 414 again2: 415 sig_unblock(SIGCHLD); 416 local.len = remote.len = sa_len; 417 if (tcp) { 418 /* Accept a connection to fd #0 */ 419 conn = accept(sock, &remote.u.sa, &remote.len); 420 } else { 421 /* In case recv_from_to won't be able to recover local addr. 422 * Also sets port - recv_from_to is unable to do it. */ 423 local = *lsa; 424 conn = recv_from_to(sock, NULL, 0, MSG_PEEK, 425 &remote.u.sa, &local.u.sa, sa_len); 426 } 427 sig_block(SIGCHLD); 428 if (conn < 0) { 429 if (errno != EINTR) 430 bb_simple_perror_msg(tcp ? "accept" : "recv"); 431 goto again2; 432 } 433 xmove_fd(tcp ? conn : sock, 0); 434 435 if (max_per_host) { 436 /* Drop connection immediately if cur_per_host > max_per_host 437 * (minimizing load under SYN flood) */ 438 remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa); 439 cur_per_host = ipsvd_perhost_add(G.cc, remote_addr, max_per_host, &hccp); 440 if (cur_per_host > max_per_host) { 441 /* ipsvd_perhost_add detected that max is exceeded 442 * (and did not store ip in connection table) */ 443 free(remote_addr); 444 if (msg_per_host) { 445 /* don't block or test for errors */ 446 send(0, msg_per_host, len_per_host, MSG_DONTWAIT); 447 } 448 goto again1; 449 } 450 /* NB: remote_addr is not leaked, it is stored in conn table */ 451 } 452 453 if (!tcp) { 454 /* Voodoo magic: making udp sockets each receive its own 455 * packets is not trivial, and I still not sure 456 * I do it 100% right. 457 * 1) we have to do it before fork() 458 * 2) order is important - is it right now? */ 459 460 /* Open new non-connected UDP socket for further clients... */ 461 sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0); 462 setsockopt_reuseaddr(sock); 463 /* Make plain write/send work for old socket by supplying default 464 * destination address. This also restricts incoming packets 465 * to ones coming from this remote IP. */ 466 xconnect(0, &remote.u.sa, sa_len); 467 /* hole? at this point we have no wildcard udp socket... 468 * can this cause clients to get "port unreachable" icmp? 469 * Yup, time window is very small, but it exists (does it?) */ 470 /* ..."open new socket", continued */ 471 xbind(sock, &lsa->u.sa, sa_len); 472 socket_want_pktinfo(sock); 473 474 /* Doesn't work: 475 * we cannot replace fd #0 - we will lose pending packet 476 * which is already buffered for us! And we cannot use fd #1 477 * instead - it will "intercept" all following packets, but child 478 * does not expect data coming *from fd #1*! */ 479#if 0 480 /* Make it so that local addr is fixed to localp->u.sa 481 * and we don't accidentally accept packets to other local IPs. */ 482 /* NB: we possibly bind to the _very_ same_ address & port as the one 483 * already bound in parent! This seems to work in Linux. 484 * (otherwise we can move socket to fd #0 only if bind succeeds) */ 485 close(0); 486 set_nport(&localp->u.sa, htons(local_port)); 487 xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0); 488 setsockopt_reuseaddr(0); /* crucial */ 489 xbind(0, &localp->u.sa, localp->len); 490#endif 491 } 492 493 pid = vfork(); 494 if (pid == -1) { 495 bb_simple_perror_msg("vfork"); 496 goto again; 497 } 498 499 if (pid != 0) { 500 /* Parent */ 501 cnum++; 502 if_verbose_print_connection_status(); 503 if (hccp) 504 hccp->pid = pid; 505 /* clean up changes done by vforked child */ 506 undo_xsetenv(); 507 goto again; 508 } 509 510 /* Child: prepare env, log, and exec prog */ 511 512 { /* vfork alert! every xmalloc in this block should be freed! */ 513 char *local_hostname = local_hostname; /* for compiler */ 514 char *local_addr = NULL; 515 char *free_me0 = NULL; 516 char *free_me1 = NULL; 517 char *free_me2 = NULL; 518 519 if (verbose || !(opts & OPT_E)) { 520 if (!max_per_host) /* remote_addr is not yet known */ 521 free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa); 522 if (opts & OPT_h) { 523 free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa); 524 if (!remote_hostname) { 525 bb_error_msg("can't look up hostname for %s", remote_addr); 526 remote_hostname = remote_addr; 527 } 528 } 529 /* Find out local IP peer connected to. 530 * Errors ignored (I'm not paranoid enough to imagine kernel 531 * which doesn't know local IP). */ 532 if (tcp) 533 getsockname(0, &local.u.sa, &local.len); 534 /* else: for UDP it is done earlier by parent */ 535 local_addr = xmalloc_sockaddr2dotted(&local.u.sa); 536 if (opts & OPT_h) { 537 local_hostname = preset_local_hostname; 538 if (!local_hostname) { 539 free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa); 540 if (!local_hostname) 541 bb_error_msg_and_die("can't look up hostname for %s", local_addr); 542 } 543 /* else: local_hostname is not NULL, but is NOT malloced! */ 544 } 545 } 546 if (verbose) { 547 pid = getpid(); 548 if (max_per_host) { 549 bb_error_msg("concurrency %s %u/%u", 550 remote_addr, 551 cur_per_host, max_per_host); 552 } 553 bb_error_msg((opts & OPT_h) 554 ? "start %u %s-%s (%s-%s)" 555 : "start %u %s-%s", 556 pid, 557 local_addr, remote_addr, 558 local_hostname, remote_hostname); 559 } 560 561 if (!(opts & OPT_E)) { 562 /* setup ucspi env */ 563 const char *proto = tcp ? "TCP" : "UDP"; 564 565#ifdef SO_ORIGINAL_DST 566 /* Extract "original" destination addr:port 567 * from Linux firewall. Useful when you redirect 568 * an outbond connection to local handler, and it needs 569 * to know where it originally tried to connect */ 570 if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) { 571 char *addr = xmalloc_sockaddr2dotted(&local.u.sa); 572 xsetenv_plain("TCPORIGDSTADDR", addr); 573 free(addr); 574 } 575#endif 576 xsetenv_plain("PROTO", proto); 577 xsetenv_proto(proto, "LOCALADDR", local_addr); 578 xsetenv_proto(proto, "REMOTEADDR", remote_addr); 579 if (opts & OPT_h) { 580 xsetenv_proto(proto, "LOCALHOST", local_hostname); 581 xsetenv_proto(proto, "REMOTEHOST", remote_hostname); 582 } 583 //compat? xsetenv_proto(proto, "REMOTEINFO", ""); 584 /* additional */ 585 if (cur_per_host > 0) /* can not be true for udp */ 586 xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host)); 587 } 588 free(local_addr); 589 free(free_me0); 590 free(free_me1); 591 free(free_me2); 592 } 593 594 xdup2(0, 1); 595 596 /* Restore signal handling for the to-be-execed process */ 597 signal(SIGPIPE, SIG_DFL); /* this one was SIG_IGNed */ 598 /* Non-ignored signals revert to SIG_DFL on exec anyway 599 * But we can get signals BEFORE execvp(), this is unlikely 600 * but it would invoke sig_child_handler(), which would 601 * check waitpid(WNOHANG), then print "status N/M" if verbose. 602 * I guess we can live with that possibility. 603 */ 604 /*signal(SIGCHLD, SIG_DFL);*/ 605 sig_unblock(SIGCHLD); 606 607#ifdef SSLSVD 608 strcpy(id, utoa(pid)); 609 ssl_io(0, argv); 610 bb_perror_msg_and_die("can't execute '%s'", argv[0]); 611#else 612 BB_EXECVP_or_die(argv); 613#endif 614} 615 616/* 617tcpsvd [-hpEvv] [-c n] [-C n:msg] [-b n] [-u user] [-l name] 618 [-i dir|-x cdb] [ -t sec] host port prog 619 620tcpsvd creates a TCP/IP socket, binds it to the address host:port, 621and listens on the socket for incoming connections. 622 623On each incoming connection, tcpsvd conditionally runs a program, 624with standard input reading from the socket, and standard output 625writing to the socket, to handle this connection. tcpsvd keeps 626listening on the socket for new connections, and can handle 627multiple connections simultaneously. 628 629tcpsvd optionally checks for special instructions depending 630on the IP address or hostname of the client that initiated 631the connection, see ipsvd-instruct(5). 632 633host 634 host either is a hostname, or a dotted-decimal IP address, 635 or 0. If host is 0, tcpsvd accepts connections to any local 636 IP address. 637 * busybox accepts IPv6 addresses and host:port pairs too 638 In this case second parameter is ignored 639port 640 tcpsvd accepts connections to host:port. port may be a name 641 from /etc/services or a number. 642prog 643 prog consists of one or more arguments. For each connection, 644 tcpsvd normally runs prog, with file descriptor 0 reading from 645 the network, and file descriptor 1 writing to the network. 646 By default it also sets up TCP-related environment variables, 647 see tcp-environ(5) 648-i dir 649 read instructions for handling new connections from the instructions 650 directory dir. See ipsvd-instruct(5) for details. 651 * ignored by busyboxed version 652-x cdb 653 read instructions for handling new connections from the constant database 654 cdb. The constant database normally is created from an instructions 655 directory by running ipsvd-cdb(8). 656 * ignored by busyboxed version 657-t sec 658 timeout. This option only takes effect if the -i option is given. 659 While checking the instructions directory, check the time of last access 660 of the file that matches the clients address or hostname if any, discard 661 and remove the file if it wasn't accessed within the last sec seconds; 662 tcpsvd does not discard or remove a file if the user's write permission 663 is not set, for those files the timeout is disabled. Default is 0, 664 which means that the timeout is disabled. 665 * ignored by busyboxed version 666-l name 667 local hostname. Do not look up the local hostname in DNS, but use name 668 as hostname. This option must be set if tcpsvd listens on port 53 669 to avoid loops. 670-u user[:group] 671 drop permissions. Switch user ID to user's UID, and group ID to user's 672 primary GID after creating and binding to the socket. If user is followed 673 by a colon and a group name, the group ID is switched to the GID of group 674 instead. All supplementary groups are removed. 675-c n 676 concurrency. Handle up to n connections simultaneously. Default is 30. 677 If there are n connections active, tcpsvd defers acceptance of a new 678 connection until an active connection is closed. 679-C n[:msg] 680 per host concurrency. Allow only up to n connections from the same IP 681 address simultaneously. If there are n active connections from one IP 682 address, new incoming connections from this IP address are closed 683 immediately. If n is followed by :msg, the message msg is written 684 to the client if possible, before closing the connection. By default 685 msg is empty. See ipsvd-instruct(5) for supported escape sequences in msg. 686 687 For each accepted connection, the current per host concurrency is 688 available through the environment variable TCPCONCURRENCY. n and msg 689 can be overwritten by ipsvd(7) instructions, see ipsvd-instruct(5). 690 By default tcpsvd doesn't keep track of connections. 691-h 692 Look up the client's hostname in DNS. 693-p 694 paranoid. After looking up the client's hostname in DNS, look up the IP 695 addresses in DNS for that hostname, and forget about the hostname 696 if none of the addresses match the client's IP address. You should 697 set this option if you use hostname based instructions. The -p option 698 implies the -h option. 699 * ignored by busyboxed version 700-b n 701 backlog. Allow a backlog of approximately n TCP SYNs. On some systems n 702 is silently limited. Default is 20. 703-E 704 no special environment. Do not set up TCP-related environment variables. 705-v 706 verbose. Print verbose messages to standard output. 707-vv 708 more verbose. Print more verbose messages to standard output. 709 * no difference between -v and -vv in busyboxed version 710*/ 711