aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-10-29 02:12:22 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2010-10-29 02:12:22 +0200
commit036dbb9d9ab239bbd7eecad8580876c9c3e57dc5 (patch)
treec8193d9c5da63a1a58eb34eba1bade3629ecd778
parent6b3f0b0dab46d910f7797dfc8d8554546dd33fa2 (diff)
downloadbusybox-w32-036dbb9d9ab239bbd7eecad8580876c9c3e57dc5.tar.gz
busybox-w32-036dbb9d9ab239bbd7eecad8580876c9c3e57dc5.tar.bz2
busybox-w32-036dbb9d9ab239bbd7eecad8580876c9c3e57dc5.zip
telnet: convert CR NUL -> CR on input. Closes bug 2569
function old new delta telnet_main 1558 1594 +36 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--networking/telnet.c132
1 files 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 {
42 UF_ECHO = 0x01, 42 UF_ECHO = 0x01,
43 UF_SGA = 0x02, 43 UF_SGA = 0x02,
44 44
45 TS_0 = 1, 45 TS_NORMAL = 0,
46 TS_COPY = 1,
46 TS_IAC = 2, 47 TS_IAC = 2,
47 TS_OPT = 3, 48 TS_OPT = 3,
48 TS_SUB1 = 4, 49 TS_SUB1 = 4,
49 TS_SUB2 = 5, 50 TS_SUB2 = 5,
51 TS_CR = 6,
50}; 52};
51 53
52typedef unsigned char byte; 54typedef unsigned char byte;
@@ -83,13 +85,13 @@ struct globals {
83 }; \ 85 }; \
84} while (0) 86} while (0)
85 87
86/* Function prototypes */ 88
87static void rawmode(void); 89static void rawmode(void);
88static void cookmode(void); 90static void cookmode(void);
89static void do_linemode(void); 91static void do_linemode(void);
90static void will_charmode(void); 92static void will_charmode(void);
91static void telopt(byte c); 93static void telopt(byte c);
92static int subneg(byte c); 94static void subneg(byte c);
93 95
94static void iac_flush(void) 96static void iac_flush(void)
95{ 97{
@@ -170,24 +172,24 @@ static void handle_net_output(int len)
170 * So I implemented it. It's really useful for me. I hope that 172 * So I implemented it. It's really useful for me. I hope that
171 * other people will find it interesting too. 173 * other people will find it interesting too.
172 */ 174 */
173 175 byte outbuf[2 * DATABUFSIZE];
174 int i, j;
175 byte *p = (byte*)G.buf; 176 byte *p = (byte*)G.buf;
176 byte outbuf[4*DATABUFSIZE]; 177 int j = 0;
177 178
178 for (i = len, j = 0; i > 0; i--, p++) { 179 for (; len > 0; len--, p++) {
179 if (*p == 0x1d) { 180 byte c = *p;
181 if (c == 0x1d) {
180 con_escape(); 182 con_escape();
181 return; 183 return;
182 } 184 }
183 outbuf[j++] = *p; 185 outbuf[j++] = c;
184 if (*p == 0xff) 186 if (c == IAC)
185 outbuf[j++] = 0xff; 187 outbuf[j++] = c; /* IAC -> IAC IAC */
186 else if (*p == 0x0d) 188 else if (c == '\r')
187 outbuf[j++] = 0x00; 189 outbuf[j++] = '\0'; /* CR -> CR NUL */
188 } 190 }
189 if (j > 0) 191 if (j > 0)
190 write(netfd, outbuf, j); 192 full_write(netfd, outbuf, j);
191} 193}
192 194
193static void handle_net_input(int len) 195static void handle_net_input(int len)
@@ -198,25 +200,44 @@ static void handle_net_input(int len)
198 for (i = 0; i < len; i++) { 200 for (i = 0; i < len; i++) {
199 byte c = G.buf[i]; 201 byte c = G.buf[i];
200 202
201 if (G.telstate == 0) { /* most of the time state == 0 */ 203 if (G.telstate == TS_NORMAL) { /* most typical state */
202 if (c == IAC) { 204 if (c == IAC) {
203 cstart = i; 205 cstart = i;
204 G.telstate = TS_IAC; 206 G.telstate = TS_IAC;
205 } 207 }
208 else if (c == '\r') {
209 cstart = i + 1;
210 G.telstate = TS_CR;
211 }
212 /* No IACs were seen so far, no need to copy
213 * bytes within G.buf: */
206 continue; 214 continue;
207 } 215 }
216
208 switch (G.telstate) { 217 switch (G.telstate) {
209 case TS_0: 218 case TS_CR:
219 /* Prev char was CR. If cur one is NUL, ignore it.
220 * See RFC 1123 section 3.3.1 for discussion of telnet EOL handling.
221 */
222 G.telstate = TS_COPY;
223 if (c == '\0')
224 break;
225 /* else: fall through - need to handle CR IAC ... properly */
226
227 case TS_COPY: /* Prev char was ordinary */
228 /* Similar to NORMAL, but in TS_COPY we need to copy bytes */
210 if (c == IAC) 229 if (c == IAC)
211 G.telstate = TS_IAC; 230 G.telstate = TS_IAC;
212 else 231 else
213 G.buf[cstart++] = c; 232 G.buf[cstart++] = c;
233 if (c == '\r')
234 G.telstate = TS_CR;
214 break; 235 break;
215 236
216 case TS_IAC: 237 case TS_IAC: /* Prev char was IAC */
217 if (c == IAC) { /* IAC IAC -> 0xFF */ 238 if (c == IAC) { /* IAC IAC -> one IAC */
218 G.buf[cstart++] = c; 239 G.buf[cstart++] = c;
219 G.telstate = TS_0; 240 G.telstate = TS_COPY;
220 break; 241 break;
221 } 242 }
222 /* else */ 243 /* else */
@@ -228,34 +249,38 @@ static void handle_net_input(int len)
228 case DONT: 249 case DONT:
229 case WILL: 250 case WILL:
230 case WONT: 251 case WONT:
231 G.telwish = c; 252 G.telwish = c;
232 G.telstate = TS_OPT; 253 G.telstate = TS_OPT;
233 break; 254 break;
255 /* DATA MARK must be added later */
234 default: 256 default:
235 G.telstate = TS_0; /* DATA MARK must be added later */ 257 G.telstate = TS_COPY;
236 } 258 }
237 break; 259 break;
238 case TS_OPT: /* WILL, WONT, DO, DONT */ 260
261 case TS_OPT: /* Prev chars were IAC WILL/WONT/DO/DONT */
239 telopt(c); 262 telopt(c);
240 G.telstate = TS_0; 263 G.telstate = TS_COPY;
241 break; 264 break;
265
242 case TS_SUB1: /* Subnegotiation */ 266 case TS_SUB1: /* Subnegotiation */
243 case TS_SUB2: /* Subnegotiation */ 267 case TS_SUB2: /* Subnegotiation */
244 if (subneg(c)) 268 subneg(c); /* can change G.telstate */
245 G.telstate = TS_0;
246 break; 269 break;
247 } 270 }
248 } 271 }
249 if (G.telstate) { 272
273 if (G.telstate != TS_NORMAL) {
274 /* We had some IACs, or CR */
250 if (G.iaclen) 275 if (G.iaclen)
251 iac_flush(); 276 iac_flush();
252 if (G.telstate == TS_0) 277 if (G.telstate == TS_COPY) /* we aren't in the middle of IAC */
253 G.telstate = 0; 278 G.telstate = TS_NORMAL;
254 len = cstart; 279 len = cstart;
255 } 280 }
256 281
257 if (len) 282 if (len)
258 write(STDOUT_FILENO, G.buf, len); 283 full_write(STDOUT_FILENO, G.buf, len);
259} 284}
260 285
261static void put_iac(int c) 286static void put_iac(int c)
@@ -495,7 +520,7 @@ static void telopt(byte c)
495} 520}
496 521
497/* subnegotiation -- ignore all (except TTYPE,NAWS) */ 522/* subnegotiation -- ignore all (except TTYPE,NAWS) */
498static int subneg(byte c) 523static void subneg(byte c)
499{ 524{
500 switch (G.telstate) { 525 switch (G.telstate) {
501 case TS_SUB1: 526 case TS_SUB1:
@@ -513,12 +538,13 @@ static int subneg(byte c)
513#endif 538#endif
514 break; 539 break;
515 case TS_SUB2: 540 case TS_SUB2:
516 if (c == SE) 541 if (c == SE) {
517 return TRUE; 542 G.telstate = TS_COPY;
543 return;
544 }
518 G.telstate = TS_SUB1; 545 G.telstate = TS_SUB1;
519 /* break; */ 546 break;
520 } 547 }
521 return FALSE;
522} 548}
523 549
524static void rawmode(void) 550static void rawmode(void)
@@ -533,21 +559,13 @@ static void cookmode(void)
533 tcsetattr(0, TCSADRAIN, &G.termios_def); 559 tcsetattr(0, TCSADRAIN, &G.termios_def);
534} 560}
535 561
536/* poll gives smaller (-70 bytes) code */
537#define USE_POLL 1
538
539int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 562int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
540int telnet_main(int argc UNUSED_PARAM, char **argv) 563int telnet_main(int argc UNUSED_PARAM, char **argv)
541{ 564{
542 char *host; 565 char *host;
543 int port; 566 int port;
544 int len; 567 int len;
545#ifdef USE_POLL
546 struct pollfd ufds[2]; 568 struct pollfd ufds[2];
547#else
548 fd_set readfds;
549 int maxfd;
550#endif
551 569
552 INIT_G(); 570 INIT_G();
553 571
@@ -585,25 +603,11 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
585 603
586 signal(SIGINT, record_signo); 604 signal(SIGINT, record_signo);
587 605
588#ifdef USE_POLL
589 ufds[0].fd = 0; ufds[1].fd = netfd; 606 ufds[0].fd = 0; ufds[1].fd = netfd;
590 ufds[0].events = ufds[1].events = POLLIN; 607 ufds[0].events = ufds[1].events = POLLIN;
591#else
592 FD_ZERO(&readfds);
593 FD_SET(STDIN_FILENO, &readfds);
594 FD_SET(netfd, &readfds);
595 maxfd = netfd + 1;
596#endif
597 608
598 while (1) { 609 while (1) {
599#ifndef USE_POLL 610 switch (poll(ufds, 2, -1)) {
600 fd_set rfds = readfds;
601
602 switch (select(maxfd, &rfds, NULL, NULL, NULL))
603#else
604 switch (poll(ufds, 2, -1))
605#endif
606 {
607 case 0: 611 case 0:
608 /* timeout */ 612 /* timeout */
609 case -1: 613 case -1:
@@ -615,12 +619,7 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
615 break; 619 break;
616 default: 620 default:
617 621
618#ifdef USE_POLL 622 if (ufds[0].revents) {
619 if (ufds[0].revents)
620#else
621 if (FD_ISSET(STDIN_FILENO, &rfds))
622#endif
623 {
624 len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE); 623 len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE);
625 if (len <= 0) 624 if (len <= 0)
626 doexit(EXIT_SUCCESS); 625 doexit(EXIT_SUCCESS);
@@ -628,12 +627,7 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
628 handle_net_output(len); 627 handle_net_output(len);
629 } 628 }
630 629
631#ifdef USE_POLL 630 if (ufds[1].revents) {
632 if (ufds[1].revents)
633#else
634 if (FD_ISSET(netfd, &rfds))
635#endif
636 {
637 len = safe_read(netfd, G.buf, DATABUFSIZE); 631 len = safe_read(netfd, G.buf, DATABUFSIZE);
638 if (len <= 0) { 632 if (len <= 0) {
639 full_write1_str("Connection closed by foreign host\r\n"); 633 full_write1_str("Connection closed by foreign host\r\n");