-/* $Id: telnetd.c,v 1.7 2003/09/02 02:36:16 bug1 Exp $
+/* $Id: telnetd.c,v 1.13 2004/09/14 17:24:58 bug1 Exp $
*
* Simple telnet server
* Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/wait.h>
+#include <sys/ioctl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#define BUFSIZE 4000
-static const char *loginpath =
+#ifdef CONFIG_FEATURE_IPV6
+#define SOCKET_TYPE AF_INET6
+typedef struct sockaddr_in6 sockaddr_type;
+#else
+#define SOCKET_TYPE AF_INET
+typedef struct sockaddr_in sockaddr_type;
+#endif
+
+
#ifdef CONFIG_LOGIN
-"/bin/login";
+static const char *loginpath = "/bin/login";
#else
-DEFAULT_SHELL;
+static const char *loginpath;
#endif
static const char *issuefile = "/etc/issue.net";
+-------+ wridx1++ +------+ rdidx1++ +----------+
| | <-------------- | buf1 | <-------------- | |
| | size1-- +------+ size1++ | |
- | pty | | socket |
+ | pty | | socket |
| | rdidx2++ +------+ wridx2++ | |
| | --------------> | buf2 | --------------> | |
+-------+ size2++ +------+ size2-- +----------+
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 = ts->buf1 + ts->wridx1;
+ 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);
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 {
- if ((ptr+2) < end) {
- /* the entire IAC is contained in the buffer
- we were asked to process. */
-#ifdef DEBUG
- fprintf(stderr, "Ignoring IAC %s,%s\n",
- *ptr, TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
-#endif
- ptr += 3;
- } 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];
+ (void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
+ ptr += 9;
+ }
+ else {
+ /* skip 3-byte IAC non-SB cmd */
+#ifdef DEBUG
+ fprintf(stderr, "Ignoring IAC %s,%s\n",
+ TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
+#endif
+ ptr += 3;
+ }
}
}
pty = getpty(tty_name);
if (pty < 0) {
- syslog_msg(LOG_USER, LOG_ERR, "All network ports in use!");
+ syslog(LOG_ERR, "All network ports in use!");
return 0;
}
*/
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);
if ((pid = fork()) < 0) {
- syslog_msg(LOG_USER, LOG_ERR, "Can`t forking");
+ syslog(LOG_ERR, "Can`t forking");
}
if (pid == 0) {
/* In child, open the child's side of the tty. */
setsid();
if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
- syslog_msg(LOG_USER, LOG_ERR, "Could not open tty");
+ syslog(LOG_ERR, "Could not open tty");
exit(1);
}
dup(0);
execv(loginpath, (char *const *)argv_init);
/* NOT REACHED */
- syslog_msg(LOG_USER, LOG_ERR, "execv error");
+ syslog(LOG_ERR, "execv error");
exit(1);
}
telnetd_main(int argc, char **argv)
{
#ifndef CONFIG_FEATURE_TELNETD_INETD
- struct sockaddr_in sa;
+ sockaddr_type sa;
int master_fd;
#endif /* CONFIG_FEATURE_TELNETD_INETD */
fd_set rdfdset, wrfdset;
#ifndef CONFIG_FEATURE_TELNETD_INETD
int on = 1;
int portnbr = 23;
+ struct in_addr bind_addr = { .s_addr = 0x0 };
#endif /* CONFIG_FEATURE_TELNETD_INETD */
int c;
static const char options[] =
#ifdef CONFIG_FEATURE_TELNETD_INETD
"f:l:";
#else /* CONFIG_EATURE_TELNETD_INETD */
- "f:l:p:";
+ "f:l:p:b:";
#endif /* CONFIG_FEATURE_TELNETD_INETD */
int maxlen, w, r;
+#ifndef CONFIG_LOGIN
+ loginpath = DEFAULT_SHELL;
+#endif
+
for (;;) {
c = getopt( argc, argv, options);
if (c == EOF) break;
switch (c) {
case 'f':
- issuefile = strdup (optarg);
+ issuefile = optarg;
break;
case 'l':
- loginpath = strdup (optarg);
+ loginpath = optarg;
break;
#ifndef CONFIG_FEATURE_TELNETD_INETD
case 'p':
portnbr = atoi(optarg);
break;
+ case 'b':
+ if (inet_aton(optarg, &bind_addr) == 0)
+ bb_show_usage();
+ break;
#endif /* CONFIG_FEATURE_TELNETD_INETD */
default:
bb_show_usage();
argv_init[0] = loginpath;
+ openlog(bb_applet_name, 0, LOG_USER);
+
#ifdef CONFIG_FEATURE_TELNETD_INETD
maxfd = 1;
sessions = make_new_session();
/* Grab a TCP socket. */
- master_fd = socket(AF_INET, SOCK_STREAM, 0);
+ master_fd = socket(SOCKET_TYPE, SOCK_STREAM, 0);
if (master_fd < 0) {
bb_perror_msg_and_die("socket");
}
/* 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
sa.sin_family = AF_INET;
sa.sin_port = htons(portnbr);
+ sa.sin_addr = bind_addr;
+#endif
if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
bb_perror_msg_and_die("bind");
#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;