nc: remove a bit of bloat
authorDenis Vlasenko <vda.linux@googlemail.com>
Wed, 12 Mar 2008 23:13:50 +0000 (23:13 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Wed, 12 Mar 2008 23:13:50 +0000 (23:13 -0000)
inetd: more NOMMU fixes
rx: shrink
devfsd: minor shrink
vlock: shrink
tcpudp: narrow down window where we have no wildcard socket

parse_one_line                                      1015    1102     +87
init_ring                                              -      53     +53
xzalloc_lsa                                            -      48     +48
read_byte                                             51      50      -1
rearm_alarm                                           28      25      -3
nc_main                                             1028    1000     -28
initring                                              53       -     -53
vlock_main                                           583     496     -87
reread_config_file                                  1089     991     -98
rx_main                                             1046     912    -134
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 1/6 up/down: 188/-404)         Total: -216 bytes
   text    data     bss     dec     hex filename
 800120     661    7428  808209   c5511 busybox_old
 799796     661    7428  807885   c53cd busybox_unstripped

ipsvd/tcpudp.c
libbb/signals.c
loginutils/vlock.c
miscutils/devfsd.c
miscutils/rx.c
networking/inetd.c
networking/nc_bloaty.c

index cb57e598ad390c481b9aef4c61bc4d0453e30176..8b4ae88f499bfa6ecd7b335cc5a776e71a920392 100644 (file)
@@ -339,16 +339,17 @@ int tcpudpsvd_main(int argc, char **argv)
                 * 1) we have to do it before fork()
                 * 2) order is important - is it right now? */
 
-               /* Make plain write/send work for this socket by supplying default
+               /* Open new non-connected UDP socket for further clients... */
+               sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
+               setsockopt_reuseaddr(sock);
+               /* Make plain write/send work for old socket by supplying default
                 * destination address. This also restricts incoming packets
                 * to ones coming from this remote IP. */
                xconnect(0, &remote.u.sa, sa_len);
        /* hole? at this point we have no wildcard udp socket...
         * can this cause clients to get "port unreachable" icmp?
         * Yup, time window is very small, but it exists (is it?) */
-               /* Open new non-connected UDP socket for further clients */
-               sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
-               setsockopt_reuseaddr(sock);
+               /* ..."open new socket", continued */
                xbind(sock, &lsa->u.sa, sa_len);
                socket_want_pktinfo(sock);
 
@@ -377,7 +378,6 @@ int tcpudpsvd_main(int argc, char **argv)
                goto again;
        }
 
-
        if (pid != 0) {
                /* parent */
                cnum++;
@@ -467,7 +467,7 @@ int tcpudpsvd_main(int argc, char **argv)
 
        argv += 2;
 #ifdef SSLSVD
-       strcpy(id, utoa(pid);
+       strcpy(id, utoa(pid));
        ssl_io(0, argv);
 #else
        BB_EXECVP(argv[0], argv);
index 1929cb88e7b7cfe0ee595334a276184df320929d..a327e87c8eaaff99c396a8f00b7b23e71970630d 100644 (file)
@@ -82,13 +82,8 @@ void sig_pause(void)
 /* Assuming the sig is fatal */
 void kill_myself_with_sig(int sig)
 {
-       sigset_t set;
-
        signal(sig, SIG_DFL);
-
-       sigemptyset(&set);
-       sigaddset(&set, sig);
-       sigprocmask(SIG_UNBLOCK, &set, NULL);
+       sig_unblock(sig);
        raise(sig);
        _exit(1); /* Should not reach it */
 }
index 6e928e2391d2098d51f9fbb7e97a65e395534615..846733ea8127cb3a7056f2655cc2add5a2b6f7f5 100644 (file)
@@ -1,5 +1,4 @@
 /* vi: set sw=4 ts=4: */
-
 /*
  * vlock implementation for busybox
  *
 /* Fixed by Erik Andersen to do passwords the tinylogin way...
  * It now works with md5, sha1, etc passwords. */
 
-#include "libbb.h"
 #include <sys/vt.h>
-
-enum { vfd = 3 };
-
-/* static unsigned long o_lock_all; */
+#include "libbb.h"
 
 static void release_vt(int signo)
 {
-       ioctl(vfd, VT_RELDISP, (unsigned long) !option_mask32 /*!o_lock_all*/);
+       /* If -a, param is 0, which means:
+        * "no, kernel, we don't allow console switch away from us!" */
+       ioctl(STDIN_FILENO, VT_RELDISP, (unsigned long) !option_mask32);
 }
 
 static void acquire_vt(int signo)
 {
-       ioctl(vfd, VT_RELDISP, VT_ACKACQ);
+       /* ACK to kernel that switch to console is successful */
+       ioctl(STDIN_FILENO, VT_RELDISP, VT_ACKACQ);
 }
 
 int vlock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int vlock_main(int argc, char **argv)
 {
-       sigset_t sig;
        struct sigaction sa;
        struct vt_mode vtm;
        struct termios term;
@@ -48,49 +45,48 @@ int vlock_main(int argc, char **argv)
        uid = getuid();
        pw = getpwuid(uid);
        if (pw == NULL)
-               bb_error_msg_and_die("unknown uid %d", uid);
-
-       if (argc > 2) {
-               bb_show_usage();
-       }
-
-       /*o_lock_all = */getopt32(argv, "a");
-
-       /* Avoid using statics - use constant fd */
-       xmove_fd(xopen(CURRENT_TTY, O_RDWR), vfd);
-       xioctl(vfd, VT_GETMODE, &vtm);
-
-       /* mask a bunch of signals */
-       sigprocmask(SIG_SETMASK, NULL, &sig);
-       sigdelset(&sig, SIGUSR1);
-       sigdelset(&sig, SIGUSR2);
-       sigaddset(&sig, SIGTSTP);
-       sigaddset(&sig, SIGTTIN);
-       sigaddset(&sig, SIGTTOU);
-       sigaddset(&sig, SIGHUP);
-       sigaddset(&sig, SIGCHLD);
-       sigaddset(&sig, SIGQUIT);
-       sigaddset(&sig, SIGINT);
-
-       sigemptyset(&(sa.sa_mask));
+               bb_error_msg_and_die("unknown uid %d", (int)uid);
+
+       opt_complementary = "=0"; /* no params! */
+       getopt32(argv, "a");
+
+       /* Ignore some signals so that we don't get killed by them */
+       bb_signals(0
+               + (1 << SIGTSTP)
+               + (1 << SIGTTIN)
+               + (1 << SIGTTOU)
+               + (1 << SIGHUP )
+               + (1 << SIGCHLD) /* paranoia :) */
+               + (1 << SIGQUIT)
+               + (1 << SIGINT )
+               , SIG_IGN);
+
+       /* We will use SIGUSRx for console switch control: */
+       /* 1: set handlers */
+       sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART;
        sa.sa_handler = release_vt;
        sigaction(SIGUSR1, &sa, NULL);
        sa.sa_handler = acquire_vt;
        sigaction(SIGUSR2, &sa, NULL);
-
-       /* need to handle some signals so that we don't get killed by them */
-       sa.sa_handler = SIG_IGN;
-       sigaction(SIGHUP, &sa, NULL);
-       sigaction(SIGQUIT, &sa, NULL);
-       sigaction(SIGINT, &sa, NULL);
-       sigaction(SIGTSTP, &sa, NULL);
-
+       /* 2: unmask them */
+       sigprocmask(SIG_SETMASK, NULL, &sa.sa_mask);
+       sigdelset(&sa.sa_mask, SIGUSR1);
+       sigdelset(&sa.sa_mask, SIGUSR2);
+       sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL);
+
+       /* Revert stdin/out to our controlling tty
+        * (or die if we have none) */
+       xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO);
+       xdup2(STDIN_FILENO, STDOUT_FILENO);
+
+       xioctl(STDIN_FILENO, VT_GETMODE, &vtm);
        ovtm = vtm;
+       /* "console switches are controlled by us, not kernel!" */
        vtm.mode = VT_PROCESS;
        vtm.relsig = SIGUSR1;
        vtm.acqsig = SIGUSR2;
-       ioctl(vfd, VT_SETMODE, &vtm);
+       ioctl(STDIN_FILENO, VT_SETMODE, &vtm);
 
        tcgetattr(STDIN_FILENO, &oterm);
        term = oterm;
@@ -111,7 +107,7 @@ int vlock_main(int argc, char **argv)
                puts("Password incorrect");
        } while (1);
 
