diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-10-29 02:12:22 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-10-29 02:12:22 +0200 |
commit | 036dbb9d9ab239bbd7eecad8580876c9c3e57dc5 (patch) | |
tree | c8193d9c5da63a1a58eb34eba1bade3629ecd778 | |
parent | 6b3f0b0dab46d910f7797dfc8d8554546dd33fa2 (diff) | |
download | busybox-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.c | 132 |
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 | ||
52 | typedef unsigned char byte; | 54 | typedef unsigned char byte; |
@@ -83,13 +85,13 @@ struct globals { | |||
83 | }; \ | 85 | }; \ |
84 | } while (0) | 86 | } while (0) |
85 | 87 | ||
86 | /* Function prototypes */ | 88 | |
87 | static void rawmode(void); | 89 | static void rawmode(void); |
88 | static void cookmode(void); | 90 | static void cookmode(void); |
89 | static void do_linemode(void); | 91 | static void do_linemode(void); |
90 | static void will_charmode(void); | 92 | static void will_charmode(void); |
91 | static void telopt(byte c); | 93 | static void telopt(byte c); |
92 | static int subneg(byte c); | 94 | static void subneg(byte c); |
93 | 95 | ||
94 | static void iac_flush(void) | 96 | static 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 | ||
193 | static void handle_net_input(int len) | 195 | static 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 | ||
261 | static void put_iac(int c) | 286 | static 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) */ |
498 | static int subneg(byte c) | 523 | static 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 | ||
524 | static void rawmode(void) | 550 | static 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 | |||
539 | int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 562 | int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
540 | int telnet_main(int argc UNUSED_PARAM, char **argv) | 563 | int 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"); |