fakeinetd: attempted ipv6-ization (and it's done)
authorDenis Vlasenko <vda.linux@googlemail.com>
Thu, 11 Jan 2007 23:26:13 +0000 (23:26 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Thu, 11 Jan 2007 23:26:13 +0000 (23:26 -0000)
but entire applet can be orders of magnitude smaller
if written as an inetd service.
So did that (#ifdef'ed out entire old version).
inetd version is less than 10% of old one!
function                                             old     new   delta
packed_usage                                       22083   22105     +22
nobodystr                                              4       -      -4
bind_ip_address                                        4       -      -4
ident_substr                                          20       -     -20
chmatch                                               22       -     -22
movefd                                                25       -     -25
skipchars                                             49       -     -49
handlexitsigs                                         51       -     -51
replyError                                            70       -     -70
.rodata                                           158120  158024     -96
deleteConn                                           102       -    -102
G                                                    524     388    -136
conns                                                560       -    -560
fakeidentd_main                                     1457     143   -1314
------------------------------------------------------------------------------
(add/remove: 0/10 grow/shrink: 1/3 up/down: 22/-2453)       Total: -2431 bytes

include/usage.h
networking/fakeidentd.c
networking/inetd.c

index 14650e5..4e52065 100644 (file)
        "\\( and \\) or null; if \\( and \\) are not used, they return the number\n" \
        "of characters matched or 0."
 
+#if 0 /* bloaty */
 #define fakeidentd_trivial_usage \
        "[-b ip] [STRING]"
 #define fakeidentd_full_usage \
        "\n\nOptions:\n" \
        "       -b      Bind to ip address\n" \
        "       STRING  The ident answer string (default is nobody)"
+#else /* inetd-only */
+#define fakeidentd_trivial_usage \
+       "[username]"
+#define fakeidentd_full_usage \
+       "Return a (faked) ident response.\n" \
+       "This applet is meant to run from inetd.\n" \
+       "Optional argument is the username to return (default is 'nobody')."
+#endif
 
 #define false_trivial_usage \
        ""
index 7eac480..8c07082 100644 (file)
@@ -9,12 +9,62 @@
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
+/* Ident crash course
+ *
+ * Incoming requests are of form "6191, 23\r\n" - peer asks us
+ * "which user connected from your port 6191 to my port 23?"
+ * We should answer:
+ * "6193, 23 : USERID : UNIX : username\r\n"
+ * and close the connection.
+ * We can also reply:
+ * "6195, 23 : USERID : OTHER[,US-ASCII] : username\r\n"
+ * "6195, 23 : ERROR : INVALID-PORT/NO-USER/HIDDEN-USER/UNKNOWN-ERROR\r\n"
+ * but we probably will never want that.
+ */
+
 #include "busybox.h"
+
+#define SANE_INETD_ONLY_VERSION
+
+#ifdef SANE_INETD_ONLY_VERSION
+
+int fakeidentd_main(int argc, char **argv)
+{
+       char buf[64];
+       const char *bogouser = "nobody";
+       char *cur = buf;
+       int rem = sizeof(buf)-1;
+
+       if (argv[1])
+               bogouser = argv[1];
+
+       alarm(30);
+       while (1) {
+               char *p;
+               int sz = safe_read(0, cur, rem);
+               if (sz < 0) return 1;
+               cur[sz] = '\0';
+               p = strpbrk(cur, "\r\n");
+               if (p) {
+                       *p = '\0';
+                       break;
+               }
+               cur += sz;
+               rem -= sz;
+               if (!rem || !sz)
+                       break;
+       }
+       printf("%s : USERID : UNIX : %s\r\n", buf, bogouser);
+       return 0;
+}
+
+#else
+
+/* Welcome to the bloaty horrors */
+
 #include <sys/syslog.h>
 #include <sys/uio.h>
 
-
-#define IDENT_PORT  113
 #define MAXCONNS    20
 #define MAXIDLETIME 45
 
@@ -43,9 +93,9 @@ enum { ident_substr_len = sizeof(ident_substr) - 1 };
  * in `conns' array + FCS
  */
 static struct {
-       char buf[20];
-       int len;
        time_t lasttime;
+       int len;
+       char buf[20];
 } conns[MAXCONNS];
 
 /* When using global variables, bind those at least to a structure. */
@@ -55,111 +105,92 @@ static struct {
        int conncnt;
 } G;
 
-/*
- * Prototypes
- */
-static void reply(int s, char *buf);
-static void replyError(int s, char *buf);
+static char *bind_ip_address;
 
-static const char *nobodystr = "nobody"; /* this needs to be declared like this */
-static char *bind_ip_address = "0.0.0.0";
+static int chmatch(char c, char *chars)
+{
+       for (; *chars; chars++)
+               if (c == *chars)
+                       return 1;
+       return 0;
+}
 
-static void movefd(int from, int to)
+static int skipchars(char **p, char *chars)
 {
-       if (from != to) {
-               dup2(from, to);
-               close(from);
-       }
+       while (chmatch(**p, chars))
+               (*p)++;
+       if (**p == '\r' || **p == '\n')
+               return 0;
+       return 1;
 }
 
-static void inetbind(void)
+static int parseAddrs(char *ptr, char **myaddr, char **heraddr)
 {
-       int s, port;
-       struct sockaddr_in addr;
-       int len = sizeof(addr);
-       struct servent *se;
+       /* parse <port-on-server> , <port-on-client> */
 
-       se = getservbyname("identd", "tcp");
-       port = IDENT_PORT;
-       if (se)
-               port = se->s_port;
+       if (!skipchars(&ptr, " \t"))
+               return -1;
 
-       s = xsocket(AF_INET, SOCK_STREAM, 0);
+       *myaddr = ptr;
 
-       setsockopt_reuseaddr(s);
+       if (!skipchars(&ptr, "1234567890"))
+               return -1;
 
-       memset(&addr, 0, sizeof(addr));
-       addr.sin_addr.s_addr = inet_addr(bind_ip_address);
-       addr.sin_family = AF_INET;
-       addr.sin_port = htons(port);
+       if (!chmatch(*ptr, " \t,"))
+               return -1;
 
-       xbind(s, (struct sockaddr *)&addr, len);
-       xlisten(s, 5);
+       *ptr++ = '\0';
 
-       movefd(s, 0);
-}
+       if (!skipchars(&ptr, " \t,") )
+               return -1;
 
-static void handlexitsigs(int signum)
-{
-       if (unlink(PIDFILE) < 0)
-               close(open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0644));
-       exit(0);
-}
+       *heraddr = ptr;
 
-/* May succeed. If not, won't care. */
-static void writepid(uid_t nobody, uid_t nogrp)
-{
-       char buf[sizeof(int)*3 + 2];
-       int fd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+       skipchars(&ptr, "1234567890");
 
-       if (fd < 0)
-               return;
+       if (!chmatch(*ptr, " \n\r"))
+               return -1;
 
-       sprintf(buf, "%d\n", getpid());
-       write(fd, buf, strlen(buf));
-       fchown(fd, nobody, nogrp);
-       close(fd);
+       *ptr = '\0';
 
-       /* should this handle ILL, ... (see signal(7)) */
-       signal(SIGTERM, handlexitsigs);
-       signal(SIGINT,  handlexitsigs);
-       signal(SIGQUIT, handlexitsigs);
+       return 0;
 }
 
-/* return 0 as parent, 1 as child */
-static int godaemon(void)
+static void replyError(int s, char *buf)
 {
-       uid_t nobody, nogrp;
-       struct passwd *pw;
-
-       switch (fork()) {
-       case -1:
-               bb_perror_msg_and_die("fork");
-
-       case 0:
-               pw = getpwnam(nobodystr);
-               if (pw == NULL)
-                       bb_error_msg_and_die("cannot find uid/gid of user '%s'", nobodystr);
-               nobody = pw->pw_uid;
-               nogrp = pw->pw_gid;
-               writepid(nobody, nogrp);
-
-               close(0);
-               inetbind();
-               xsetgid(nogrp);
-               xsetuid(nobody);
-               close(1);
-               close(2);
+       struct iovec iv[3];
+       iv[0].iov_base = "0, 0 : ERROR : ";   iv[0].iov_len = 15;
+       iv[1].iov_base = buf;                 iv[1].iov_len = strlen(buf);
+       iv[2].iov_base = "\r\n";              iv[2].iov_len = 2;
+       writev(s, iv, 3);
+}
 
-               signal(SIGHUP, SIG_IGN);
-               signal(SIGPIPE, SIG_IGN); /* connection closed when writing (raises ???) */
+static void reply(int s, char *buf)
+{
+       char *myaddr, *heraddr;
 
-               setsid();
+       myaddr = heraddr = NULL;
 
-               return 1;
+       if (parseAddrs(buf, &myaddr, &heraddr))
+               replyError(s, "X-INVALID-REQUEST");
+       else {
+               struct iovec iv[6];
+               iv[0].iov_base = myaddr;               iv[0].iov_len = strlen(myaddr);
+               iv[1].iov_base = ", ";                 iv[1].iov_len = 2;
+               iv[2].iov_base = heraddr;              iv[2].iov_len = strlen(heraddr);
+               iv[3].iov_base = (void *)ident_substr; iv[3].iov_len = ident_substr_len;
+               iv[4].iov_base = (void *)G.identuser;  iv[4].iov_len = strlen(G.identuser);
+               iv[5].iov_base = "\r\n";               iv[5].iov_len = 2;
+               writev(s, iv, 6);
        }
+}
 
-       return 0;
+static void movefd(int from, int to)
+{
+       if (from != to) {
+               dup2(from, to);
+               close(from);
+       }
 }
 
 static void deleteConn(int s)
@@ -215,15 +246,32 @@ static int checkInput(char *buf, int len, int l)
        return 0;
 }
 
+/* May succeed. If not, won't care. */
+static const char *to_unlink;
+static void writepid(void)
+{
+       int fd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+       if (fd < 0)
+               return;
+       to_unlink = PIDFILE;
+       fdprintf(fd, "%d\n", getpid());
+       close(fd);
+}
+
+static void handlexitsigs(int signum)
+{
+       if (to_unlink)
+               if (unlink(to_unlink) < 0)
+                       close(open(to_unlink, O_WRONLY|O_CREAT|O_TRUNC, 0644));
+       exit(0);
+}
+
 int fakeidentd_main(int argc, char **argv)
 {
-       /* This applet is an inetd-style daemon */
-       openlog(applet_name, 0, LOG_DAEMON);
-       logmode = LOGMODE_SYSLOG;
+       int fd;
+       pid_t pid;
 
-       memset(conns, 0, sizeof(conns));
-       memset(&G, 0, sizeof(G));
-       FD_ZERO(&G.readfds);
+       /* FD_ZERO(&G.readfds); - in bss, already zeroed */
        FD_SET(0, &G.readfds);
 
        /* handle -b <ip> parameter */
@@ -232,11 +280,30 @@ int fakeidentd_main(int argc, char **argv)
        if (optind < argc)
                G.identuser = argv[optind];
        else
-               G.identuser = nobodystr;
+               G.identuser = "nobody";
 
-       /* daemonize and have the parent return */
-       if (godaemon() == 0)
-               return 0;
+       writepid();
+       signal(SIGTERM, handlexitsigs);
+       signal(SIGINT,  handlexitsigs);
+       signal(SIGQUIT, handlexitsigs);
+       signal(SIGHUP, SIG_IGN);
+       signal(SIGPIPE, SIG_IGN); /* ignore closed connections when writing */
+
+       fd = create_and_bind_stream_or_die(bind_ip_address, bb_lookup_port("identd", "tcp", 113));
+       xlisten(fd, 5);
+
+       pid = fork();
+       if (pid < 0)
+               bb_perror_msg_and_die("fork");
+       if (pid != 0) /* parent */
+               exit(0);
+       /* child */
+       setsid();
+       movefd(fd, 0);
+       while (fd)
+               close(fd--);
+       openlog(applet_name, 0, LOG_DAEMON);
+       logmode = LOGMODE_SYSLOG;
 
        /* main loop where we process all events and never exit */
        while (1) {
@@ -252,10 +319,11 @@ int fakeidentd_main(int argc, char **argv)
 
                        if (FD_ISSET(s, &rfds)) {
                                char *buf = conns[i].buf;
-                               unsigned int len = conns[i].len;
-                               unsigned int l;
+                               unsigned len = conns[i].len;
+                               unsigned l;
 
-                               if ((l = read(s, buf + len, sizeof(conns[0].buf) - len)) > 0) {
+                               l = read(s, buf + len, sizeof(conns[0].buf) - len);
+                               if (l > 0) {
                                        if (checkInput(buf, len, l)) {
                                                reply(s, buf);
                                                goto deleteconn;
@@ -268,10 +336,8 @@ int fakeidentd_main(int argc, char **argv)
                                } else {
                                        goto deleteconn;
                                }
-
                                conns[i].lasttime = tim;
                                continue;
-
 deleteconn:
                                deleteConn(s);
                        } else {
@@ -287,7 +353,7 @@ deleteconn:
                        int s = accept(0, NULL, 0);
 
                        if (s < 0) {
-                               if (errno != EINTR) /* EINTR */
+                               if (errno != EINTR)
                                        bb_perror_msg("accept");
                        } else {
                                if (G.conncnt == MAXCONNS)
@@ -297,7 +363,6 @@ deleteconn:
 
                                movefd(s, i + FCS); /* move if not already there */
                                FD_SET(i + FCS, &G.readfds);
-
                                conns[i].len = 0;
                                conns[i].lasttime = time(NULL);
                        }
@@ -307,81 +372,4 @@ deleteconn:
        return 0;
 }
 
-static int parseAddrs(char *ptr, char **myaddr, char **heraddr);
-static void reply(int s, char *buf)
-{
-       char *myaddr, *heraddr;
-
-       myaddr = heraddr = NULL;
-
-       if (parseAddrs(buf, &myaddr, &heraddr))
-               replyError(s, "X-INVALID-REQUEST");
-       else {
-               struct iovec iv[6];
-               iv[0].iov_base = myaddr;               iv[0].iov_len = strlen(myaddr);
-               iv[1].iov_base = ", ";                 iv[1].iov_len = 2;
-               iv[2].iov_base = heraddr;              iv[2].iov_len = strlen(heraddr);
-               iv[3].iov_base = (void *)ident_substr; iv[3].iov_len = ident_substr_len;
-               iv[4].iov_base = (void *)G.identuser;  iv[4].iov_len = strlen(G.identuser);
-               iv[5].iov_base = "\r\n";               iv[5].iov_len = 2;
-               writev(s, iv, 6);
-       }
-}
-
-static void replyError(int s, char *buf)
-{
-       struct iovec iv[3];
-       iv[0].iov_base = "0, 0 : ERROR : ";   iv[0].iov_len = 15;
-       iv[1].iov_base = buf;                 iv[1].iov_len = strlen(buf);
-       iv[2].iov_base = "\r\n";              iv[2].iov_len = 2;
-       writev(s, iv, 3);
-}
-
-static int chmatch(char c, char *chars)
-{
-       for (; *chars; chars++)
-               if (c == *chars)
-                       return 1;
-       return 0;
-}
-
-static int skipchars(char **p, char *chars)
-{
-       while (chmatch(**p, chars))
-               (*p)++;
-       if (**p == '\r' || **p == '\n')
-               return 0;
-       return 1;
-}
-
-static int parseAddrs(char *ptr, char **myaddr, char **heraddr)
-{
-       /* parse <port-on-server> , <port-on-client> */
-
-       if (!skipchars(&ptr, " \t"))
-               return -1;
-
-       *myaddr = ptr;
-
-       if (!skipchars(&ptr, "1234567890"))
-               return -1;
-
-       if (!chmatch(*ptr, " \t,"))
-               return -1;
-
-       *ptr++ = '\0';
-
-       if (!skipchars(&ptr, " \t,") )
-               return -1;
-
-       *heraddr = ptr;
-
-       skipchars(&ptr, "1234567890");
-
-       if (!chmatch(*ptr, " \n\r"))
-               return -1;
-
-       *ptr = '\0';
-
-       return 0;
-}
+#endif /* !SANE_INETD_ONLY_VERSION */
index 75665ba..93c16bf 100644 (file)
@@ -23,7 +23,7 @@
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
@@ -36,8 +36,7 @@
  * SUCH DAMAGE.
  */
 
-/*
- * Inetd - Internet super-server
+/* Inetd - Internet super-server
  *
  * This program invokes all internet services as needed.
  * connection-oriented services are invoked each time a
  * 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'',
+ * 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''.
+ * out.  The first type of server is said to be "multi-threaded";
+ * the second type of server "single-threaded".
  *
  * Inetd uses a configuration file which is read at startup
  * and, possibly, at some later time in response to a hangup signal.
- * The configuration file is ``free format'' with fields given in the
+ * The configuration file is "free format" with fields given in the
  * order shown below.  Continuation lines for an entry must begin with
  * a space or tab.  All fields must be present in each entry.
  *
  * Comment lines are indicated by a `#' in column 1.
  */
 
-/*
- * Here's the scoop concerning the user[.:]group feature:
+/* inetd rules for passing file descriptors to children
+ * (http://www.freebsd.org/cgi/man.cgi?query=inetd):
+ *
+ * The wait/nowait entry specifies whether the server that is invoked by
+ * inetd will take over the socket associated with the service access point,
+ * and thus whether inetd should wait for the server to exit before listen-
+ * ing for new service requests.  Datagram servers must use "wait", as
+ * they are always invoked with the original datagram socket bound to the
+ * specified service address.  These servers must read at least one datagram
+ * from the socket before exiting.  If a datagram server connects to its
+ * peer, freeing the socket so inetd can receive further messages on the
+ * socket, it is said to be a "multi-threaded" server; it should read one
+ * datagram from the socket and create a new socket connected to the peer.
+ * It should fork, and the parent should then exit to allow inetd to check
+ * for new service requests to spawn new servers.  Datagram servers which
+ * process all incoming datagrams on a socket and eventually time out are
+ * said to be "single-threaded".  The comsat(8), (biff(1)) and talkd(8)
+ * utilities are both examples of the latter type of datagram server.  The
+ * tftpd(8) utility is an example of a multi-threaded datagram server.
+ *
+ * Servers using stream sockets generally are multi-threaded and use the
+ * "nowait" entry. Connection requests for these services are accepted by
+ * inetd, and the server is given only the newly-accepted socket connected
+ * to a client of the service.  Most stream-based services operate in this
+ * manner.  Stream-based servers that use "wait" are started with the lis-
+ * 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.
+ */
+
+/* Here's the scoop concerning the user[.:]group feature:
  *
  * 1) set-group-option off.
  *
  *      b) other:       setgid(specified group)
  *                      initgroups(name, specified group)
  *                      setuid()
- *
  */
 
 #include "busybox.h"
 #endif
 
 /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
-#define FD_MARGIN       (8)
+#define FD_MARGIN       8
 static rlim_t rlim_ofile_cur = OPEN_MAX;
 static struct rlimit rlim_ofile;