-       ioctl(vfd, VT_SETMODE, &ovtm);
+       ioctl(STDIN_FILENO, VT_SETMODE, &ovtm);
        tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
        fflush_stdout_and_exit(0);
 }
index 286f00fd877df2140b94bf2a2d5e2feedc53192a..50c8203cb693843dad77b5216eae98aaea97f71f 100644 (file)
@@ -386,15 +386,14 @@ int devfsd_main(int argc, char **argv)
        /*  Tell kernel we are special(i.e. we get to see hidden entries)  */
        xioctl(fd, DEVFSDIOC_SET_EVENT_MASK, 0);
 
+       /*  Set up SIGHUP and SIGUSR1 handlers  */
        sigemptyset(&new_action.sa_mask);
        new_action.sa_flags = 0;
-
-       /*  Set up SIGHUP and SIGUSR1 handlers  */
        new_action.sa_handler = signal_handler;
-       if (sigaction(SIGHUP, &new_action, NULL) != 0 || sigaction(SIGUSR1, &new_action, NULL) != 0)
-               bb_error_msg_and_die("sigaction");
+       sigaction(SIGHUP, &new_action, NULL);
+       sigaction(SIGUSR1, &new_action, NULL);
 
-       printf("%s v%s  started for %s\n",applet_name, DEVFSD_VERSION, mount_point);
+       printf("%s v%s started for %s\n", applet_name, DEVFSD_VERSION, mount_point);
 
        /*  Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions  */
        umask(0);
index 8ccea497438e8e67d73334b9370d901423b99943..9a8fcaa2075d60529999fd2bf1be9881e6c59c0c 100644 (file)
@@ -16,7 +16,6 @@
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  *
  * This was originally written for blob and then adapted for busybox.
- *
  */
 
 #include "libbb.h"
 #define BS  0x08
 
 /*
-
 Cf:
-
   http://www.textfiles.com/apple/xmodem
   http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt
   http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt
   http://www.phys.washington.edu/~belonis/xmodem/modmprot.col
-
 */
 
 #define TIMEOUT 1
 #define TIMEOUT_LONG 10
 #define MAXERRORS 10
 
-static int read_byte(int fd, unsigned timeout)
+#define read_fd  STDIN_FILENO
+#define write_fd STDOUT_FILENO
+
+static int read_byte(unsigned timeout)
 {
        char buf[1];
        int n;
 
        alarm(timeout);
-
-       n = read(fd, &buf, 1);
-
+       /* NOT safe_read! We want ALRM to interrupt us */
+       n = read(read_fd, buf, 1);
        alarm(0);
-
        if (n == 1)
-               return buf[0] & 0xff;
-       else
-               return -1;
+               return (unsigned char)buf[0];
+       return -1;
 }
 
