aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libbb.h2
-rw-r--r--include/usage.h22
-rw-r--r--libbb/xfuncs.c24
-rw-r--r--networking/Kbuild3
-rw-r--r--networking/inetd.c15
-rw-r--r--networking/isrv.c337
-rw-r--r--networking/isrv_identd.c144
7 files changed, 523 insertions, 24 deletions
diff --git a/include/libbb.h b/include/libbb.h
index 4060498b8..c191dc2a0 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -278,6 +278,8 @@ extern int wait4pid(int pid);
extern void xsetgid(gid_t gid);
extern void xsetuid(uid_t uid);
extern void xdaemon(int nochdir, int noclose);
+/* More clever/thorough xdaemon */
+extern void bb_sanitize_stdio(int daemonize);
extern void xchdir(const char *path);
extern void xsetenv(const char *key, const char *value);
extern int xopen(const char *pathname, int flags);
diff --git a/include/usage.h b/include/usage.h
index 0275df3f0..2b51fad72 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -826,22 +826,16 @@
"\\( 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]"
+ "[-fiw] [-b ADDR] [STRING]"
#define fakeidentd_full_usage \
- "Return a set string to auth requests" \
- "\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
+ "Provide fake ident (auth) service" \
+ "\n\nOptions:" \
+ "\n -f Run in foreground" \
+ "\n -i Inetd mode" \
+ "\n -w Inetd 'wait' mode" \
+ "\n -b ADDR Bind to specified address" \
+ "\n STRING Ident answer string (default is 'nobody')"
#define false_trivial_usage \
""
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index 207537929..6a6bdced3 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -509,6 +509,30 @@ void xdaemon(int nochdir, int noclose)
}
#endif
+void bb_sanitize_stdio(int daemonize)
+{
+ int fd;
+ /* Mega-paranoid */
+ fd = xopen(bb_dev_null, O_RDWR);
+ while (fd < 2)
+ fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
+ if (daemonize) {
+ pid_t pid = fork();
+ if (pid < 0) /* wtf? */
+ bb_perror_msg_and_die("fork");
+ if (pid) /* parent */
+ exit(0);
+ /* child */
+ setsid();
+ /* if daemonizing, make sure we detach from stdio */
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ }
+ while (fd > 2)
+ close(fd--); /* close everything after fd#2 */
+}
+
// Die with an error message if we can't open a new socket.
int xsocket(int domain, int type, int protocol)
{
diff --git a/networking/Kbuild b/networking/Kbuild
index 4c29e45a8..bb024c9b7 100644
--- a/networking/Kbuild
+++ b/networking/Kbuild
@@ -9,7 +9,8 @@ lib-$(CONFIG_ARP) += arp.o interface.o
lib-$(CONFIG_ARPING) += arping.o
lib-$(CONFIG_DNSD) += dnsd.o
lib-$(CONFIG_ETHER_WAKE) += ether-wake.o
-lib-$(CONFIG_FAKEIDENTD) += fakeidentd.o
+#lib-$(CONFIG_FAKEIDENTD) += fakeidentd.o
+lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o
lib-$(CONFIG_FTPGET) += ftpgetput.o
lib-$(CONFIG_FTPPUT) += ftpgetput.o
lib-$(CONFIG_HOSTNAME) += hostname.o
diff --git a/networking/inetd.c b/networking/inetd.c
index 93c16bf60..f9f3b51b6 100644
--- a/networking/inetd.c
+++ b/networking/inetd.c
@@ -1289,31 +1289,28 @@ inetd_main(int argc, char *argv[])
if (CONFIG == NULL)
bb_error_msg_and_die("non-root must specify a config file");
- if (!(opt & 2)) {
#ifdef BB_NOMMU
+ if (!(opt & 2)) {
/* reexec for vfork() do continue parent */
vfork_daemon_rexec(0, 0, argc, argv, "-f");
+ }
+ bb_sanitize_stdio(0);
#else
- xdaemon(0, 0);
+ bb_sanitize_stdio(!(opt & 2));
#endif
- } else {
- setsid();
- }
logmode = LOGMODE_SYSLOG;
if (uid == 0) {
- gid_t gid = getgid();
-
/* If run by hand, ensure groups vector gets trashed */
+ gid_t gid = getgid();
setgroups(1, &gid);
}
{
FILE *fp = fopen(_PATH_INETDPID, "w");
-
if (fp != NULL) {
fprintf(fp, "%u\n", getpid());
- (void) fclose(fp);
+ fclose(fp);
}
}
diff --git a/networking/isrv.c b/networking/isrv.c
new file mode 100644
index 000000000..02ca1d787
--- /dev/null
+++ b/networking/isrv.c
@@ -0,0 +1,337 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include "isrv.h"
+
+#define DEBUG 0
+
+#if DEBUG
+#define DPRINTF(args...) bb_error_msg(args)
+#else
+#define DPRINTF(args...) ((void)0)
+#endif
+
+/* Helpers */
+
+#if 0 /*def _POSIX_MONOTONIC_CLOCK*/
+static time_t monotonic_time(void)
+{
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ time(&ts.tv_sec);
+ return ts.tv_sec;
+}
+#else
+#define monotonic_time() (time(NULL))
+#endif
+
+/* Opaque structure */
+
+struct isrv_state_t {
+ short *fd2peer; /* one per registered fd */
+ void **param_tbl; /* one per registered peer */
+ /* one per registered peer; doesn't exist if !timeout */
+ time_t *timeo_tbl;
+ int (*new_peer)(isrv_state_t *state, int fd);
+ time_t curtime;
+ int timeout;
+ int fd_count;
+ int peer_count;
+ int wr_count;
+ fd_set rd;
+ fd_set wr;
+};
+#define FD2PEER (state->fd2peer)
+#define PARAM_TBL (state->param_tbl)
+#define TIMEO_TBL (state->timeo_tbl)
+#define CURTIME (state->curtime)
+#define TIMEOUT (state->timeout)
+#define FD_COUNT (state->fd_count)
+#define PEER_COUNT (state->peer_count)
+#define WR_COUNT (state->wr_count)
+
+/* callback */
+void isrv_want_rd(isrv_state_t *state, int fd)
+{
+ FD_SET(fd, &state->rd);
+}
+
+/* callback */
+void isrv_want_wr(isrv_state_t *state, int fd)
+{
+ if (!FD_ISSET(fd, &state->wr)) {
+ WR_COUNT++;
+ FD_SET(fd, &state->wr);
+ }
+}
+
+/* callback */
+void isrv_dont_want_rd(isrv_state_t *state, int fd)
+{
+ FD_CLR(fd, &state->rd);
+}
+
+/* callback */
+void isrv_dont_want_wr(isrv_state_t *state, int fd)
+{
+ if (FD_ISSET(fd, &state->wr)) {
+ WR_COUNT--;
+ FD_CLR(fd, &state->wr);
+ }
+}
+
+/* callback */
+int isrv_register_fd(isrv_state_t *state, int peer, int fd)
+{
+ int n;
+
+ DPRINTF("register_fd(peer:%d,fd:%d)", peer, fd);
+
+ if (FD_COUNT >= FD_SETSIZE) return -1;
+ if (FD_COUNT <= fd) {
+ n = FD_COUNT;
+ FD_COUNT = fd + 1;
+
+ DPRINTF("register_fd: FD_COUNT %d", FD_COUNT);
+
+ FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+ while (n < fd) FD2PEER[n++] = -1;
+ }
+
+ DPRINTF("register_fd: FD2PEER[%d] = %d", fd, peer);
+
+ FD2PEER[fd] = peer;
+ return 0;
+}
+
+/* callback */
+void isrv_close_fd(isrv_state_t *state, int fd)
+{
+ DPRINTF("close_fd(%d)", fd);
+
+ close(fd);
+ isrv_dont_want_rd(state, fd);
+ if (WR_COUNT) isrv_dont_want_wr(state, fd);
+
+ FD2PEER[fd] = -1;
+ if (fd == FD_COUNT-1) {
+ do fd--; while (fd >= 0 && FD2PEER[fd] == -1);
+ FD_COUNT = fd + 1;
+
+ DPRINTF("close_fd: FD_COUNT %d", FD_COUNT);
+
+ FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+ }
+}
+
+/* callback */
+int isrv_register_peer(isrv_state_t *state, void *param)
+{
+ int n;
+
+ if (PEER_COUNT >= FD_SETSIZE) return -1;
+ n = PEER_COUNT++;
+
+ DPRINTF("register_peer: PEER_COUNT %d", PEER_COUNT);
+
+ PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+ PARAM_TBL[n] = param;
+ if (TIMEOUT) {
+ TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+ TIMEO_TBL[n] = CURTIME;
+ }
+ return n;
+}
+
+static void remove_peer(isrv_state_t *state, int peer)
+{
+ int movesize;
+ int fd;
+
+ DPRINTF("remove_peer(%d)", peer);
+
+ fd = FD_COUNT - 1;
+ while (fd >= 0) {
+ if (FD2PEER[fd] == peer) {
+ isrv_close_fd(state, fd);
+ fd--;
+ continue;
+ }
+ if (FD2PEER[fd] > peer)
+ FD2PEER[fd]--;
+ fd--;
+ }
+
+ PEER_COUNT--;
+ DPRINTF("remove_peer: PEER_COUNT %d", PEER_COUNT);
+
+ movesize = (PEER_COUNT - peer) * sizeof(void*);
+ if (movesize > 0) {
+ memcpy(&PARAM_TBL[peer], &PARAM_TBL[peer+1], movesize);
+ if (TIMEOUT)
+ memcpy(&TIMEO_TBL[peer], &TIMEO_TBL[peer+1], movesize);
+ }
+ PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+ if (TIMEOUT)
+ TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+}
+
+static void handle_accept(isrv_state_t *state, int fd)
+{
+ int n, newfd;
+
+ fcntl(fd, F_SETFL, (int)(PARAM_TBL[0]) | O_NONBLOCK);
+ newfd = accept(fd, NULL, 0);
+ fcntl(fd, F_SETFL, (int)(PARAM_TBL[0]));
+ if (newfd < 0) {
+ if (errno == EAGAIN) return;
+ /* Most probably someone gave us wrong fd type
+ * (for example, non-socket) */
+ bb_perror_msg_and_die("accept");
+ }
+
+ DPRINTF("new_peer(%d)", newfd);
+ n = state->new_peer(state, newfd);
+ if (n)
+ remove_peer(state, n); /* unsuccesful peer start */
+}
+
+void BUG_sizeof_fd_set_is_strange(void);
+static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void **))
+{
+ enum { LONG_CNT = sizeof(fd_set) / sizeof(long) };
+ int fds_pos;
+ int fd, peer;
+ int fd_cnt = FD_COUNT;
+
+ if (LONG_CNT * sizeof(long) != sizeof(fd_set))
+ BUG_sizeof_fd_set_is_strange();
+
+ fds_pos = 0;
+ while (1) {
+ /* Find next nonzero bit */
+ while (fds_pos < LONG_CNT) {
+ if (((long*)fds)[fds_pos] == 0) {
+ fds_pos++;
+ continue;
+ }
+ /* Found non-zero word */
+ fd = fds_pos * sizeof(long)*8; /* word# -> bit# */
+ while (1) {
+ if (FD_ISSET(fd, fds)) {
+ FD_CLR(fd, fds);
+ goto found_fd;
+ }
+ fd++;
+ }
+ }
+ break; /* all words are zero */
+ found_fd:
+ if (fd >= fd_cnt) /* paranoia */
+ break;
+ DPRINTF("handle_fd_set: fd %d is active", fd);
+ peer = FD2PEER[fd];
+ if (peer == 0) {
+ handle_accept(state, fd);
+ continue;
+ }
+ DPRINTF("h(fd:%d)", fd);
+ if (h(fd, &PARAM_TBL[peer])) {
+ /* this peer is gone */
+ remove_peer(state, peer);
+ } else if (TIMEOUT) {
+ TIMEO_TBL[peer] = monotonic_time();
+ }
+ }
+}
+
+static void handle_timeout(isrv_state_t *state, int (*do_timeout)(void **))
+{
+ int n, peer;
+ peer = PEER_COUNT-1;
+ /* peer 0 is not checked */
+ while (peer > 0) {
+ DPRINTF("peer %d: time diff %d", peer, (int)(CURTIME - TIMEO_TBL[peer]));
+
+ if ((CURTIME - TIMEO_TBL[peer]) > TIMEOUT) {
+ DPRINTF("peer %d: do_timeout()", peer);
+ n = do_timeout(&PARAM_TBL[peer]);
+ if (n)
+ remove_peer(state, peer);
+ }
+ peer--;
+ }
+}
+
+/* Driver */
+void isrv_run(
+ int listen_fd,
+ int (*new_peer)(isrv_state_t *state, int fd),
+ int (*do_rd)(int fd, void **),
+ int (*do_wr)(int fd, void **),
+ int (*do_timeout)(void **),
+ int timeout,
+ int exit_if_no_clients)
+{
+ isrv_state_t *state = xzalloc(sizeof(*state));
+ state->new_peer = new_peer;
+ state->timeout = timeout;
+
+ /* register "peer" #0 - it will accept new connections */
+ isrv_register_peer(state, NULL);
+ isrv_register_fd(state, /*peer:*/ 0, listen_fd);
+ isrv_want_rd(state, listen_fd);
+ /* remember flags to make blocking<->nonblocking switch faster */
+ PARAM_TBL[0] = (void*) (fcntl(listen_fd, F_GETFL, 0));
+
+ while (1) {
+ struct timeval tv;
+ fd_set rd;
+ fd_set wr;
+ fd_set *wrp = NULL;
+ int n;
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ rd = state->rd;
+ if (WR_COUNT) {
+ wr = state->wr;
+ wrp = &wr;
+ }
+
+ DPRINTF("run: select(FD_COUNT:%d,timeout:%d)...", FD_COUNT, timeout);
+ n = select(FD_COUNT, &rd, wrp, NULL, timeout ? &tv : NULL);
+ DPRINTF("run: ...select:%d", n);
+
+ if (n < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("select");
+ continue;
+ }
+
+ if (exit_if_no_clients && n == 0 && PEER_COUNT <= 1)
+ break;
+
+ if (timeout) {
+ time_t t = monotonic_time();
+ if (t != CURTIME) {
+ CURTIME = t;
+ handle_timeout(state, do_timeout);
+ }
+ }
+ if (n > 0) {
+ handle_fd_set(state, &rd, do_rd);
+ if (wrp)
+ handle_fd_set(state, wrp, do_wr);
+ }
+ }
+ DPRINTF("run: bailout");
+}
diff --git a/networking/isrv_identd.c b/networking/isrv_identd.c
new file mode 100644
index 000000000..b9481f8d3
--- /dev/null
+++ b/networking/isrv_identd.c
@@ -0,0 +1,144 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Fake identd server.
+ *
+ * Copyright (C) 2007 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include <syslog.h>
+#include "busybox.h"
+#include "isrv.h"
+
+enum { TIMEOUT = 20 };
+
+/* Why use alarm(TIMEOUT-1)?
+ * isrv's internal select() will run with timeout=TIMEOUT.
+ * If nothing happens during TIMEOUT-1 seconds (no accept/read),
+ * then ALL sessions timed out by now. Instead of closing them one-by-one
+ * (isrv calls do_timeout for each 'stale' session),
+ * SIGALRM triggered by alarm(TIMEOUT-1) will kill us, terminating them all.
+ */
+
+typedef struct identd_buf_t {
+ int pos;
+ int fd_flag;
+ char buf[64 - 2*sizeof(int)];
+} identd_buf_t;
+
+static const char *bogouser = "nobody";
+
+static int new_peer(isrv_state_t *state, int fd)
+{
+ int peer;
+ identd_buf_t *buf = xzalloc(sizeof(*buf));
+
+ alarm(TIMEOUT - 1);
+
+ peer = isrv_register_peer(state, buf);
+ if (peer < 0)
+ return 0; /* failure */
+ if (isrv_register_fd(state, peer, fd) < 0)
+ return peer; /* failure, unregister peer */
+
+ buf->fd_flag = fcntl(fd, F_GETFL, 0) | O_NONBLOCK;
+ isrv_want_rd(state, fd);
+ return 0;
+}
+
+static int do_rd(int fd, void **paramp)
+{
+ identd_buf_t *buf = *paramp;
+ char *cur, *p;
+ int sz;
+
+ alarm(TIMEOUT - 1);
+
+ cur = buf->buf + buf->pos;
+
+ fcntl(fd, F_SETFL, buf->fd_flag | O_NONBLOCK);
+ sz = safe_read(fd, cur, sizeof(buf->buf) - buf->pos);
+
+ if (sz < 0) {
+ if (errno != EAGAIN)
+ goto term; /* terminate this session if !EAGAIN */
+ goto ok;
+ }
+
+ buf->pos += sz;
+ buf->buf[buf->pos] = '\0';
+ p = strpbrk(cur, "\r\n");
+ if (p)
+ *p = '\0';
+ if (p || !sz || buf->pos == sizeof(buf->buf)) {
+ /* fd is still in nonblocking mode - we never block here */
+ fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser);
+ goto term;
+ }
+ ok:
+ fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
+ return 0;
+ term:
+ fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
+ free(buf);
+ return 1;
+}
+
+static int do_timeout(void **paramp)
+{
+ return 1; /* terminate session */
+}
+
+static void inetd_mode(void)
+{
+ identd_buf_t *buf = xzalloc(sizeof(*buf));
+ /* We do NOT want nonblocking I/O here! */
+ buf->fd_flag = fcntl(0, F_GETFL, 0);
+ while (do_rd(0, (void*)&buf) == 0) /* repeat */;
+}
+
+int fakeidentd_main(int argc, char **argv)
+{
+ enum {
+ OPT_foreground = 0x1,
+ OPT_inetd = 0x2,
+ OPT_inetdwait = 0x4,
+ OPT_nodeamon = 0x7,
+ OPT_bindaddr = 0x8,
+ };
+
+ const char *bind_address = NULL;
+ unsigned opt;
+ int fd;
+
+ opt = getopt32(argc, argv, "fiwb:", &bind_address);
+ if (optind < argc)
+ bogouser = argv[optind];
+
+ /* Daemonize if no -f or -i or -w */
+ bb_sanitize_stdio(!(opt & OPT_nodeamon));
+ if (!(opt & OPT_nodeamon)) {
+ openlog(applet_name, 0, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ if (opt & OPT_inetd) {
+ inetd_mode();
+ return 0;
+ }
+
+ /* Ignore closed connections when writing */
+ signal(SIGPIPE, SIG_IGN);
+
+ if (opt & OPT_inetdwait) {
+ fd = 0;
+ } else {
+ fd = create_and_bind_stream_or_die(bind_address,
+ bb_lookup_port("identd", "tcp", 113));
+ xlisten(fd, 5);
+ }
+
+ isrv_run(fd, new_peer, do_rd, NULL, do_timeout, TIMEOUT, 1);
+ return 0;
+}