/* 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;
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;
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);
}
* 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;
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;
}
/* 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)
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);
}
/* $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
#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 */
} 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
};
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;
#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 )
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);
return lsa;
}
-
static void rearm_alarm(void)
{
if (!alarm_armed) {
}
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 */
}
/* 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?? */
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));
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;
}
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 */
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)
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);
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)
}
#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
/* 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)
#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 */
int len;
char text[LINESIZ + 2];
- if (!endring) {
- initring();
+ if (!end_ring) {
+ init_ring();
rs = ring;
}
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));
}
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);
* 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;