telnetd: we were having telnetd with is ONLY inetd or ONLY standalone.

What if I need to have both?? This patch introduces
CONFIG_FEATURE_TELNETD_STANDALONE: y - both, n - only inetd.
This commit is contained in:
Denis Vlasenko 2006-11-22 15:54:52 +00:00
parent d6bbf99a8b
commit 75f8d080a2
7 changed files with 353 additions and 395 deletions

View File

@ -204,6 +204,7 @@ extern off_t bb_copyfd_size(int fd1, int fd2, off_t size);
extern off_t bb_copyfd_eof(int fd1, int fd2); extern off_t bb_copyfd_eof(int fd1, int fd2);
extern char bb_process_escape_sequence(const char **ptr); extern char bb_process_escape_sequence(const char **ptr);
extern char *bb_get_last_path_component(char *path); extern char *bb_get_last_path_component(char *path);
extern int ndelay_on(int fd);
extern DIR *xopendir(const char *path); extern DIR *xopendir(const char *path);

View File

@ -3034,22 +3034,24 @@ USE_FEATURE_START_STOP_DAEMON_FANCY( \
"computer over a network using the TELNET protocol." "computer over a network using the TELNET protocol."
#endif #endif
#ifdef CONFIG_FEATURE_TELNETD_INETD #ifdef CONFIG_FEATURE_TELNETD_STANDALONE
#define telnetd_trivial_usage \
"(inetd mode) [OPTION]"
#define telnetd_full_usage \
"Telnetd uses incoming TELNET connections via inetd.\n" \
"Options:\n" \
"\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n" \
"\t-f issue_file\tDisplay issue_file instead of /etc/issue"
#else
#define telnetd_trivial_usage \ #define telnetd_trivial_usage \
"[OPTION]" "[OPTION]"
#define telnetd_full_usage \ #define telnetd_full_usage \
"Telnetd listens for incoming TELNET connections on PORT.\n" \ "Telnetd listens for incoming TELNET connections on PORT.\n" \
"Options:\n" \ "Options:\n" \
"\t-p PORT\tlisten for connections on PORT (default 23)\n" \ "\t-p PORT\t\tlisten for connections on PORT (default 23)\n" \
"\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n" \ "\t-l LOGIN\texec LOGIN on connect\n" \
"\t-f issue_file\tDisplay issue_file instead of /etc/issue\n" \
"\t-F\t\tForeground mode\n" \
"\t-i\t\tInetd mode"
#else
#define telnetd_trivial_usage \
"[OPTION]"
#define telnetd_full_usage \
"Telnetd uses incoming TELNET connections via inetd.\n" \
"Options:\n" \
"\t-l LOGIN\texec LOGIN on connect\n" \
"\t-f issue_file\tDisplay issue_file instead of /etc/issue" "\t-f issue_file\tDisplay issue_file instead of /etc/issue"
#endif #endif

View File

@ -110,6 +110,18 @@ int xopen3(const char *pathname, int flags, int mode)
return ret; return ret;
} }
/*
int ndelay_off(int fd)
{
return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
}
*/
// Turn on nonblocking I/O on a fd
int ndelay_on(int fd)
{
return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
}
// Die with an error message if we can't write the entire buffer. // Die with an error message if we can't write the entire buffer.
void xwrite(int fd, void *buf, size_t count) void xwrite(int fd, void *buf, size_t count)
{ {

View File

@ -586,13 +586,12 @@ config TELNETD
with all that done, telnetd _should_ work.... with all that done, telnetd _should_ work....
config FEATURE_TELNETD_INETD config FEATURE_TELNETD_STANDALONE
bool "Support call from inetd only" bool "Support standalone telnetd (not inetd only)"
default n default n
depends on TELNETD depends on TELNETD
help help
Selecting this will make telnetd only callable from inetd, Selecting this will make telnetd able to run standalone.
removing the standalone support.
config TFTP config TFTP
bool "tftp" bool "tftp"

View File

@ -22,11 +22,11 @@
*/ */
/*#define DEBUG 1 */ /*#define DEBUG 1 */
#undef DEBUG #define DEBUG 0
#include "busybox.h" #include "busybox.h"
#ifdef DEBUG #if DEBUG
#define TELCMDS #define TELCMDS
#define TELOPTS #define TELOPTS
#endif #endif
@ -36,7 +36,7 @@
#define BUFSIZE 4000 #define BUFSIZE 4000
#ifdef CONFIG_FEATURE_IPV6 #if ENABLE_FEATURE_IPV6
#define SOCKET_TYPE AF_INET6 #define SOCKET_TYPE AF_INET6
typedef struct sockaddr_in6 sockaddr_type; typedef struct sockaddr_in6 sockaddr_type;
#else #else
@ -44,27 +44,23 @@ typedef struct sockaddr_in6 sockaddr_type;
typedef struct sockaddr_in sockaddr_type; typedef struct sockaddr_in sockaddr_type;
#endif #endif
#if ENABLE_LOGIN
#ifdef CONFIG_LOGIN
static const char *loginpath = "/bin/login"; static const char *loginpath = "/bin/login";
#else #else
static const char *loginpath; static const char *loginpath = DEFAULT_SHELL;
#endif #endif
static const char *issuefile = "/etc/issue.net"; static const char *issuefile = "/etc/issue.net";
/* shell name and arguments */ /* shell name and arguments */
static const char *argv_init[] = {NULL, NULL}; static const char *argv_init[2];
/* structure that describes a session */ /* structure that describes a session */
struct tsession { struct tsession {
#ifdef CONFIG_FEATURE_TELNETD_INETD
int sockfd_read, sockfd_write, ptyfd;
#else /* CONFIG_FEATURE_TELNETD_INETD */
struct tsession *next; struct tsession *next;
int sockfd, ptyfd; int sockfd_read, sockfd_write, ptyfd;
#endif /* CONFIG_FEATURE_TELNETD_INETD */
int shell_pid; int shell_pid;
/* two circular buffers */ /* two circular buffers */
char *buf1, *buf2; char *buf1, *buf2;
@ -73,7 +69,6 @@ struct tsession {
}; };
/* /*
This is how the buffers are used. The arrows indicate the movement This is how the buffers are used. The arrows indicate the movement
of data. of data.
@ -86,7 +81,6 @@ struct tsession {
+-------+ size2++ +------+ size2-- +----------+ +-------+ size2++ +------+ size2-- +----------+
Each session has got two buffers. Each session has got two buffers.
*/ */
static int maxfd; static int maxfd;
@ -95,7 +89,6 @@ static struct tsession *sessions;
/* /*
Remove all IAC's from the buffer pointed to by bf (received IACs are ignored Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
and must be removed so as to not be interpreted by the terminal). Make an and must be removed so as to not be interpreted by the terminal). Make an
uninterrupted string of characters fit for the terminal. Do this by packing uninterrupted string of characters fit for the terminal. Do this by packing
@ -113,10 +106,10 @@ static struct tsession *sessions;
what is the escape character? We aren't handling that situation here. what is the escape character? We aren't handling that situation here.
CR-LF ->'s CR mapping is also done here, for convenience CR-LF ->'s CR mapping is also done here, for convenience
*/
*/
static char * static char *
remove_iacs(struct tsession *ts, int *pnum_totty) { remove_iacs(struct tsession *ts, int *pnum_totty)
{
unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1; unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
unsigned char *ptr = ptr0; unsigned char *ptr = ptr0;
unsigned char *totty = ptr; unsigned char *totty = ptr;
@ -134,8 +127,7 @@ remove_iacs(struct tsession *ts, int *pnum_totty) {
*/ */
if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end) if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
ptr++; ptr++;
} } else {
else {
/* /*
* TELOPT_NAWS support! * TELOPT_NAWS support!
*/ */
@ -155,14 +147,13 @@ remove_iacs(struct tsession *ts, int *pnum_totty) {
break; /* incomplete, can't process */ break; /* incomplete, can't process */
ws.ws_col = (ptr[3] << 8) | ptr[4]; ws.ws_col = (ptr[3] << 8) | ptr[4];
ws.ws_row = (ptr[5] << 8) | ptr[6]; ws.ws_row = (ptr[5] << 8) | ptr[6];
(void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws); ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
ptr += 9; ptr += 9;
} } else {
else {
/* skip 3-byte IAC non-SB cmd */ /* skip 3-byte IAC non-SB cmd */
#ifdef DEBUG #if DEBUG
fprintf(stderr, "Ignoring IAC %s,%s\n", fprintf(stderr, "Ignoring IAC %s,%s\n",
TELCMD(*(ptr+1)), TELOPT(*(ptr+2))); TELCMD(ptr[1]), TELOPT(ptr[2]));
#endif #endif
ptr += 3; ptr += 3;
} }
@ -184,10 +175,10 @@ remove_iacs(struct tsession *ts, int *pnum_totty) {
static int static int
getpty(char *line) getpty(char *line, int size)
{ {
int p; int p;
#ifdef CONFIG_FEATURE_DEVPTS #if ENABLE_FEATURE_DEVPTS
p = open("/dev/ptmx", O_RDWR); p = open("/dev/ptmx", O_RDWR);
if (p > 0) { if (p > 0) {
const char *name; const char *name;
@ -198,7 +189,7 @@ getpty(char *line)
bb_perror_msg("ptsname error (is /dev/pts mounted?)"); bb_perror_msg("ptsname error (is /dev/pts mounted?)");
return -1; return -1;
} }
strcpy(line, name); safe_strncpy(line, name, size);
return p; return p;
} }
#else #else
@ -216,9 +207,8 @@ getpty(char *line)
} }
for (j = 0; j < 16; j++) { for (j = 0; j < 16; j++) {
line[9] = j < 10 ? j + '0' : j - 10 + 'a'; line[9] = j < 10 ? j + '0' : j - 10 + 'a';
#ifdef DEBUG if (DEBUG)
fprintf(stderr, "Trying to open device: %s\n", line); fprintf(stderr, "Trying to open device: %s\n", line);
#endif
p = open(line, O_RDWR | O_NOCTTY); p = open(line, O_RDWR | O_NOCTTY);
if (p >= 0) { if (p >= 0) {
line[5] = 't'; line[5] = 't';
@ -226,7 +216,7 @@ getpty(char *line)
} }
} }
} }
#endif /* CONFIG_FEATURE_DEVPTS */ #endif /* FEATURE_DEVPTS */
return -1; return -1;
} }
@ -234,7 +224,7 @@ getpty(char *line)
static void static void
send_iac(struct tsession *ts, unsigned char command, int option) send_iac(struct tsession *ts, unsigned char command, int option)
{ {
/* We rely on that there is space in the buffer for now. */ /* We rely on that there is space in the buffer for now. */
char *b = ts->buf2 + ts->rdidx2; char *b = ts->buf2 + ts->rdidx2;
*b++ = IAC; *b++ = IAC;
*b++ = command; *b++ = command;
@ -245,401 +235,376 @@ send_iac(struct tsession *ts, unsigned char command, int option)
static struct tsession * static struct tsession *
#ifdef CONFIG_FEATURE_TELNETD_INETD make_new_session(
make_new_session(void) USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
#else /* CONFIG_FEATURE_TELNETD_INETD */ SKIP_FEATURE_TELNETD_STANDALONE(void)
make_new_session(int sockfd) ) {
#endif /* CONFIG_FEATURE_TELNETD_INETD */
{
struct termios termbuf; struct termios termbuf;
int pty, pid; int fd, pid;
char tty_name[32]; char tty_name[32];
struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2); struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
ts->buf1 = (char *)(&ts[1]); ts->buf1 = (char *)(&ts[1]);
ts->buf2 = ts->buf1 + BUFSIZE; ts->buf2 = ts->buf1 + BUFSIZE;
#ifdef CONFIG_FEATURE_TELNETD_INETD /* Got a new connection, set up a tty. */
ts->sockfd_write = 1; fd = getpty(tty_name, 32);
#else /* CONFIG_FEATURE_TELNETD_INETD */ if (fd < 0) {
ts->sockfd = sockfd;
#endif /* CONFIG_FEATURE_TELNETD_INETD */
/* Got a new connection, set up a tty and spawn a shell. */
pty = getpty(tty_name);
if (pty < 0) {
bb_error_msg("all terminals in use"); bb_error_msg("all terminals in use");
return 0; return NULL;
} }
if (fd > maxfd) maxfd = fd;
if (pty > maxfd) ndelay_on(ts->ptyfd = fd);
maxfd = pty; #if ENABLE_FEATURE_TELNETD_STANDALONE
if (sock_w > maxfd) maxfd = sock_w;
ts->ptyfd = pty; if (sock_r > maxfd) maxfd = sock_r;
ndelay_on(ts->sockfd_write = sock_w);
ndelay_on(ts->sockfd_read = sock_r);
#else
ts->sockfd_write = 1;
/* xzalloc: ts->sockfd_read = 0; */
ndelay_on(0);
ndelay_on(1);
#endif
/* Make the telnet client understand we will echo characters so it /* Make the telnet client understand we will echo characters so it
* should not do it locally. We don't tell the client to run linemode, * should not do it locally. We don't tell the client to run linemode,
* because we want to handle line editing and tab completion and other * because we want to handle line editing and tab completion and other
* stuff that requires char-by-char support. * stuff that requires char-by-char support. */
*/
send_iac(ts, DO, TELOPT_ECHO); send_iac(ts, DO, TELOPT_ECHO);
send_iac(ts, DO, TELOPT_NAWS); send_iac(ts, DO, TELOPT_NAWS);
send_iac(ts, DO, TELOPT_LFLOW); send_iac(ts, DO, TELOPT_LFLOW);
send_iac(ts, WILL, TELOPT_ECHO); send_iac(ts, WILL, TELOPT_ECHO);
send_iac(ts, WILL, TELOPT_SGA); send_iac(ts, WILL, TELOPT_SGA);
if ((pid = fork()) < 0) { pid = fork();
if (pid < 0) {
free(ts);
close(fd);
bb_perror_msg("fork"); bb_perror_msg("fork");
return NULL;
} }
if (pid == 0) { if (pid > 0) {
/* In child, open the child's side of the tty. */ /* parent */
int i; ts->shell_pid = pid;
return ts;
for(i = 0; i <= maxfd; i++)
close(i);
/* make new process group */
setsid();
xopen(tty_name, O_RDWR /*| O_NOCTTY*/);
dup(0);
dup(0);
tcsetpgrp(0, getpid());
/* The pseudo-terminal allocated to the client is configured to operate in
* cooked mode, and with XTABS CRMOD enabled (see tty(4)).
*/
tcgetattr(0, &termbuf);
termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
termbuf.c_oflag |= ONLCR|XTABS;
termbuf.c_iflag |= ICRNL;
termbuf.c_iflag &= ~IXOFF;
/*termbuf.c_lflag &= ~ICANON;*/
tcsetattr(0, TCSANOW, &termbuf);
print_login_issue(issuefile, NULL);
/* exec shell, with correct argv and env */
execv(loginpath, (char *const *)argv_init);
/* NOT REACHED */
bb_perror_msg_and_die("execv");
} }
/* child */
ts->shell_pid = pid; /* open the child's side of the tty. */
fd = xopen(tty_name, O_RDWR /*| O_NOCTTY*/);
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
while (fd > 2) close(fd--);
/* make new process group */
setsid();
tcsetpgrp(0, getpid());
return ts; /* The pseudo-terminal allocated to the client is configured to operate in
* cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
tcgetattr(0, &termbuf);
termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
termbuf.c_oflag |= ONLCR|XTABS;
termbuf.c_iflag |= ICRNL;
termbuf.c_iflag &= ~IXOFF;
/*termbuf.c_lflag &= ~ICANON;*/
tcsetattr(0, TCSANOW, &termbuf);
print_login_issue(issuefile, NULL);
/* exec shell, with correct argv and env */
execv(loginpath, (char *const *)argv_init);
bb_perror_msg_and_die("execv");
} }
#ifndef CONFIG_FEATURE_TELNETD_INETD #if ENABLE_FEATURE_TELNETD_STANDALONE
static void static void
free_session(struct tsession *ts) free_session(struct tsession *ts)
{ {
struct tsession *t = sessions; struct tsession *t = sessions;
/* Unlink this telnet session from the session list. */ /* unlink this telnet session from the session list */
if (t == ts) if (t == ts)
sessions = ts->next; sessions = ts->next;
else { else {
while(t->next != ts) while (t->next != ts)
t = t->next; t = t->next;
t->next = ts->next; t->next = ts->next;
} }
kill(ts->shell_pid, SIGKILL); kill(ts->shell_pid, SIGKILL);
wait4(ts->shell_pid, NULL, 0, NULL); wait4(ts->shell_pid, NULL, 0, NULL);
close(ts->ptyfd); close(ts->ptyfd);
close(ts->sockfd); close(ts->sockfd_read);
/* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
if (ts->ptyfd == maxfd || ts->sockfd == maxfd) close(ts->sockfd_write);
maxfd--;
if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
maxfd--;
free(ts); free(ts);
/* scan all sessions and find new maxfd */
ts = sessions;
maxfd = 0;
while (ts) {
if (maxfd < ts->ptyfd)
maxfd = ts->ptyfd;
if (maxfd < ts->sockfd_read)
maxfd = ts->sockfd_read;
if (maxfd < ts->sockfd_write)
maxfd = ts->sockfd_write;
ts = ts->next;
}
} }
#endif /* CONFIG_FEATURE_TELNETD_INETD */
static int
create_socket(int port, const char *opt_bindaddr)
{
static const int on = 1;
int fd;
sockaddr_type sa;
#if !ENABLE_FEATURE_IPV6
struct in_addr bind_addr = { .s_addr = 0x0 };
/* TODO: generic string -> sockaddr converter */
if (opt_bindaddr && inet_aton(opt_bindaddr, &bind_addr) == 0)
bb_show_usage();
#endif
fd = xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
memset((void *)&sa, 0, sizeof(sa));
#if ENABLE_FEATURE_IPV6
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(port);
/* sa.sin6_addr = bind_addr6; */
#else
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr = bind_addr;
#endif
xbind(fd, (struct sockaddr *) &sa, sizeof(sa));
xlisten(fd, 1);
return fd;
}
#else /* !FEATURE_TELNETD_STANDALONE */
/* Never actually called */
void free_session(struct tsession *ts);
int create_socket(int port, const char *opt_bindaddr);
#endif
int int
telnetd_main(int argc, char **argv) telnetd_main(int argc, char **argv)
{ {
unsigned opt;
fd_set rdfdset, wrfdset; fd_set rdfdset, wrfdset;
int selret; unsigned opt;
#ifndef CONFIG_FEATURE_TELNETD_INETD int selret, maxlen, w, r;
sockaddr_type sa; struct tsession *ts;
int master_fd; #if ENABLE_FEATURE_TELNETD_STANDALONE
int on = 1; #define IS_INETD (opt & OPT_INETD)
int master_fd = -1; /* be happy, gcc */
unsigned portnbr = 23; unsigned portnbr = 23;
struct in_addr bind_addr = { .s_addr = 0x0 }; char *opt_bindaddr = NULL;
char *opt_portnbr, *opt_bindaddr; char *opt_portnbr;
#endif /* CONFIG_FEATURE_TELNETD_INETD */ #else
int maxlen, w, r; enum {
IS_INETD = 1,
#ifndef CONFIG_LOGIN master_fd = -1,
loginpath = DEFAULT_SHELL; portnbr = 23,
};
#endif #endif
enum {
OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
};
/* We use inetd-style operation unconditionally opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
* (no --foreground option), user most likely will
* look into syslog for all errors, even early ones.
* Direct all output to syslog at once.
*/
openlog(applet_name, 0, LOG_USER);
logmode = LOGMODE_SYSLOG;
opt = getopt32(argc, argv, "f:l:" SKIP_FEATURE_TELNETD_INETD("p:b:"),
&issuefile, &loginpath &issuefile, &loginpath
SKIP_FEATURE_TELNETD_INETD(, &opt_portnbr, &opt_bindaddr)); USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
/* Redirect log to syslog early, if needed */
if (IS_INETD || !(opt & OPT_FOREGROUND)) {
openlog(applet_name, 0, LOG_USER);
logmode = LOGMODE_SYSLOG;
}
//if (opt & 1) // -f //if (opt & 1) // -f
//if (opt & 2) // -l //if (opt & 2) // -l
#ifndef CONFIG_FEATURE_TELNETD_INETD USE_FEATURE_TELNETD_STANDALONE(
if (opt & 4) portnbr = xatou16(opt_portnbr); // -p if (opt & OPT_PORT) // -p
if (opt & 8) // -b portnbr = xatou16(opt_portnbr);
if (inet_aton(opt_bindaddr, &bind_addr) == 0) bb_show_usage(); //if (opt & 8) // -b
#endif /* CONFIG_FEATURE_TELNETD_INETD */ //if (opt & 0x10) // -F
//if (opt & 0x20) // -i
if (access(loginpath, X_OK) < 0) { );
bb_error_msg_and_die("'%s' unavailable", loginpath);
}
/* Used to check access(loginpath, X_OK) here. Pointless.
* exec will do this for us for free later. */
argv_init[0] = loginpath; argv_init[0] = loginpath;
#ifdef CONFIG_FEATURE_TELNETD_INETD #if ENABLE_FEATURE_TELNETD_STANDALONE
maxfd = 1; if (IS_INETD) {
sessions = make_new_session(); sessions = make_new_session(0, 1);
#else /* CONFIG_EATURE_TELNETD_INETD */ } else {
sessions = 0; master_fd = create_socket(portnbr, opt_bindaddr);
if (!(opt & OPT_FOREGROUND))
/* Grab a TCP socket. */ xdaemon(0, 0);
}
master_fd = xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
(void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
/* Set it to listen to specified port. */
memset((void *)&sa, 0, sizeof(sa));
#ifdef CONFIG_FEATURE_IPV6
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(portnbr);
/* sa.sin6_addr = bind_addr6; */
#else #else
sa.sin_family = AF_INET; sessions = make_new_session();
sa.sin_port = htons(portnbr);
sa.sin_addr = bind_addr;
#endif #endif
xbind(master_fd, (struct sockaddr *) &sa, sizeof(sa)); /* We don't want to die if just one session is broken */
xlisten(master_fd, 1); signal(SIGPIPE, SIG_IGN);
xdaemon(0, 0);
maxfd = master_fd; again:
#endif /* CONFIG_FEATURE_TELNETD_INETD */ FD_ZERO(&rdfdset);
FD_ZERO(&wrfdset);
while(1) { if (!IS_INETD) {
struct tsession *ts;
FD_ZERO(&rdfdset);
FD_ZERO(&wrfdset);
/* select on the master socket, all telnet sockets and their
* ptys if there is room in their respective session buffers.
*/
#ifndef CONFIG_FEATURE_TELNETD_INETD
FD_SET(master_fd, &rdfdset); FD_SET(master_fd, &rdfdset);
#endif /* CONFIG_FEATURE_TELNETD_INETD */ /* This is needed because free_session() does not
* take into account master_fd when it finds new
* maxfd among remaining fd's: */
if (master_fd > maxfd)
maxfd = master_fd;
}
ts = sessions; /* select on the master socket, all telnet sockets and their
#ifndef CONFIG_FEATURE_TELNETD_INETD * ptys if there is room in their session buffers. */
while (ts) { ts = sessions;
#endif /* CONFIG_FEATURE_TELNETD_INETD */ while (ts) {
/* buf1 is used from socket to pty /* buf1 is used from socket to pty
* buf2 is used from pty to socket * buf2 is used from pty to socket */
*/ if (ts->size1 > 0) /* can write to pty */
if (ts->size1 > 0) { FD_SET(ts->ptyfd, &wrfdset);
FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */ if (ts->size1 < BUFSIZE) /* can read from socket */
} FD_SET(ts->sockfd_read, &rdfdset);
if (ts->size1 < BUFSIZE) { if (ts->size2 > 0) /* can write to socket */
#ifdef CONFIG_FEATURE_TELNETD_INETD FD_SET(ts->sockfd_write, &wrfdset);
FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */ if (ts->size2 < BUFSIZE) /* can read from pty */
#else /* CONFIG_FEATURE_TELNETD_INETD */ FD_SET(ts->ptyfd, &rdfdset);
FD_SET(ts->sockfd, &rdfdset); /* can read from socket */ ts = ts->next;
#endif /* CONFIG_FEATURE_TELNETD_INETD */ }
}
if (ts->size2 > 0) { selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
#ifdef CONFIG_FEATURE_TELNETD_INETD if (!selret)
FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */ return 0;
#else /* CONFIG_FEATURE_TELNETD_INETD */
FD_SET(ts->sockfd, &wrfdset); /* can write to socket */ #if ENABLE_FEATURE_TELNETD_STANDALONE
#endif /* CONFIG_FEATURE_TELNETD_INETD */ /* First check for and accept new sessions. */
} if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
if (ts->size2 < BUFSIZE) { sockaddr_type sa;
FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */ int fd;
} socklen_t salen;
#ifndef CONFIG_FEATURE_TELNETD_INETD struct tsession *new_ts;
ts = ts->next;
salen = sizeof(sa);
fd = accept(master_fd, (struct sockaddr *)&sa, &salen);
if (fd < 0)
goto again;
/* Create a new session and link it into our active list */
new_ts = make_new_session(fd, fd);
if (new_ts) {
new_ts->next = sessions;
sessions = new_ts;
} else {
close(fd);
} }
#endif /* CONFIG_FEATURE_TELNETD_INETD */ }
#endif
selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); /* Then check for data tunneling. */
ts = sessions;
while (ts) { /* For all sessions... */
struct tsession *next = ts->next; /* in case we free ts. */
if (!selret) if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
break; int num_totty;
char *ptr;
#ifndef CONFIG_FEATURE_TELNETD_INETD /* Write to pty from buffer 1. */
/* First check for and accept new sessions. */ ptr = remove_iacs(ts, &num_totty);
if (FD_ISSET(master_fd, &rdfdset)) { w = safe_write(ts->ptyfd, ptr, num_totty);
int fd; /* needed? if (w < 0 && errno == EAGAIN) continue; */
socklen_t salen; if (w < 0) {
if (IS_INETD)
salen = sizeof(sa); return 0;
fd = accept(master_fd, (struct sockaddr *)&sa, &salen); free_session(ts);
if (fd < 0) { ts = next;
continue; continue;
} else {
/* Create a new session and link it into
our active list. */
struct tsession *new_ts = make_new_session(fd);
if (new_ts) {
new_ts->next = sessions;
sessions = new_ts;
if (fd > maxfd)
maxfd = fd;
} else {
close(fd);
}
} }
} ts->wridx1 += w;
ts->size1 -= w;
/* Then check for data tunneling. */ if (ts->wridx1 == BUFSIZE)
ts = sessions;
while (ts) { /* For all sessions... */
#endif /* CONFIG_FEATURE_TELNETD_INETD */
#ifndef CONFIG_FEATURE_TELNETD_INETD
struct tsession *next = ts->next; /* in case we free ts. */
#endif /* CONFIG_FEATURE_TELNETD_INETD */
if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
int num_totty;
char *ptr;
/* Write to pty from buffer 1. */
ptr = remove_iacs(ts, &num_totty);
w = write(ts->ptyfd, ptr, num_totty);
if (w < 0) {
#ifdef CONFIG_FEATURE_TELNETD_INETD
exit(0);
#else /* CONFIG_FEATURE_TELNETD_INETD */
free_session(ts);
ts = next;
continue;
#endif /* CONFIG_FEATURE_TELNETD_INETD */
}
ts->wridx1 += w;
ts->size1 -= w;
if (ts->wridx1 == BUFSIZE)
ts->wridx1 = 0;
}
#ifdef CONFIG_FEATURE_TELNETD_INETD
if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
#else /* CONFIG_FEATURE_TELNETD_INETD */
if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
#endif /* CONFIG_FEATURE_TELNETD_INETD */
/* Write to socket from buffer 2. */
maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
#ifdef CONFIG_FEATURE_TELNETD_INETD
w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
if (w < 0)
exit(0);
#else /* CONFIG_FEATURE_TELNETD_INETD */
w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
if (w < 0) {
free_session(ts);
ts = next;
continue;
}
#endif /* CONFIG_FEATURE_TELNETD_INETD */
ts->wridx2 += w;
ts->size2 -= w;
if (ts->wridx2 == BUFSIZE)
ts->wridx2 = 0;
}
#ifdef CONFIG_FEATURE_TELNETD_INETD
if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
#else /* CONFIG_FEATURE_TELNETD_INETD */
if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
#endif /* CONFIG_FEATURE_TELNETD_INETD */
/* Read from socket to buffer 1. */
maxlen = MIN(BUFSIZE - ts->rdidx1,
BUFSIZE - ts->size1);
#ifdef CONFIG_FEATURE_TELNETD_INETD
r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
if (!r || (r < 0 && errno != EINTR))
exit(0);
#else /* CONFIG_FEATURE_TELNETD_INETD */
r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
if (!r || (r < 0 && errno != EINTR)) {
free_session(ts);
ts = next;
continue;
}
#endif /* CONFIG_FEATURE_TELNETD_INETD */
if (!*(ts->buf1 + ts->rdidx1 + r - 1)) {
r--;
if (!r)
continue;
}
ts->rdidx1 += r;
ts->size1 += r;
if (ts->rdidx1 == BUFSIZE)
ts->rdidx1 = 0;
}
if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
/* Read from pty to buffer 2. */
maxlen = MIN(BUFSIZE - ts->rdidx2,
BUFSIZE - ts->size2);
r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
if (!r || (r < 0 && errno != EINTR)) {
#ifdef CONFIG_FEATURE_TELNETD_INETD
exit(0);
#else /* CONFIG_FEATURE_TELNETD_INETD */
free_session(ts);
ts = next;
continue;
#endif /* CONFIG_FEATURE_TELNETD_INETD */
}
ts->rdidx2 += r;
ts->size2 += r;
if (ts->rdidx2 == BUFSIZE)
ts->rdidx2 = 0;
}
if (ts->size1 == 0) {
ts->rdidx1 = 0;
ts->wridx1 = 0; ts->wridx1 = 0;
}
if (ts->size2 == 0) {
ts->rdidx2 = 0;
ts->wridx2 = 0;
}
#ifndef CONFIG_FEATURE_TELNETD_INETD
ts = next;
} }
#endif /* CONFIG_FEATURE_TELNETD_INETD */
} /* while(1) */ if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
/* Write to socket from buffer 2. */
maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
/* needed? if (w < 0 && errno == EAGAIN) continue; */
if (w < 0) {
if (IS_INETD)
return 0;
free_session(ts);
ts = next;
continue;
}
ts->wridx2 += w;
ts->size2 -= w;
if (ts->wridx2 == BUFSIZE)
ts->wridx2 = 0;
}
return 0; if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
/* Read from socket to buffer 1. */
maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
if (r < 0 && errno == EAGAIN) continue;
if (r <= 0) {
if (IS_INETD)
return 0;
free_session(ts);
ts = next;
continue;
}
if (!ts->buf1[ts->rdidx1 + r - 1])
if (!--r)
continue;
ts->rdidx1 += r;
ts->size1 += r;
if (ts->rdidx1 == BUFSIZE)
ts->rdidx1 = 0;
}
if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
/* Read from pty to buffer 2. */
maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
if (r < 0 && errno == EAGAIN) continue;
if (r <= 0) {
if (IS_INETD)
return 0;
free_session(ts);
ts = next;
continue;
}
ts->rdidx2 += r;
ts->size2 += r;
if (ts->rdidx2 == BUFSIZE)
ts->rdidx2 = 0;
}
if (ts->size1 == 0) {
ts->rdidx1 = 0;
ts->wridx1 = 0;
}
if (ts->size2 == 0) {
ts->rdidx2 = 0;
ts->wridx2 = 0;
}
ts = next;
}
goto again;
} }