-static int receive(char *error_buf, size_t error_buf_size,
-                                  int ttyfd, int filefd)
+static int receive(/*int read_fd, */int file_fd)
 {
-       char blockBuf[1024];
-       unsigned int errors = 0;
-       unsigned int wantBlockNo = 1;
-       unsigned int length = 0;
-       int docrc = 1;
+       unsigned char blockBuf[1024];
+       unsigned errors = 0;
+       unsigned wantBlockNo = 1;
+       unsigned length = 0;
+       int do_crc = 1;
        char nak = 'C';
-       unsigned int timeout = TIMEOUT_LONG;
-
-#define note_error(fmt,args...) \
-       snprintf(error_buf, error_buf_size, fmt,##args)
+       unsigned timeout = TIMEOUT_LONG;
 
        /* Flush pending input */
-       tcflush(ttyfd, TCIFLUSH);
+       tcflush(read_fd, TCIFLUSH);
 
        /* Ask for CRC; if we get errors, we will go with checksum */
-       write(ttyfd, &nak, 1);
+       full_write(write_fd, &nak, 1);
 
        for (;;) {
                int blockBegin;
                int blockNo, blockNoOnesCompl;
                int blockLength;
-               int cksum = 0;
-               int crcHi = 0;
-               int crcLo = 0;
+               int cksum_crc;  /* cksum OR crc */
+               int expected;
+               int i,j;
 
-               blockBegin = read_byte(ttyfd, timeout);
+               blockBegin = read_byte(timeout);
                if (blockBegin < 0)
                        goto timeout;
 
@@ -102,52 +94,47 @@ static int receive(char *error_buf, size_t error_buf_size,
 
                case EOT:
                        nak = ACK;
-                       write(ttyfd, &nak, 1);
-                       goto done;
+                       full_write(write_fd, &nak, 1);
+                       return length;
 
                default:
                        goto error;
                }
 
                /* block no */
-               blockNo = read_byte(ttyfd, TIMEOUT);
+               blockNo = read_byte(TIMEOUT);
                if (blockNo < 0)
                        goto timeout;
 
                /* block no one's compliment */
-               blockNoOnesCompl = read_byte(ttyfd, TIMEOUT);
+               blockNoOnesCompl = read_byte(TIMEOUT);
                if (blockNoOnesCompl < 0)
                        goto timeout;
 
                if (blockNo != (255 - blockNoOnesCompl)) {
-                       note_error("bad block ones compl");
+                       bb_error_msg("bad block ones compl");
                        goto error;
                }
 
                blockLength = (blockBegin == SOH) ? 128 : 1024;
 
-               {
-                       int i;
-
-                       for (i = 0; i < blockLength; i++) {
-                               int cc = read_byte(ttyfd, TIMEOUT);
-                               if (cc < 0)
-                                       goto timeout;
-                               blockBuf[i] = cc;
-                       }
+               for (i = 0; i < blockLength; i++) {
+                       int cc = read_byte(TIMEOUT);
+                       if (cc < 0)
+                               goto timeout;
+                       blockBuf[i] = cc;
                }
 
-               if (docrc) {
-                       crcHi = read_byte(ttyfd, TIMEOUT);
-                       if (crcHi < 0)
+               if (do_crc) {
+                       cksum_crc = read_byte(TIMEOUT);
+                       if (cksum_crc < 0)
                                goto timeout;
-
-                       crcLo = read_byte(ttyfd, TIMEOUT);
-                       if (crcLo < 0)
+                       cksum_crc = (cksum_crc << 8) | read_byte(TIMEOUT);
+                       if (cksum_crc < 0)
                                goto timeout;
                } else {
-                       cksum = read_byte(ttyfd, TIMEOUT);
-                       if (cksum < 0)
+                       cksum_crc = read_byte(TIMEOUT);
+                       if (cksum_crc < 0)
                                goto timeout;
                }
 
@@ -156,93 +143,74 @@ static int receive(char *error_buf, size_t error_buf_size,
                        /* this also ignores the initial block 0 which is */
                        /* meta data. */
                        goto next;
-               } else if (blockNo != (wantBlockNo & 0xff)) {
-                       note_error("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
+               }
+               if (blockNo != (wantBlockNo & 0xff)) {
+                       bb_error_msg("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
                        goto error;
                }
 
-               if (docrc) {
-                       int crc = 0;
-                       int i, j;
-                       int expectedCrcHi;
-                       int expectedCrcLo;
-
+               expected = 0;
+               if (do_crc) {
                        for (i = 0; i < blockLength; i++) {
-                               crc = crc ^ (int) blockBuf[i] << 8;
-                               for (j = 0; j < 8; j++)
-                                       if (crc & 0x8000)
-                                               crc = crc << 1 ^ 0x1021;
+                               expected = expected ^ blockBuf[i] << 8;
+                               for (j = 0; j < 8; j++) {
+                                       if (expected & 0x8000)
+                                               expected = expected << 1 ^ 0x1021;
                                        else
-                                               crc = crc << 1;
-                       }
-
-                       expectedCrcHi = (crc >> 8) & 0xff;
-                       expectedCrcLo = crc & 0xff;
-
-                       if ((crcHi != expectedCrcHi) ||
-                           (crcLo != expectedCrcLo)) {
-                               note_error("crc error, expected 0x%02x 0x%02x, got 0x%02x 0x%02x", expectedCrcHi, expectedCrcLo, crcHi, crcLo);
-                               goto error;
+                                               expected = expected << 1;
+                               }
                        }
+                       expected &= 0xffff;
                } else {
-                       unsigned char expectedCksum = 0;
-                       int i;
-
                        for (i = 0; i < blockLength; i++)
-                               expectedCksum += blockBuf[i];
-
-                       if (cksum != expectedCksum) {
-                               note_error("checksum error, expected 0x%02x, got 0x%02x", expectedCksum, cksum);
-                               goto error;
-                       }
+                               expected += blockBuf[i];
+                       expected &= 0xff;
+               }
+               if (cksum_crc != expected) {
+                       bb_error_msg(do_crc ? "crc error, expected 0x%04x, got 0x%04x"
+                                          : "checksum error, expected 0x%02x, got 0x%02x",
+                                           expected, cksum_crc);
+                       goto error;
                }
 
                wantBlockNo++;
                length += blockLength;
 
-               if (full_write(filefd, blockBuf, blockLength) < 0) {
-                       note_error("write to file failed: %m");
+               errno = 0;
+               if (full_write(file_fd, blockBuf, blockLength) != blockLength) {
+                       bb_perror_msg("can't write to file");
                        goto fatal;
                }
-
-       next:
+ next:
                errors = 0;
                nak = ACK;
-               write(ttyfd, &nak, 1);
+               full_write(write_fd, &nak, 1);
                continue;
-
-       error:
-       timeout:
+ error:
+ timeout:
                errors++;
                if (errors == MAXERRORS) {
                        /* Abort */
 
-                       // if using crc, try again w/o crc
+                       /* if were asking for crc, try again w/o crc */
                        if (nak == 'C') {
                                nak = NAK;
                                errors = 0;
-                               docrc = 0;
+                               do_crc = 0;
                                goto timeout;
                        }
-
-                       note_error("too many errors; giving up");
-
-               fatal:
-                       /* 5 CAN followed by 5 BS */
-                       write(ttyfd, "\030\030\030\030\030\010\010\010\010\010", 10);
+                       bb_error_msg("too many errors; giving up");
+ fatal:
+                       /* 5 CAN followed by 5 BS. Don't try too hard... */
+                       safe_write(write_fd, "\030\030\030\030\030\010\010\010\010\010", 10);
                        return -1;
                }
 
                /* Flush pending input */
-               tcflush(ttyfd, TCIFLUSH);
-
-               write(ttyfd, &nak, 1);
-       }
-
- done:
-       return length;
+               tcflush(read_fd, TCIFLUSH);
 
-#undef note_error
+               full_write(write_fd, &nak, 1);
+       } /* for (;;) */
 }
 
 static void sigalrm_handler(int ATTRIBUTE_UNUSED signum)
@@ -252,40 +220,38 @@ static void sigalrm_handler(int ATTRIBUTE_UNUSED signum)
 int rx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int rx_main(int argc, char **argv)
 {
-       char *fn;
-       int ttyfd, filefd;
-       struct termios tty, orig_tty;
        struct sigaction act;
+       struct termios tty, orig_tty;
+       int termios_err;
+       int file_fd;
        int n;
-       char error_buf[256];
 
        if (argc != 2)
-                       bb_show_usage();
-
-       fn = argv[1];
-       ttyfd = xopen(CURRENT_TTY, O_RDWR);
-       filefd = xopen(fn, O_RDWR|O_CREAT|O_TRUNC);
-
-       if (tcgetattr(ttyfd, &tty) < 0)
-                       bb_perror_msg_and_die("tcgetattr");
-
-       orig_tty = tty;
-
-       cfmakeraw(&tty);
-       tcsetattr(ttyfd, TCSAFLUSH, &tty);
+               bb_show_usage();
+
+       /* Disabled by vda:
+        * why we can't receive from stdin? Why we *require*
+        * controlling tty?? */
+       /*read_fd = xopen(CURRENT_TTY, O_RDWR);*/
+       file_fd = xopen(argv[1], O_RDWR|O_CREAT|O_TRUNC);
+
+       termios_err = tcgetattr(read_fd, &tty);
+       if (termios_err == 0) {
+               orig_tty = tty;
+               cfmakeraw(&tty);
+               tcsetattr(read_fd, TCSAFLUSH, &tty);
+       }
 
+       /* No SA_RESTART: we want ALRM to interrupt read() */
        memset(&act, 0, sizeof(act));
        act.sa_handler = sigalrm_handler;
-       sigaction(SIGALRM, &act, 0);
-
-       n = receive(error_buf, sizeof(error_buf), ttyfd, filefd);
-
-       close(filefd);
-
-       tcsetattr(ttyfd, TCSAFLUSH, &orig_tty);
+       sigaction(SIGALRM, &act, NULL);
 
-       if (n < 0)
-               bb_error_msg_and_die("\nreceive failed:\n  %s", error_buf);
+       n = receive(file_fd);
 
-       fflush_stdout_and_exit(EXIT_SUCCESS);
+       if (termios_err == 0)
+               tcsetattr(read_fd, TCSAFLUSH, &orig_tty);
+       if (ENABLE_FEATURE_CLEAN_UP)
+               close(file_fd);
+       fflush_stdout_and_exit(n >= 0);
 }
index 8a4c9fbf6e0493ef5c07abc00f6f6541a8c47273..463c7cfcf80566b912dd2ee8493e55061fafb850 100644 (file)
@@ -3,6 +3,7 @@
 /*      $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $      */
 /*      $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $       */
 /* Busybox port by Vladimir Oleynik (C) 2001-2005 <dzo@simtreas.ru>     */
+/* IPv6 support, many bug fixes by Denys Vlasenko (c) 2008 */
 /*
  * Copyright (c) 1983,1991 The Regents of the University of California.
  * All rights reserved.
 
 /* Inetd - Internet super-server
  *
- * This program invokes all internet services as needed.
- * connection-oriented services are invoked each time a
+ * This program invokes configured services when a connection
+ * from a peer is established or a datagram arrives.
+ * Connection-oriented services are invoked each time a
  * connection is made, by creating a process.  This process
  * is passed the connection as file descriptor 0 and is
- * expected to do a getpeername to find out the source host
+ * expected to do a getpeername to find out peer's host
  * and port.
- *
  * Datagram oriented services are invoked when a datagram
  * arrives; a process is created and passed a pending message
- * on file descriptor 0.  Datagram servers may either connect
- * to their peer, freeing up the original socket for inetd
- * to receive further messages on, or "take over the socket",
- * processing all arriving datagrams and, eventually, timing
- * out.  The first type of server is said to be "multi-threaded";
- * the second type of server "single-threaded".
+ * on file descriptor 0. peer's address can be obtained
+ * using recvfrom.
  *
  * Inetd uses a configuration file which is read at startup
  * and, possibly, at some later time in response to a hangup signal.
  * order shown below.  Continuation lines for an entry must begin with
  * a space or tab.  All fields must be present in each entry.
  *
- *      service name                    must be in /etc/services
- *      socket type                     stream/dgram/raw/rdm/seqpacket
+ *      service_name                    must be in /etc/services
+ *      socket_type                     stream/dgram/raw/rdm/seqpacket
  *      protocol                        must be in /etc/protocols
  *                                      (usually "tcp" or "udp")
  *      wait/nowait[.max]               single-threaded/multi-threaded, max #
  *      user[.group] or user[:group]    user/group to run daemon as
- *      server program                  full path name
- *      server program arguments        maximum of MAXARGS (20)
+ *      server_program                  full path name
+ *      server_program_arguments        maximum of MAXARGS (20)
  *
  * For RPC services
- *      service name/version            must be in /etc/rpc
- *      socket type                     stream/dgram/raw/rdm/seqpacket
+ *      service_name/version            must be in /etc/rpc
+ *      socket_type                     stream/dgram/raw/rdm/seqpacket
  *      rpc/protocol                    "rpc/tcp" etc
  *      wait/nowait[.max]               single-threaded/multi-threaded
  *      user[.group] or user[:group]    user to run daemon as
- *      server program                  full path name
- *      server program arguments        maximum of MAXARGS (20)
+ *      server_program                  full path name
+ *      server_program_arguments        maximum of MAXARGS (20)
  *
  * For non-RPC services, the "service name" can be of the form
  * hostaddress:servicename, in which case the hostaddress is used
  * as the host portion of the address to listen on.  If hostaddress
- * consists of a single `*' character, INADDR_ANY is used.
+ * consists of a single '*' character, INADDR_ANY is used.
  *
  * A line can also consist of just
  *      hostaddress:
  * one line for any given RPC service, even if the host-address
  * specifiers are different.
  *
- * Comment lines are indicated by a `#' in column 1.
+ * Comment lines are indicated by a '#' in column 1.
  */
 
 /* inetd rules for passing file descriptors to children
  * tening service socket, and must accept at least one connection request
  * before exiting.  Such a server would normally accept and process incoming
  * connection requests until a timeout.
+ *
+ * In short: "stream" can be "wait" or "nowait"; "dgram" must be "wait".
  */
 
 /* Here's the scoop concerning the user[:group] feature:
 
 #include <syslog.h>
 #include <sys/un.h>
+
 #include "libbb.h"
 
+#if ENABLE_FEATURE_INETD_RPC
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#endif
+
 #if !BB_MMU
-/* stream versions of these builtins are forking,
+/* stream version of chargen is forking but not execing,
  * can't do that (easily) on NOMMU */
-#undef  ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
-#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD 0
 #undef  ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
 #define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 0
 #endif
 
-#if ENABLE_FEATURE_INETD_RPC
-#include <rpc/rpc.h>
-#include <rpc/pmap_clnt.h>
-#endif
-
 #define _PATH_INETDPID  "/var/run/inetd.pid"
 
-#define CNT_INTERVAL    60              /* servers in CNT_INTERVAL sec. */
-#define RETRYTIME       (60*10)         /* retry after bind or server fail */
+#define CNT_INTERVAL    60      /* servers in CNT_INTERVAL sec. */
+#define RETRYTIME       60      /* retry after bind or server fail */
+
+// TODO: explain, or get rid of setrlimit games
 
 #ifndef RLIMIT_NOFILE
 #define RLIMIT_NOFILE   RLIMIT_OFILE
@@ -209,20 +209,21 @@ typedef struct servtab_t {
 #else
 #define is_rpc_service(sep)       0
 #endif
-       pid_t se_wait;                        /* 0:"nowait", 1:"wait" */
+       pid_t se_wait;                        /* 0:"nowait", 1:"wait", >1:"wait" */
+                                             /* and waiting for this pid */
        socktype_t se_socktype;               /* SOCK_STREAM/DGRAM/RDM/... */
        family_t se_family;                   /* AF_UNIX/INET[6] */
-       smallint se_proto_no;                 /* almost "getprotobyname(se_proto)" */
+       /* se_proto_no is used by RPC code only... hmm */
+       smallint se_proto_no;                 /* IPPROTO_TCP/UDP, n/a for AF_UNIX */
        smallint se_checked;                  /* looked at during merge */
+       unsigned se_max;                      /* allowed instances per minute */
+       unsigned se_count;                    /* number started since se_time */
+       unsigned se_time;                     /* whem we started counting */
        char *se_user;                        /* user name to run as */
        char *se_group;                       /* group name to run as, can be NULL */
 #ifdef INETD_BUILTINS_ENABLED
        const struct builtin *se_builtin;     /* if built-in, description */
 #endif
-// TODO: wrong algorithm!!!
-       unsigned se_max;                      /* max # of instances of this service */
-       unsigned se_count;                    /* number started since se_time */
-       unsigned se_time;                     /* start of se_count */
        struct servtab_t *se_next;
        len_and_sockaddr *se_lsa;
        char *se_program;                     /* server program */
@@ -231,60 +232,54 @@ typedef struct servtab_t {
 } servtab_t;
 
 #ifdef INETD_BUILTINS_ENABLED
-               /* Echo received data */
+/* Echo received data */
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
 static void echo_stream(int, servtab_t *);
 static void echo_dg(int, servtab_t *);
 #endif
-               /* Internet /dev/null */
+/* Internet /dev/null */
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
 static void discard_stream(int, servtab_t *);
 static void discard_dg(int, servtab_t *);
 #endif
-               /* Return 32 bit time since 1900 */
+/* Return 32 bit time since 1900 */
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
 static void machtime_stream(int, servtab_t *);
 static void machtime_dg(int, servtab_t *);
 #endif
-               /* Return human-readable time */
+/* Return human-readable time */
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
 static void daytime_stream(int, servtab_t *);
 static void daytime_dg(int, servtab_t *);
 #endif
-               /* Familiar character generator */
+/* Familiar character generator */
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
 static void chargen_stream(int, servtab_t *);
 static void chargen_dg(int, servtab_t *);
 #endif
 
 struct builtin {
-       const char *bi_service;         /* internally provided service name */
-       uint8_t bi_fork;                /* 1 if stream fn should run in child */
-       /* All builtins are "nowait" */
-       /* uint8_t bi_wait; */          /* 1 if should wait for child */
+       /* NB: not necessarily NUL terminated */
+       char bi_service7[7];      /* internally provided service name */
+       uint8_t bi_fork;          /* 1 if stream fn should run in child */
        void (*bi_stream_fn)(int, servtab_t *);
        void (*bi_dgram_fn)(int, servtab_t *);
 };
 
 static const struct builtin builtins[] = {
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
-       /* Echo received data */
        { "echo", 1, echo_stream, echo_dg },
 #endif
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
-       /* Internet /dev/null */
        { "discard", 1, discard_stream, discard_dg },
 #endif
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
-       /* Familiar character generator */
        { "chargen", 1, chargen_stream, chargen_dg },
 #endif
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
-       /* Return 32 bit time since 1900 */
        { "time", 0, machtime_stream, machtime_dg },
 #endif
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
-       /* Return human-readable time */
        { "daytime", 0, daytime_stream, daytime_dg },
 #endif
 };
@@ -305,8 +300,8 @@ struct globals {
        FILE *fconfig;
        char *default_local_hostname;
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
-       char *endring;
-       char *ringpos;
+       char *end_ring;
+       char *ring_pos;
        char ring[128];
 #endif
        fd_set allsock;
@@ -333,8 +328,8 @@ struct BUG_G_too_big {
 #define default_local_hostname (G.default_local_hostname)
 #define first_ps_byte   (G.first_ps_byte  )
 #define last_ps_byte    (G.last_ps_byte   )
-#define endring         (G.endring        )
-#define ringpos         (G.ringpos        )
+#define end_ring        (G.end_ring       )
+#define ring_pos        (G.ring_pos       )
 #define ring            (G.ring           )
 #define allsock         (G.allsock        )
 #define line            (G.line           )
@@ -357,6 +352,8 @@ static len_and_sockaddr *xzalloc_lsa(int family)
        int sz;
 
        sz = sizeof(struct sockaddr_in);
+       if (family == AF_UNIX)
+               sz = sizeof(struct sockaddr_un);
 #if ENABLE_FEATURE_IPV6
        if (family == AF_INET6)
                sz = sizeof(struct sockaddr_in6);
@@ -367,7 +364,6 @@ static len_and_sockaddr *xzalloc_lsa(int family)
        return lsa;     
 }
 
-
 static void rearm_alarm(void)
 {
        if (!alarm_armed) {
@@ -660,10 +656,8 @@ static NOINLINE servtab_t *parse_one_line(void)
        }
 
        arg = next_word(&cp);
-       if (arg == NULL) {
-               /* A blank line. */
+       if (arg == NULL) /* a blank line. */
                goto more;
-       }
 
        /* [host:]service socktype proto wait user[:group] prog [args] */
        /* Check for "host:...." line */
@@ -764,9 +758,9 @@ static NOINLINE servtab_t *parse_one_line(void)
                }
                /* we don't really need getprotobyname()! */
                if (strcmp(arg, "tcp") == 0)
-                       sep->se_proto_no = 6;
+                       sep->se_proto_no = IPPROTO_TCP; /* = 6 */
                if (strcmp(arg, "udp") == 0)
-                       sep->se_proto_no = 17;
+                       sep->se_proto_no = IPPROTO_UDP; /* = 17 */
                if (six)
                        *six = '6';
                if (!sep->se_proto_no) /* not tcp/udp?? */
@@ -785,7 +779,11 @@ static NOINLINE servtab_t *parse_one_line(void)
                if (errno)
                        goto parse_err;
        }
-       sep->se_wait = (strcmp(arg, "wait") == 0);
+       sep->se_wait = (arg[0] != 'n' || arg[1] != 'o');
+       if (!sep->se_wait) /* "no" seen */
+               arg += 2;
+       if (strcmp(arg, "wait") != 0)
+               goto parse_err;
 
        /* user[:group] prog [args] */
        sep->se_user = xstrdup(next_word(&cp));
@@ -804,48 +802,56 @@ static NOINLINE servtab_t *parse_one_line(void)
        if (sep->se_program == NULL)
                goto parse_err;
 #ifdef INETD_BUILTINS_ENABLED
-       /* sep->se_builtin = NULL; - done by new_servtab() */
        if (strcmp(sep->se_program, "internal") == 0
+        && strlen(sep->se_service) <= 7
         && (sep->se_socktype == SOCK_STREAM
             || sep->se_socktype == SOCK_DGRAM)
        ) {
                int i;
                for (i = 0; i < ARRAY_SIZE(builtins); i++)
-                       if (strcmp(builtins[i].bi_service, sep->se_service) == 0)
+                       if (strncmp(builtins[i].bi_service7, sep->se_service, 7) == 0)
                                goto found_bi;
                bb_error_msg("unknown internal service %s", sep->se_service);
                goto parse_err;
  found_bi:
                sep->se_builtin = &builtins[i];
-               sep->se_wait = 0; /* = builtins[i].bi_wait; - always 0 */
+               /* stream builtins must be "nowait", dgram must be "wait" */
+               if (sep->se_wait != (sep->se_socktype == SOCK_DGRAM))
+                       goto parse_err;
        }
 #endif
        argc = 0;
-       while ((arg = next_word(&cp)) != NULL && argc < MAXARGV) {
+       while ((arg = next_word(&cp)) != NULL && argc < MAXARGV)
                sep->se_argv[argc++] = xstrdup(arg);
+
+       /* catch mixups. "<service> stream udp ..." == wtf */
+       if (sep->se_socktype == SOCK_STREAM) {
+               if (sep->se_proto_no == IPPROTO_UDP)
+                       goto parse_err;
+       }
+       if (sep->se_socktype == SOCK_DGRAM) {
+               if (sep->se_proto_no == IPPROTO_TCP)
+                       goto parse_err;
+               /* "udp nowait" is a small fork bomb :) */
+               if (!sep->se_wait)
+                       goto parse_err;
        }
-       /* while (argc <= MAXARGV) */
-       /*      sep->se_argv[argc++] = NULL; - done by new_servtab() */
-
-       /*
-        * Now that we've processed the entire line, check if the hostname
-        * specifier was a comma separated list of hostnames. If so
-        * we'll make new entries for each address.
-        */
+
+       /* check if the hostname specifier is a comma separated list
+        * of hostnames. we'll make new entries for each address. */
        while ((hostdelim = strrchr(sep->se_local_hostname, ',')) != NULL) {
                nsep = dup_servtab(sep);
-               /*
-                * NUL terminate the hostname field of the existing entry,
-                * and make a dup for the new entry.
-                */
+               /* NUL terminate the hostname field of the existing entry,
+                * and make a dup for the new entry. */
                *hostdelim++ = '\0';
                nsep->se_local_hostname = xstrdup(hostdelim);
                nsep->se_next = sep->se_next;
                sep->se_next = nsep;
        }
 
-       /* Was doing it here: */
+       /* was doing it here: */
        /* DNS resolution, create copies for each IP address */
+       /* IPv6-ization destroyed it :( */
 
        return sep;
 }
@@ -947,15 +953,9 @@ static void reread_config_file(int sig ATTRIBUTE_UNUSED)
                switch (sep->se_family) {
                        struct sockaddr_un *sun;
                case AF_UNIX:
-                       /* we have poor infrastructure for AF_UNIX... */
-                       n = strlen(sep->se_service);
-                       if (n > sizeof(sun->sun_path) - 1)
-                               n = sizeof(sun->sun_path) - 1;
-                       lsa = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un));
-                       lsa->len = sizeof(struct sockaddr_un);
+                       lsa = xzalloc_lsa(AF_UNIX);
                        sun = (struct sockaddr_un*)&lsa->u.sa;
-                       sun->sun_family = AF_UNIX;
-                       strncpy(sun->sun_path, sep->se_service, n);
+                       safe_strncpy(sun->sun_path, sep->se_service, sizeof(sun->sun_path));
                        break;
 
                default: /* case AF_INET, case AF_INET6 */
@@ -1259,8 +1259,8 @@ int inetd_main(int argc, char **argv)
                                                sep->se_count = 0;
                                        }
                                }
-                               /* on NOMMU, streamed echo, chargen and discard
-                                * builtins wouldn't work, but they are
+                               /* on NOMMU, streamed chargen
+                                * builtin wouldn't work, but it is
                                 * not allowed on NOMMU (ifdefed out) */
 #ifdef INETD_BUILTINS_ENABLED
                                if (BB_MMU && sep->se_builtin)
@@ -1311,8 +1311,42 @@ int inetd_main(int argc, char **argv)
                                continue; /* -> check next fd in fd set */
                        }
 #endif
-                       /* child. prepare env and exec program */
+                       /* child */
                        setsid();
+#if 0
+/* This does not work.
+ * Actually, it _almost_ works. The idea behind it is: child
+ * can peek at (already received and buffered by kernel) UDP packet,
+ * and perform connect() on the socket so that it is linked only
+ * to this peer. But this also affects parent, because descriptors
+ * are shared after fork() a-la dup(). When parent returns to
+ * select(), it will see this descriptor attached to the peer (!)
+ * and likely still readable, will act on it and mess things up
+ * (can create many copies of same child, etc).
+ * If child will create new socket instead, then bind() and
+ * connect() it to peer's address, descriptor aliasing problem
+ * is solved, but first packet cannot be "transferred" to the new
+ * socket. It is not a problem if child can account for this,
+ * but our child will exec - and exec'ed program does not know
+ * about this "lost packet" problem! Pity... */
+                       /* "nowait" udp[6]. Hmmm... */
+                       if (!sep->se_wait
+                        && sep->se_socktype == SOCK_DGRAM
+                        && sep->se_family != AF_UNIX
+                       ) {
+                               len_and_sockaddr *lsa = xzalloc_lsa(sep->se_family);
+                               /* peek at the packet and remember peer addr */
+                               int r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT,
+                                       &lsa->u.sa, &lsa->len);
+                               if (r >= 0)
+                                       /* make this socket "connected" to peer addr:
+                                        * only packets from this peer will be recv'ed,
+                                        * and bare write()/send() will work on it */
+                                       connect(ctrl, &lsa->u.sa, lsa->len);
+                               free(lsa);
+                       }
+#endif
+                       /* prepare env and exec program */
                        pwd = getpwnam(sep->se_user);
                        if (pwd == NULL) {
                                bb_error_msg("%s: no such user", sep->se_user);
@@ -1342,8 +1376,8 @@ int inetd_main(int argc, char **argv)
                                        bb_perror_msg("setrlimit");
                        closelog();
                        xmove_fd(ctrl, 0);
-                       dup2(0, 1);
-                       dup2(0, 2);
+                       xdup2(0, 1);
+                       xdup2(0, 2);
                        /* NB: among others, this loop closes listening socket
                         * for nowait stream children */
                        for (sep2 = serv_list; sep2; sep2 = sep2->se_next)
@@ -1378,6 +1412,8 @@ static void echo_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED)
        }
 #else
        static const char *const args[] = { "cat", NULL };
+       /* no error messages */
+       xmove_fd(xopen("/dev/null", O_WRONLY), STDERR_FILENO);
        BB_EXECVP("cat", (char**)args);
        _exit(1);
 #endif
@@ -1404,8 +1440,16 @@ static void echo_dg(int s, servtab_t *sep)
 /* ARGSUSED */
 static void discard_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED)
 {
+#if BB_MMU
        while (safe_read(s, line, LINE_SIZE) > 0)
                continue;
+#else
+       static const char *const args[] = { "dd", "of=/dev/null", NULL };
+       /* no error messages */
+       xmove_fd(xopen("/dev/null", O_WRONLY), STDERR_FILENO);
+       BB_EXECVP("dd", (char**)args);
+       _exit(1);
+#endif
 }
 /* ARGSUSED */
 static void discard_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
