fakeinetd: attempted ipv6-ization (and it's done) 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 p
No binary (build failed)
—
(log yok)
| @@ -826,6 +826,7 @@ | |
| "\\( 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 \ | |
| @@ -833,6 +834,14 @@ | |
| "\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 \ | |
| "" | |
| @@ -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 */ |
| @@ -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 | |
| @@ -50,14 +49,14 @@ | |
| * 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. | |
| * | |
| @@ -105,8 +104,37 @@ | |
| * 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. | |
| * | |
| @@ -125,7 +153,6 @@ | |
| * b) other: setgid(specified group) | |
| * initgroups(name, specified group) | |
| * setuid() | |
| - | * |
| */ | |
| #include "busybox.h" | |
| @@ -161,7 +188,7 @@ | |
| #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; | |