From 5e1a84ab74d5e97582427f016f291a8c11e66f99 Mon Sep 17 00:00:00 2001 From: jutteau Date: Fri, 11 May 2007 18:10:19 +0000 Subject: Completion du script de mise à jour de la pc-104 : * Ajout des sources de busybox dans ./conf/busybox/ * Ajout d'un fichier réclamé par les script dans ./conf/busybox.links --- i/pc104/initrd/conf/busybox/networking/telnetd.c | 573 +++++++++++++++++++++++ 1 file changed, 573 insertions(+) create mode 100644 i/pc104/initrd/conf/busybox/networking/telnetd.c (limited to 'i/pc104/initrd/conf/busybox/networking/telnetd.c') diff --git a/i/pc104/initrd/conf/busybox/networking/telnetd.c b/i/pc104/initrd/conf/busybox/networking/telnetd.c new file mode 100644 index 0000000..ef9b1ac --- /dev/null +++ b/i/pc104/initrd/conf/busybox/networking/telnetd.c @@ -0,0 +1,573 @@ +/* vi: set sw=4 ts=4: */ +/* + * Simple telnet server + * Bjorn Wesen, Axis Communications AB (bjornw@axis.com) + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * --------------------------------------------------------------------------- + * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN + **************************************************************************** + * + * The telnetd manpage says it all: + * + * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for + * a client, then creating a login process which has the slave side of the + * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the + * master side of the pseudo-terminal, implementing the telnet protocol and + * passing characters between the remote client and the login process. + * + * Vladimir Oleynik 2001 + * Set process group corrections, initial busybox port + */ + +/*#define DEBUG 1 */ +#define DEBUG 0 + +#include "busybox.h" + +#if DEBUG +#define TELCMDS +#define TELOPTS +#endif +#include +#include + + +#define BUFSIZE 4000 + +#if ENABLE_LOGIN +static const char *loginpath = "/bin/login"; +#else +static const char *loginpath = DEFAULT_SHELL; +#endif + +static const char *issuefile = "/etc/issue.net"; + +/* shell name and arguments */ + +static const char *argv_init[2]; + +/* structure that describes a session */ + +struct tsession { + struct tsession *next; + int sockfd_read, sockfd_write, ptyfd; + int shell_pid; + /* two circular buffers */ + char *buf1, *buf2; + int rdidx1, wridx1, size1; + int rdidx2, wridx2, size2; +}; + +/* + This is how the buffers are used. The arrows indicate the movement + of data. + + +-------+ wridx1++ +------+ rdidx1++ +----------+ + | | <-------------- | buf1 | <-------------- | | + | | size1-- +------+ size1++ | | + | pty | | socket | + | | rdidx2++ +------+ wridx2++ | | + | | --------------> | buf2 | --------------> | | + +-------+ size2++ +------+ size2-- +----------+ + + Each session has got two buffers. +*/ + +static int maxfd; + +static struct tsession *sessions; + + +/* + 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 + uninterrupted string of characters fit for the terminal. Do this by packing + all characters meant for the terminal sequentially towards the end of bf. + + Return a pointer to the beginning of the characters meant for the terminal. + and make *num_totty the number of characters that should be sent to + the terminal. + + Note - If an IAC (3 byte quantity) starts before (bf + len) but extends + past (bf + len) then that IAC will be left unprocessed and *processed will be + less than len. + + FIXME - if we mean to send 0xFF to the terminal then it will be escaped, + what is the escape character? We aren't handling that situation here. + + CR-LF ->'s CR mapping is also done here, for convenience + */ +static char * +remove_iacs(struct tsession *ts, int *pnum_totty) +{ + unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1; + unsigned char *ptr = ptr0; + unsigned char *totty = ptr; + unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1); + int processed; + int num_totty; + + while (ptr < end) { + if (*ptr != IAC) { + int c = *ptr; + *totty++ = *ptr++; + /* We now map \r\n ==> \r for pragmatic reasons. + * Many client implementations send \r\n when + * the user hits the CarriageReturn key. + */ + if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end) + ptr++; + } else { + /* + * TELOPT_NAWS support! + */ + if ((ptr+2) >= end) { + /* only the beginning of the IAC is in the + buffer we were asked to process, we can't + process this char. */ + break; + } + + /* + * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE + */ + else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) { + struct winsize ws; + if ((ptr+8) >= end) + break; /* incomplete, can't process */ + ws.ws_col = (ptr[3] << 8) | ptr[4]; + ws.ws_row = (ptr[5] << 8) | ptr[6]; + ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws); + ptr += 9; + } else { + /* skip 3-byte IAC non-SB cmd */ +#if DEBUG + fprintf(stderr, "Ignoring IAC %s,%s\n", + TELCMD(ptr[1]), TELOPT(ptr[2])); +#endif + ptr += 3; + } + } + } + + processed = ptr - ptr0; + num_totty = totty - ptr0; + /* the difference between processed and num_to tty + is all the iacs we removed from the stream. + Adjust buf1 accordingly. */ + ts->wridx1 += processed - num_totty; + ts->size1 -= processed - num_totty; + *pnum_totty = num_totty; + /* move the chars meant for the terminal towards the end of the + buffer. */ + return memmove(ptr - num_totty, ptr0, num_totty); +} + + +static int +getpty(char *line, int size) +{ + int p; +#if ENABLE_FEATURE_DEVPTS + p = open("/dev/ptmx", O_RDWR); + if (p > 0) { + const char *name; + grantpt(p); + unlockpt(p); + name = ptsname(p); + if (!name) { + bb_perror_msg("ptsname error (is /dev/pts mounted?)"); + return -1; + } + safe_strncpy(line, name, size); + return p; + } +#else + struct stat stb; + int i; + int j; + + strcpy(line, "/dev/ptyXX"); + + for (i = 0; i < 16; i++) { + line[8] = "pqrstuvwxyzabcde"[i]; + line[9] = '0'; + if (stat(line, &stb) < 0) { + continue; + } + for (j = 0; j < 16; j++) { + line[9] = j < 10 ? j + '0' : j - 10 + 'a'; + if (DEBUG) + fprintf(stderr, "Trying to open device: %s\n", line); + p = open(line, O_RDWR | O_NOCTTY); + if (p >= 0) { + line[5] = 't'; + return p; + } + } + } +#endif /* FEATURE_DEVPTS */ + return -1; +} + + +static void +send_iac(struct tsession *ts, unsigned char command, int option) +{ + /* We rely on that there is space in the buffer for now. */ + char *b = ts->buf2 + ts->rdidx2; + *b++ = IAC; + *b++ = command; + *b++ = option; + ts->rdidx2 += 3; + ts->size2 += 3; +} + + +static struct tsession * +make_new_session( + USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w) + SKIP_FEATURE_TELNETD_STANDALONE(void) +) { + struct termios termbuf; + int fd, pid; + char tty_name[32]; + struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2); + + ts->buf1 = (char *)(&ts[1]); + ts->buf2 = ts->buf1 + BUFSIZE; + + /* Got a new connection, set up a tty. */ + fd = getpty(tty_name, 32); + if (fd < 0) { + bb_error_msg("all terminals in use"); + return NULL; + } + if (fd > maxfd) maxfd = fd; + ndelay_on(ts->ptyfd = fd); +#if ENABLE_FEATURE_TELNETD_STANDALONE + if (sock_w > maxfd) maxfd = sock_w; + 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 + * 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 + * stuff that requires char-by-char support. */ + send_iac(ts, DO, TELOPT_ECHO); + send_iac(ts, DO, TELOPT_NAWS); + send_iac(ts, DO, TELOPT_LFLOW); + send_iac(ts, WILL, TELOPT_ECHO); + send_iac(ts, WILL, TELOPT_SGA); + + pid = fork(); + if (pid < 0) { + free(ts); + close(fd); + bb_perror_msg("fork"); + return NULL; + } + if (pid > 0) { + /* parent */ + ts->shell_pid = pid; + return ts; + } + + /* child */ + + /* make new process group */ + setsid(); + tcsetpgrp(0, getpid()); + /* ^^^ strace says: "ioctl(0, TIOCSPGRP, [pid]) = -1 ENOTTY" -- ??! */ + + /* open the child's side of the tty. */ + /* NB: setsid() disconnects from any previous ctty's. Therefore + * we must open child's side of the tty AFTER setsid! */ + fd = xopen(tty_name, O_RDWR); /* becomes our ctty */ + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + while (fd > 2) close(fd--); + + /* 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"); +} + +#if ENABLE_FEATURE_TELNETD_STANDALONE + +static void +free_session(struct tsession *ts) +{ + struct tsession *t = sessions; + + /* unlink this telnet session from the session list */ + if (t == ts) + sessions = ts->next; + else { + while (t->next != ts) + t = t->next; + t->next = ts->next; + } + + kill(ts->shell_pid, SIGKILL); + wait4(ts->shell_pid, NULL, 0, NULL); + close(ts->ptyfd); + close(ts->sockfd_read); + /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */ + close(ts->sockfd_write); + 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; + } +} + +#else /* !FEATURE_TELNETD_STANDALONE */ + +/* Never actually called */ +void free_session(struct tsession *ts); + +#endif + + +int telnetd_main(int argc, char **argv); +int telnetd_main(int argc, char **argv) +{ + fd_set rdfdset, wrfdset; + unsigned opt; + int selret, maxlen, w, r; + struct tsession *ts; +#if ENABLE_FEATURE_TELNETD_STANDALONE +#define IS_INETD (opt & OPT_INETD) + int master_fd = -1; /* be happy, gcc */ + unsigned portnbr = 23; + char *opt_bindaddr = NULL; + char *opt_portnbr; +#else + enum { + IS_INETD = 1, + master_fd = -1, + portnbr = 23, + }; +#endif + enum { + OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE, + OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE, + OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE, + }; + + opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"), + &issuefile, &loginpath + 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 & 2) // -l + USE_FEATURE_TELNETD_STANDALONE( + if (opt & OPT_PORT) // -p + portnbr = xatou16(opt_portnbr); + //if (opt & 8) // -b + //if (opt & 0x10) // -F + //if (opt & 0x20) // -i + ); + + /* Used to check access(loginpath, X_OK) here. Pointless. + * exec will do this for us for free later. */ + argv_init[0] = loginpath; + +#if ENABLE_FEATURE_TELNETD_STANDALONE + if (IS_INETD) { + sessions = make_new_session(0, 1); + } else { + master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); + xlisten(master_fd, 1); + if (!(opt & OPT_FOREGROUND)) + xdaemon(0, 0); + } +#else + sessions = make_new_session(); +#endif + + /* We don't want to die if just one session is broken */ + signal(SIGPIPE, SIG_IGN); + + again: + FD_ZERO(&rdfdset); + FD_ZERO(&wrfdset); + if (!IS_INETD) { + FD_SET(master_fd, &rdfdset); + /* 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; + } + + /* select on the master socket, all telnet sockets and their + * ptys if there is room in their session buffers. */ + ts = sessions; + while (ts) { + /* buf1 is used from socket to pty + * buf2 is used from pty to socket */ + if (ts->size1 > 0) /* can write to pty */ + FD_SET(ts->ptyfd, &wrfdset); + if (ts->size1 < BUFSIZE) /* can read from socket */ + FD_SET(ts->sockfd_read, &rdfdset); + if (ts->size2 > 0) /* can write to socket */ + FD_SET(ts->sockfd_write, &wrfdset); + if (ts->size2 < BUFSIZE) /* can read from pty */ + FD_SET(ts->ptyfd, &rdfdset); + ts = ts->next; + } + + selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); + if (!selret) + return 0; + +#if ENABLE_FEATURE_TELNETD_STANDALONE + /* First check for and accept new sessions. */ + if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) { + int fd; + struct tsession *new_ts; + + fd = accept(master_fd, NULL, 0); + 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 + + /* Then check for data tunneling. */ + ts = sessions; + while (ts) { /* For all sessions... */ + struct tsession *next = ts->next; /* in case we free ts. */ + + 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 = safe_write(ts->ptyfd, ptr, num_totty); + /* needed? if (w < 0 && errno == EAGAIN) continue; */ + if (w < 0) { + if (IS_INETD) + return 0; + free_session(ts); + ts = next; + continue; + } + ts->wridx1 += w; + ts->size1 -= w; + if (ts->wridx1 == BUFSIZE) + ts->wridx1 = 0; + } + + 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; + } + + 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; +} -- cgit v1.2.3