View File

@ -34,10 +34,6 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "libbb.h" #include "libbb.h"
#include "runit_lib.h" #include "runit_lib.h"
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
/*** buffer.c ***/ /*** buffer.c ***/
void buffer_init(buffer *s,int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len) void buffer_init(buffer *s,int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len)
@ -625,22 +621,6 @@ int lock_exnb(int fd)
} }
/*** ndelay_off.c ***/
int ndelay_off(int fd)
{
return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
}
/*** ndelay_on.c ***/
int ndelay_on(int fd)
{
return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
}
/*** open_append.c ***/ /*** open_append.c ***/
int open_append(const char *fn) int open_append(const char *fn)

View File

@ -560,7 +560,6 @@ CONFIG_TELNET=y
CONFIG_FEATURE_TELNET_TTYPE=y CONFIG_FEATURE_TELNET_TTYPE=y
CONFIG_FEATURE_TELNET_AUTOLOGIN=y CONFIG_FEATURE_TELNET_AUTOLOGIN=y
CONFIG_TELNETD=y CONFIG_TELNETD=y
CONFIG_FEATURE_TELNETD_INETD=y
CONFIG_TFTP=y CONFIG_TFTP=y
CONFIG_FEATURE_TFTP_GET=y CONFIG_FEATURE_TFTP_GET=y
CONFIG_FEATURE_TFTP_PUT=y CONFIG_FEATURE_TFTP_PUT=y