aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-10-15 17:28:00 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-10-15 17:28:00 +0000
commit10916c5c6b34a9268356ef5447f5de0b20089aa7 (patch)
treed6f27392403c20dae460bdd86177999438c3ae83
parent59d7c43dbe502ef7425f6828bb43528c5adfc9a9 (diff)
downloadbusybox-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.c103
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 */
87static char * 89static unsigned char *
88remove_iacs(struct tsession *ts, int *pnum_totty) 90remove_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 }