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