diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-10-15 17:28:00 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-10-15 17:28:00 +0000 |
commit | 10916c5c6b34a9268356ef5447f5de0b20089aa7 (patch) | |
tree | d6f27392403c20dae460bdd86177999438c3ae83 | |
parent | 59d7c43dbe502ef7425f6828bb43528c5adfc9a9 (diff) | |
download | busybox-w32-10916c5c6b34a9268356ef5447f5de0b20089aa7.tar.gz busybox-w32-10916c5c6b34a9268356ef5447f5de0b20089aa7.tar.bz2 busybox-w32-10916c5c6b34a9268356ef5447f5de0b20089aa7.zip |
telnetd: document bug in remove_iacs. reinstate band-aid
which was making it near-impossible to trigger.
remove memmove call which was happening at each network read,
and in 99%+ cases was not needed. Unfortunately, +50 bytes.
-rw-r--r-- | networking/telnetd.c | 103 |
1 files changed, 55 insertions, 48 deletions
diff --git a/networking/telnetd.c b/networking/telnetd.c index 9ce793fb7..85c2ebc44 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c | |||
@@ -43,8 +43,8 @@ struct tsession { | |||
43 | /*char *buf1, *buf2;*/ | 43 | /*char *buf1, *buf2;*/ |
44 | /*#define TS_BUF1 ts->buf1*/ | 44 | /*#define TS_BUF1 ts->buf1*/ |
45 | /*#define TS_BUF2 TS_BUF2*/ | 45 | /*#define TS_BUF2 TS_BUF2*/ |
46 | #define TS_BUF1 ((char*)(ts + 1)) | 46 | #define TS_BUF1 ((unsigned char*)(ts + 1)) |
47 | #define TS_BUF2 (((char*)(ts + 1)) + BUFSIZE) | 47 | #define TS_BUF2 (((unsigned char*)(ts + 1)) + BUFSIZE) |
48 | int rdidx1, wridx1, size1; | 48 | int rdidx1, wridx1, size1; |
49 | int rdidx2, wridx2, size2; | 49 | int rdidx2, wridx2, size2; |
50 | }; | 50 | }; |
@@ -82,27 +82,30 @@ static const char *issuefile = "/etc/issue.net"; | |||
82 | FIXME - if we mean to send 0xFF to the terminal then it will be escaped, | 82 | FIXME - if we mean to send 0xFF to the terminal then it will be escaped, |
83 | what is the escape character? We aren't handling that situation here. | 83 | what is the escape character? We aren't handling that situation here. |
84 | 84 | ||
85 | CR-LF ->'s CR mapping is also done here, for convenience | 85 | CR-LF ->'s CR mapping is also done here, for convenience. |
86 | |||
87 | NB: may fail to remove iacs which wrap around buffer! | ||
86 | */ | 88 | */ |
87 | static char * | 89 | static unsigned char * |
88 | remove_iacs(struct tsession *ts, int *pnum_totty) | 90 | remove_iacs(struct tsession *ts, int *pnum_totty) |
89 | { | 91 | { |
90 | unsigned char *ptr0 = (unsigned char *)TS_BUF1 + ts->wridx1; | 92 | unsigned char *ptr0 = TS_BUF1 + ts->wridx1; |
91 | unsigned char *ptr = ptr0; | 93 | unsigned char *ptr = ptr0; |
92 | unsigned char *totty = ptr; | 94 | unsigned char *totty = ptr; |
93 | unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1); | 95 | unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1); |
94 | int processed; | ||
95 | int num_totty; | 96 | int num_totty; |
96 | 97 | ||
97 | while (ptr < end) { | 98 | while (ptr < end) { |
98 | if (*ptr != IAC) { | 99 | if (*ptr != IAC) { |
99 | int c = *ptr; | 100 | char c = *ptr; |
100 | *totty++ = *ptr++; | 101 | |
102 | *totty++ = c; | ||
103 | ptr++; | ||
101 | /* We now map \r\n ==> \r for pragmatic reasons. | 104 | /* We now map \r\n ==> \r for pragmatic reasons. |
102 | * Many client implementations send \r\n when | 105 | * Many client implementations send \r\n when |
103 | * the user hits the CarriageReturn key. | 106 | * the user hits the CarriageReturn key. |
104 | */ | 107 | */ |
105 | if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end) | 108 | if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0')) |
106 | ptr++; | 109 | ptr++; |
107 | } else { | 110 | } else { |
108 | /* | 111 | /* |
@@ -120,6 +123,7 @@ remove_iacs(struct tsession *ts, int *pnum_totty) | |||
120 | */ | 123 | */ |
121 | else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) { | 124 | else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) { |
122 | struct winsize ws; | 125 | struct winsize ws; |
126 | |||
123 | if ((ptr+8) >= end) | 127 | if ((ptr+8) >= end) |
124 | break; /* incomplete, can't process */ | 128 | break; /* incomplete, can't process */ |
125 | ws.ws_col = (ptr[3] << 8) | ptr[4]; | 129 | ws.ws_col = (ptr[3] << 8) | ptr[4]; |
@@ -137,16 +141,15 @@ remove_iacs(struct tsession *ts, int *pnum_totty) | |||
137 | } | 141 | } |
138 | } | 142 | } |
139 | 143 | ||
140 | processed = ptr - ptr0; | 144 | num_totty = totty - ptr0; |
141 | num_totty = totty - ptr0; | ||
142 | /* the difference between processed and num_to tty | ||
143 | is all the iacs we removed from the stream. | ||
144 | Adjust buf1 accordingly. */ | ||
145 | ts->wridx1 += processed - num_totty; | ||
146 | ts->size1 -= processed - num_totty; | ||
147 | *pnum_totty = num_totty; | 145 | *pnum_totty = num_totty; |
148 | /* move the chars meant for the terminal towards the end of the | 146 | /* the difference between ptr and totty is number of iacs |
149 | buffer. */ | 147 | we removed from the stream. Adjust buf1 accordingly. */ |
148 | if ((ptr - totty) == 0) /* 99.999% of cases */ | ||
149 | return ptr0; | ||
150 | ts->wridx1 += ptr - totty; | ||
151 | ts->size1 -= ptr - totty; | ||
152 | /* move chars meant for the terminal towards the end of the buffer */ | ||
150 | return memmove(ptr - num_totty, ptr0, num_totty); | 153 | return memmove(ptr - num_totty, ptr0, num_totty); |
151 | } | 154 | } |
152 | 155 | ||
@@ -360,7 +363,7 @@ int telnetd_main(int argc, char **argv) | |||
360 | { | 363 | { |
361 | fd_set rdfdset, wrfdset; | 364 | fd_set rdfdset, wrfdset; |
362 | unsigned opt; | 365 | unsigned opt; |
363 | int selret, maxlen, w, r; | 366 | int count; |
364 | struct tsession *ts; | 367 | struct tsession *ts; |
365 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 368 | #if ENABLE_FEATURE_TELNETD_STANDALONE |
366 | #define IS_INETD (opt & OPT_INETD) | 369 | #define IS_INETD (opt & OPT_INETD) |
@@ -473,8 +476,8 @@ int telnetd_main(int argc, char **argv) | |||
473 | ts = ts->next; | 476 | ts = ts->next; |
474 | } | 477 | } |
475 | 478 | ||
476 | selret = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL); | 479 | count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL); |
477 | if (selret < 0) | 480 | if (count < 0) |
478 | goto again; /* EINTR or ENOMEM */ | 481 | goto again; /* EINTR or ENOMEM */ |
479 | 482 | ||
480 | #if ENABLE_FEATURE_TELNETD_STANDALONE | 483 | #if ENABLE_FEATURE_TELNETD_STANDALONE |
@@ -504,11 +507,11 @@ int telnetd_main(int argc, char **argv) | |||
504 | 507 | ||
505 | if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) { | 508 | if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) { |
506 | int num_totty; | 509 | int num_totty; |
507 | char *ptr; | 510 | unsigned char *ptr; |
508 | /* Write to pty from buffer 1. */ | 511 | /* Write to pty from buffer 1. */ |
509 | ptr = remove_iacs(ts, &num_totty); | 512 | ptr = remove_iacs(ts, &num_totty); |
510 | w = safe_write(ts->ptyfd, ptr, num_totty); | 513 | count = safe_write(ts->ptyfd, ptr, num_totty); |
511 | if (w < 0) { | 514 | if (count < 0) { |
512 | if (errno == EAGAIN) | 515 | if (errno == EAGAIN) |
513 | goto skip1; | 516 | goto skip1; |
514 | if (IS_INETD) | 517 | if (IS_INETD) |
@@ -517,17 +520,17 @@ int telnetd_main(int argc, char **argv) | |||
517 | ts = next; | 520 | ts = next; |
518 | continue; | 521 | continue; |
519 | } | 522 | } |
520 | ts->size1 -= w; | 523 | ts->size1 -= count; |
521 | ts->wridx1 += w; | 524 | ts->wridx1 += count; |
522 | if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */ | 525 | if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */ |
523 | ts->wridx1 = 0; | 526 | ts->wridx1 = 0; |
524 | } | 527 | } |
525 | skip1: | 528 | skip1: |
526 | if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) { | 529 | if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) { |
527 | /* Write to socket from buffer 2. */ | 530 | /* Write to socket from buffer 2. */ |
528 | maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2); | 531 | count = MIN(BUFSIZE - ts->wridx2, ts->size2); |
529 | w = safe_write(ts->sockfd_write, TS_BUF2 + ts->wridx2, maxlen); | 532 | count = safe_write(ts->sockfd_write, TS_BUF2 + ts->wridx2, count); |
530 | if (w < 0) { | 533 | if (count < 0) { |
531 | if (errno == EAGAIN) | 534 | if (errno == EAGAIN) |
532 | goto skip2; | 535 | goto skip2; |
533 | if (IS_INETD) | 536 | if (IS_INETD) |
@@ -536,14 +539,18 @@ int telnetd_main(int argc, char **argv) | |||
536 | ts = next; | 539 | ts = next; |
537 | continue; | 540 | continue; |
538 | } | 541 | } |
539 | ts->size2 -= w; | 542 | ts->size2 -= count; |
540 | ts->wridx2 += w; | 543 | ts->wridx2 += count; |
541 | if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */ | 544 | if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */ |
542 | ts->wridx2 = 0; | 545 | ts->wridx2 = 0; |
543 | } | 546 | } |
544 | skip2: | 547 | skip2: |
545 | #if 0 | 548 | /* Should not be needed, but... remove_iacs is actually buggy |
546 | /* Not strictly needed, but allows for bigger reads in common case */ | 549 | * (it cannot process iacs which wrap around buffer's end)! |
550 | * Since properly fixing it requires writing bigger code, | ||
551 | * we will rely instead on this code making it virtually impossible | ||
552 | * to have wrapped iac (people don't type at 2k/second). | ||
553 | * It also allows for bigger reads in common case. */ | ||
547 | if (ts->size1 == 0) { | 554 | if (ts->size1 == 0) { |
548 | ts->rdidx1 = 0; | 555 | ts->rdidx1 = 0; |
549 | ts->wridx1 = 0; | 556 | ts->wridx1 = 0; |
@@ -552,13 +559,13 @@ int telnetd_main(int argc, char **argv) | |||
552 | ts->rdidx2 = 0; | 559 | ts->rdidx2 = 0; |
553 | ts->wridx2 = 0; | 560 | ts->wridx2 = 0; |
554 | } | 561 | } |
555 | #endif | 562 | |
556 | if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) { | 563 | if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) { |
557 | /* Read from socket to buffer 1. */ | 564 | /* Read from socket to buffer 1. */ |
558 | maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); | 565 | count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1); |
559 | r = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, maxlen); | 566 | count = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, count); |
560 | if (r <= 0) { | 567 | if (count <= 0) { |
561 | if (r < 0 && errno == EAGAIN) | 568 | if (count < 0 && errno == EAGAIN) |
562 | goto skip3; | 569 | goto skip3; |
563 | if (IS_INETD) | 570 | if (IS_INETD) |
564 | return 0; | 571 | return 0; |
@@ -567,22 +574,22 @@ int telnetd_main(int argc, char **argv) | |||
567 | continue; | 574 | continue; |
568 | } | 575 | } |
569 | /* Ignore trailing NUL if it is there */ | 576 | /* Ignore trailing NUL if it is there */ |
570 | if (!TS_BUF1[ts->rdidx1 + r - 1]) { | 577 | if (!TS_BUF1[ts->rdidx1 + count - 1]) { |
571 | if (!--r) | 578 | if (!--count) |
572 | goto skip3; | 579 | goto skip3; |
573 | } | 580 | } |
574 | ts->size1 += r; | 581 | ts->size1 += count; |
575 | ts->rdidx1 += r; | 582 | ts->rdidx1 += count; |
576 | if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */ | 583 | if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */ |
577 | ts->rdidx1 = 0; | 584 | ts->rdidx1 = 0; |
578 | } | 585 | } |
579 | skip3: | 586 | skip3: |
580 | if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) { | 587 | if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) { |
581 | /* Read from pty to buffer 2. */ | 588 | /* Read from pty to buffer 2. */ |
582 | maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); | 589 | count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2); |
583 | r = safe_read(ts->ptyfd, TS_BUF2 + ts->rdidx2, maxlen); | 590 | count = safe_read(ts->ptyfd, TS_BUF2 + ts->rdidx2, count); |
584 | if (r <= 0) { | 591 | if (count <= 0) { |
585 | if (r < 0 && errno == EAGAIN) | 592 | if (count < 0 && errno == EAGAIN) |
586 | goto skip4; | 593 | goto skip4; |
587 | if (IS_INETD) | 594 | if (IS_INETD) |
588 | return 0; | 595 | return 0; |
@@ -590,8 +597,8 @@ int telnetd_main(int argc, char **argv) | |||
590 | ts = next; | 597 | ts = next; |
591 | continue; | 598 | continue; |
592 | } | 599 | } |
593 | ts->size2 += r; | 600 | ts->size2 += count; |
594 | ts->rdidx2 += r; | 601 | ts->rdidx2 += count; |
595 | if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */ | 602 | if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */ |
596 | ts->rdidx2 = 0; | 603 | ts->rdidx2 = 0; |
597 | } | 604 | } |