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