@@ -1418,14 +1462,14 @@ static void discard_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
 
 #if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
 #define LINESIZ 72
-static void initring(void)
+static void init_ring(void)
 {
        int i;
 
-       endring = ring;
+       end_ring = ring;
        for (i = 0; i <= 128; ++i)
                if (isprint(i))
-                       *endring++ = i;
+                       *end_ring++ = i;
 }
 /* Character generator. MMU arches only. */
 /* ARGSUSED */
@@ -1435,8 +1479,8 @@ static void chargen_stream(int s, servtab_t *sep)
        int len;
        char text[LINESIZ + 2];
 
-       if (!endring) {
-               initring();
+       if (!end_ring) {
+               init_ring();
                rs = ring;
        }
 
@@ -1444,14 +1488,14 @@ static void chargen_stream(int s, servtab_t *sep)
        text[LINESIZ + 1] = '\n';
        rs = ring;
        for (;;) {
-               len = endring - rs;
+               len = end_ring - rs;
                if (len >= LINESIZ)
                        memmove(text, rs, LINESIZ);
                else {
                        memmove(text, rs, len);
                        memmove(text + len, ring, LINESIZ - len);
                }
-               if (++rs == endring)
+               if (++rs == end_ring)
                        rs = ring;
                xwrite(s, text, sizeof(text));
        }
@@ -1469,20 +1513,20 @@ static void chargen_dg(int s, servtab_t *sep)
        if (recvfrom(s, text, sizeof(text), MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
                return;
 
-       if (!endring) {
-               initring();
-               ringpos = ring;
+       if (!end_ring) {
+               init_ring();
+               ring_pos = ring;
        }
 
-       len = endring - ringpos;
+       len = end_ring - ring_pos;
        if (len >= LINESIZ)
-               memmove(text, ringpos, LINESIZ);
+               memmove(text, ring_pos, LINESIZ);
        else {
-               memmove(text, ringpos, len);
+               memmove(text, ring_pos, len);
                memmove(text + len, ring, LINESIZ - len);
        }
-       if (++ringpos == endring)
-               ringpos = ring;
+       if (++ring_pos == end_ring)
+               ring_pos = ring;
        text[LINESIZ] = '\r';
        text[LINESIZ + 1] = '\n';
        sendto(s, text, sizeof(text), 0, &lsa->u.sa, lsa->len);
@@ -1498,7 +1542,7 @@ static void chargen_dg(int s, servtab_t *sep)
  * we must add 2208988800 seconds to this figure to make up for
  * some seventy years Bell Labs was asleep.
  */
-static unsigned machtime(void)
+static uint32_t machtime(void)
 {
        struct timeval tv;
 
index 2ee833e3f6585554602b8952c49ac528feb774e8..ce4829584f06acbd54b37e0e2196fc31fee1a458 100644 (file)
@@ -751,6 +751,11 @@ int nc_main(int argc, char **argv)
        if (option_mask32 & OPT_s) { /* local address */
                /* if o_lport is still 0, then we will use random port */
                ouraddr = xhost2sockaddr(str_s, o_lport);
+#ifdef BLOAT
+               /* prevent spurious "UDP listen needs !0 port" */
+               o_lport = get_nport(ouraddr);
+               o_lport = ntohs(o_lport);
+#endif
                x = xsocket(ouraddr->u.sa.sa_family, x, 0);
        } else {
                /* We try IPv6, then IPv4, unless addr family is
@@ -771,12 +776,14 @@ int nc_main(int argc, char **argv)
        setsockopt(netfd, SOL_SOCKET, SO_SNDBUF, &o_sndbuf, sizeof o_sndbuf);
 #endif
 
+#ifdef BLOAT
        if (OPT_l && (option_mask32 & (OPT_u|OPT_l)) == (OPT_u|OPT_l)) {
                /* apparently UDP can listen ON "port 0",
                 but that's not useful */
                if (!o_lport)
                        bb_error_msg_and_die("UDP listen needs nonzero -p port");
        }
+#endif
 
        FD_SET(0, &ding1);                        /* stdin *is* initially open */
        if (proggie) {