From 036dbb9d9ab239bbd7eecad8580876c9c3e57dc5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 29 Oct 2010 02:12:22 +0200 Subject: telnet: convert CR NUL -> CR on input. Closes bug 2569 function old new delta telnet_main 1558 1594 +36 Signed-off-by: Denys Vlasenko --- networking/telnet.c | 132 +++++++++++++++++++++++++--------------------------- 1 file changed, 63 insertions(+), 69 deletions(-) diff --git a/networking/telnet.c b/networking/telnet.c index 12d1970fa..0cf28f641 100644 --- a/networking/telnet.c +++ b/networking/telnet.c @@ -42,11 +42,13 @@ enum { UF_ECHO = 0x01, UF_SGA = 0x02, - TS_0 = 1, + TS_NORMAL = 0, + TS_COPY = 1, TS_IAC = 2, TS_OPT = 3, TS_SUB1 = 4, TS_SUB2 = 5, + TS_CR = 6, }; typedef unsigned char byte; @@ -83,13 +85,13 @@ struct globals { }; \ } while (0) -/* Function prototypes */ + static void rawmode(void); static void cookmode(void); static void do_linemode(void); static void will_charmode(void); static void telopt(byte c); -static int subneg(byte c); +static void subneg(byte c); static void iac_flush(void) { @@ -170,24 +172,24 @@ static void handle_net_output(int len) * So I implemented it. It's really useful for me. I hope that * other people will find it interesting too. */ - - int i, j; + byte outbuf[2 * DATABUFSIZE]; byte *p = (byte*)G.buf; - byte outbuf[4*DATABUFSIZE]; + int j = 0; - for (i = len, j = 0; i > 0; i--, p++) { - if (*p == 0x1d) { + for (; len > 0; len--, p++) { + byte c = *p; + if (c == 0x1d) { con_escape(); return; } - outbuf[j++] = *p; - if (*p == 0xff) - outbuf[j++] = 0xff; - else if (*p == 0x0d) - outbuf[j++] = 0x00; + outbuf[j++] = c; + if (c == IAC) + outbuf[j++] = c; /* IAC -> IAC IAC */ + else if (c == '\r') + outbuf[j++] = '\0'; /* CR -> CR NUL */ } if (j > 0) - write(netfd, outbuf, j); + full_write(netfd, outbuf, j); } static void handle_net_input(int len) @@ -198,25 +200,44 @@ static void handle_net_input(int len) for (i = 0; i < len; i++) { byte c = G.buf[i]; - if (G.telstate == 0) { /* most of the time state == 0 */ + if (G.telstate == TS_NORMAL) { /* most typical state */ if (c == IAC) { cstart = i; G.telstate = TS_IAC; } + else if (c == '\r') { + cstart = i + 1; + G.telstate = TS_CR; + } + /* No IACs were seen so far, no need to copy + * bytes within G.buf: */ continue; } + switch (G.telstate) { - case TS_0: + case TS_CR: + /* Prev char was CR. If cur one is NUL, ignore it. + * See RFC 1123 section 3.3.1 for discussion of telnet EOL handling. + */ + G.telstate = TS_COPY; + if (c == '\0') + break; + /* else: fall through - need to handle CR IAC ... properly */ + + case TS_COPY: /* Prev char was ordinary */ + /* Similar to NORMAL, but in TS_COPY we need to copy bytes */ if (c == IAC) G.telstate = TS_IAC; else G.buf[cstart++] = c; + if (c == '\r') + G.telstate = TS_CR; break; - case TS_IAC: - if (c == IAC) { /* IAC IAC -> 0xFF */ + case TS_IAC: /* Prev char was IAC */ + if (c == IAC) { /* IAC IAC -> one IAC */ G.buf[cstart++] = c; - G.telstate = TS_0; + G.telstate = TS_COPY; break; } /* else */ @@ -228,34 +249,38 @@ static void handle_net_input(int len) case DONT: case WILL: case WONT: - G.telwish = c; + G.telwish = c; G.telstate = TS_OPT; break; + /* DATA MARK must be added later */ default: - G.telstate = TS_0; /* DATA MARK must be added later */ + G.telstate = TS_COPY; } break; - case TS_OPT: /* WILL, WONT, DO, DONT */ + + case TS_OPT: /* Prev chars were IAC WILL/WONT/DO/DONT */ telopt(c); - G.telstate = TS_0; + G.telstate = TS_COPY; break; + case TS_SUB1: /* Subnegotiation */ case TS_SUB2: /* Subnegotiation */ - if (subneg(c)) - G.telstate = TS_0; + subneg(c); /* can change G.telstate */ break; } } - if (G.telstate) { + + if (G.telstate != TS_NORMAL) { + /* We had some IACs, or CR */ if (G.iaclen) iac_flush(); - if (G.telstate == TS_0) - G.telstate = 0; + if (G.telstate == TS_COPY) /* we aren't in the middle of IAC */ + G.telstate = TS_NORMAL; len = cstart; } if (len) - write(STDOUT_FILENO, G.buf, len); + full_write(STDOUT_FILENO, G.buf, len); } static void put_iac(int c) @@ -495,7 +520,7 @@ static void telopt(byte c) } /* subnegotiation -- ignore all (except TTYPE,NAWS) */ -static int subneg(byte c) +static void subneg(byte c) { switch (G.telstate) { case TS_SUB1: @@ -513,12 +538,13 @@ static int subneg(byte c) #endif break; case TS_SUB2: - if (c == SE) - return TRUE; + if (c == SE) { + G.telstate = TS_COPY; + return; + } G.telstate = TS_SUB1; - /* break; */ + break; } - return FALSE; } static void rawmode(void) @@ -533,21 +559,13 @@ static void cookmode(void) tcsetattr(0, TCSADRAIN, &G.termios_def); } -/* poll gives smaller (-70 bytes) code */ -#define USE_POLL 1 - int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int telnet_main(int argc UNUSED_PARAM, char **argv) { char *host; int port; int len; -#ifdef USE_POLL struct pollfd ufds[2]; -#else - fd_set readfds; - int maxfd; -#endif INIT_G(); @@ -585,25 +603,11 @@ int telnet_main(int argc UNUSED_PARAM, char **argv) signal(SIGINT, record_signo); -#ifdef USE_POLL ufds[0].fd = 0; ufds[1].fd = netfd; ufds[0].events = ufds[1].events = POLLIN; -#else - FD_ZERO(&readfds); - FD_SET(STDIN_FILENO, &readfds); - FD_SET(netfd, &readfds); - maxfd = netfd + 1; -#endif while (1) { -#ifndef USE_POLL - fd_set rfds = readfds; - - switch (select(maxfd, &rfds, NULL, NULL, NULL)) -#else - switch (poll(ufds, 2, -1)) -#endif - { + switch (poll(ufds, 2, -1)) { case 0: /* timeout */ case -1: @@ -615,12 +619,7 @@ int telnet_main(int argc UNUSED_PARAM, char **argv) break; default: -#ifdef USE_POLL - if (ufds[0].revents) -#else - if (FD_ISSET(STDIN_FILENO, &rfds)) -#endif - { + if (ufds[0].revents) { len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE); if (len <= 0) doexit(EXIT_SUCCESS); @@ -628,12 +627,7 @@ int telnet_main(int argc UNUSED_PARAM, char **argv) handle_net_output(len); } -#ifdef USE_POLL - if (ufds[1].revents) -#else - if (FD_ISSET(netfd, &rfds)) -#endif - { + if (ufds[1].revents) { len = safe_read(netfd, G.buf, DATABUFSIZE); if (len <= 0) { full_write1_str("Connection closed by foreign host\r\n"); -- cgit v1.2.3