diff options
-rw-r--r-- | ipsvd/tcpudp.c | 12 | ||||
-rw-r--r-- | libbb/signals.c | 7 | ||||
-rw-r--r-- | loginutils/vlock.c | 84 | ||||
-rw-r--r-- | miscutils/devfsd.c | 9 | ||||
-rw-r--r-- | miscutils/rx.c | 232 | ||||
-rw-r--r-- | networking/inetd.c | 258 | ||||
-rw-r--r-- | networking/nc_bloaty.c | 7 |
7 files changed, 308 insertions, 301 deletions
diff --git a/ipsvd/tcpudp.c b/ipsvd/tcpudp.c index cb57e59..8b4ae88 100644 --- a/ipsvd/tcpudp.c +++ b/ipsvd/tcpudp.c @@ -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); diff --git a/libbb/signals.c b/libbb/signals.c index 1929cb8..a327e87 100644 --- a/libbb/signals.c +++ b/libbb/signals.c @@ -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 */ } diff --git a/loginutils/vlock.c b/loginutils/vlock.c index 6e928e2..846733e 100644 --- a/loginutils/vlock.c +++ b/loginutils/vlock.c @@ -1,5 +1,4 @@ /* vi: set sw=4 ts=4: */ - /* * vlock implementation for busybox * @@ -16,27 +15,25 @@ /* 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); } diff --git a/miscutils/devfsd.c b/miscutils/devfsd.c index 286f00f..50c8203 100644 --- a/miscutils/devfsd.c +++ b/miscutils/devfsd.c @@ -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); diff --git a/miscutils/rx.c b/miscutils/rx.c index 8ccea49..9a8fcaa 100644 --- a/miscutils/rx.c +++ b/miscutils/rx.c @@ -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" @@ -29,66 +28,59 @@ #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); } diff --git a/networking/inetd.c b/networking/inetd.c index 8a4c9fb..463c7cf 100644 --- a/networking/inetd.c +++ b/networking/inetd.c @@ -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. @@ -38,21 +39,17 @@ /* 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. @@ -60,28 +57,28 @@ * 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: @@ -102,7 +99,7 @@ * 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 @@ -133,6 +130,8 @@ * 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: @@ -152,26 +151,27 @@ #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; diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c index 2ee833e..ce48295 100644 --- a/networking/nc_bloaty.c +++ b/networking/nc_bloaty.c @@ -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) { |