[3320] | 1 | From: "Doug Graham" <dgraham@nortel.com>
|
---|
| 2 | Date: 2009-01-22 07:20
|
---|
| 3 |
|
---|
| 4 | Hello,
|
---|
| 5 |
|
---|
| 6 | Busybox's telnetd does not disable local (client-side) flow control
|
---|
| 7 | properly. It does not put the pty into packet mode and then notify the
|
---|
| 8 | client whenever flow control is disabled by an application running under
|
---|
| 9 | its control. The result is that ^S/^Q are not passed through to the
|
---|
| 10 | application, which is painful when the application is an emacs variant.
|
---|
| 11 |
|
---|
| 12 | I suppose that support for this might be considered bloat, but the
|
---|
| 13 | included patch only adds about 200 bytes of text to x86 busybox and 300
|
---|
| 14 | bytes to mipsel busybox. Please consider applying.
|
---|
| 15 |
|
---|
| 16 | =============================
|
---|
| 17 |
|
---|
| 18 | NB: the patch doesn't work as-is because we now have iac_safe_write()
|
---|
| 19 | which quotes IACs on output.
|
---|
| 20 |
|
---|
| 21 | =============================
|
---|
| 22 | Docs:
|
---|
| 23 |
|
---|
| 24 | The following ioctl(2) calls apply only to pseudo terminals:
|
---|
| 25 |
|
---|
| 26 | TIOCSTOP Stops output to a terminal (e.g. like typing ^S). Takes no parameter.
|
---|
| 27 |
|
---|
| 28 | TIOCSTART Restarts output (stopped by TIOCSTOP or by typing ^S). Takes no parameter.
|
---|
| 29 |
|
---|
| 30 | TIOCPKT Enable/disable packet mode. When applied to the master side of a pseudo terminal, each
|
---|
| 31 | subsequent read(2) from the terminal will return data written on the slave part of the pseudo terminal preceded by a
|
---|
| 32 | zero byte (symbolically defined as TIOCPKT_DATA), or a single byte reflecting control status information.
|
---|
| 33 | In the latter case, the byte is an inclusive-or of zero or more of the bits:
|
---|
| 34 |
|
---|
| 35 | TIOCPKT_FLUSHREAD whenever the read queue for the terminal is flushed.
|
---|
| 36 | TIOCPKT_FLUSHWRITE whenever the write queue for the terminal is flushed.
|
---|
| 37 | TIOCPKT_STOP whenever output to the terminal is stopped a la ^S.
|
---|
| 38 | TIOCPKT_START whenever output to the terminal is restarted.
|
---|
| 39 | TIOCPKT_DOSTOP whenever t_stopc is ^S and t_startc is ^Q.
|
---|
| 40 | TIOCPKT_NOSTOP whenever the start and stop characters are not ^S/^Q.
|
---|
| 41 |
|
---|
| 42 | While this mode is in use, the presence of control status information to be read from the master side may be detected
|
---|
| 43 | by a select(2) for exceptional conditions.
|
---|
| 44 |
|
---|
| 45 | This mode is used by rlogin(1) and rlogind(8) to implement a remote-echoed, locally ^S/^Q flow-controlled remote login
|
---|
| 46 | with proper back-flushing of output; it can be used by other similar programs.
|
---|
| 47 |
|
---|
| 48 | TIOCUCNTL Enable/disable a mode that allows a small number of simple user ioctl(2) commands to be passed through
|
---|
| 49 | the pseudo-terminal, using a protocol similar to that of TIOCPKT. The TIOCUCNTL and TIOCPKT modes are mutually
|
---|
| 50 | exclusive. This mode is enabled from the master side of a pseudo terminal. Each subsequent read(2) from the master side
|
---|
| 51 | will return data written on the slave part of the pseudo terminal preceded by a zero byte, or a single byte reflecting a
|
---|
| 52 | user control operation on the slave side. A user control command consists of a special ioctl(2) operation with no data;
|
---|
| 53 | the command is given as UIOCCMD (n), where n is a number in the range 1-255. The operation value n will be received as
|
---|
| 54 | a single byte on the next read(2) from the master side. The ioctl(2) UIOCCMD (0) is a no-op that may be used to probe
|
---|
| 55 | for the existence of this facility. As with TIOCPKT mode, command operations may be detected with a select(2) for
|
---|
| 56 | exceptional conditions.
|
---|
| 57 |
|
---|
| 58 | --- busybox-1.13.2/networking/telnetd.c 2009/01/21 20:02:39 1.1
|
---|
| 59 | +++ busybox-1.13.2/networking/telnetd.c 2009/01/22 00:35:28
|
---|
| 60 | @@ -38,6 +38,9 @@
|
---|
| 61 | int sockfd_read, sockfd_write, ptyfd;
|
---|
| 62 | int shell_pid;
|
---|
| 63 |
|
---|
| 64 | +#ifdef TIOCPKT
|
---|
| 65 | + int flowstate;
|
---|
| 66 | +#endif
|
---|
| 67 | /* two circular buffers */
|
---|
| 68 | /*char *buf1, *buf2;*/
|
---|
| 69 | /*#define TS_BUF1 ts->buf1*/
|
---|
| 70 | @@ -170,6 +173,9 @@
|
---|
| 71 | int fd, pid;
|
---|
| 72 | char tty_name[GETPTY_BUFSIZE];
|
---|
| 73 | struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
|
---|
| 74 | +#ifdef TIOCPKT
|
---|
| 75 | + int on = 1;
|
---|
| 76 | +#endif
|
---|
| 77 |
|
---|
| 78 | /*ts->buf1 = (char *)(ts + 1);*/
|
---|
| 79 | /*ts->buf2 = ts->buf1 + BUFSIZE;*/
|
---|
| 80 | @@ -180,6 +186,10 @@
|
---|
| 81 | maxfd = fd;
|
---|
| 82 | ts->ptyfd = fd;
|
---|
| 83 | ndelay_on(fd);
|
---|
| 84 | +#ifdef TIOCPKT
|
---|
| 85 | + ioctl(fd, TIOCPKT, &on);
|
---|
| 86 | + ts->flowstate = TIOCPKT_DOSTOP;
|
---|
| 87 | +#endif
|
---|
| 88 | #if ENABLE_FEATURE_TELNETD_STANDALONE
|
---|
| 89 | ts->sockfd_read = sock;
|
---|
| 90 | /* SO_KEEPALIVE by popular demand */
|
---|
| 91 | @@ -385,6 +395,16 @@
|
---|
| 92 | portnbr = 23,
|
---|
| 93 | };
|
---|
| 94 | #endif
|
---|
| 95 | +#ifdef TIOCPKT
|
---|
| 96 | + int control;
|
---|
| 97 | + static const char lflow_on[] =
|
---|
| 98 | + {IAC, SB, TELOPT_LFLOW, LFLOW_ON, IAC, SE};
|
---|
| 99 | + static const char lflow_off[] =
|
---|
| 100 | + {IAC, SB, TELOPT_LFLOW, LFLOW_OFF, IAC, SE};
|
---|
| 101 | +# define RESERVED sizeof(lflow_on)
|
---|
| 102 | +#else
|
---|
| 103 | +# define RESERVED 0
|
---|
| 104 | +#endif
|
---|
| 105 | /* Even if !STANDALONE, we accept (and ignore) -i, thus people
|
---|
| 106 | * don't need to guess whether it's ok to pass -i to us */
|
---|
| 107 | opt = getopt32(argv, "f:l:Ki" IF_FEATURE_TELNETD_STANDALONE("p:b:F"),
|
---|
| 108 | @@ -475,7 +495,7 @@
|
---|
| 109 | FD_SET(ts->sockfd_read, &rdfdset);
|
---|
| 110 | if (ts->size2 > 0) /* can write to socket */
|
---|
| 111 | FD_SET(ts->sockfd_write, &wrfdset);
|
---|
| 112 | - if (ts->size2 < BUFSIZE) /* can read from pty */
|
---|
| 113 | + if (ts->size2 < (BUFSIZE - RESERVED)) /* can read from pty */
|
---|
| 114 | FD_SET(ts->ptyfd, &rdfdset);
|
---|
| 115 | }
|
---|
| 116 | ts = next;
|
---|
| 117 | @@ -593,6 +613,52 @@
|
---|
| 118 | goto skip4;
|
---|
| 119 | goto kill_session;
|
---|
| 120 | }
|
---|
| 121 | +#ifdef TIOCPKT
|
---|
| 122 | + control = TS_BUF2[ts->rdidx2];
|
---|
| 123 | + if (--count > 0 && control == TIOCPKT_DATA) {
|
---|
| 124 | + /*
|
---|
| 125 | + * If we are in packet mode, and we have
|
---|
| 126 | + * just read a chunk of actual data from
|
---|
| 127 | + * the pty, then there is the TIOCPKT_DATA
|
---|
| 128 | + * byte (zero) that we have got to remove
|
---|
| 129 | + * somehow. If there were no chars in
|
---|
| 130 | + * TS_BUF2 before we did this read, then
|
---|
| 131 | + * we can optimize by just advancing wridx2.
|
---|
| 132 | + * Otherwise we have to copy the new data down
|
---|
| 133 | + * to close the gap (Could use readv() instead).
|
---|
| 134 | + */
|
---|
| 135 | + if (ts->size2 == 0)
|
---|
| 136 | + ts->wridx2++;
|
---|
| 137 | + else {
|
---|
| 138 | + memmove(TS_BUF2 + ts->rdidx2,
|
---|
| 139 | + TS_BUF2 + ts->rdidx2 + 1, count);
|
---|
| 140 | + }
|
---|
| 141 | + }
|
---|
| 142 | +
|
---|
| 143 | + /*
|
---|
| 144 | + * If the flow control state changed, notify
|
---|
| 145 | + * the client. If "control" is not TIOCPKT_DATA,
|
---|
| 146 | + * then there are no data bytes to worry about.
|
---|
| 147 | + */
|
---|
| 148 | + if ((control & (TIOCPKT_DOSTOP|TIOCPKT_NOSTOP)) != 0
|
---|
| 149 | + && ts->flowstate != (control & TIOCPKT_DOSTOP)) {
|
---|
| 150 | + const char *p = ts->flowstate ? lflow_off : lflow_on;
|
---|
| 151 | +
|
---|
| 152 | + /*
|
---|
| 153 | + * We know we have enough free slots available
|
---|
| 154 | + * (see RESERVED) but they are not necessarily
|
---|
| 155 | + * contiguous; we may have to wrap.
|
---|
| 156 | + */
|
---|
| 157 | + for (count = sizeof(lflow_on); count > 0; count--) {
|
---|
| 158 | + TS_BUF2[ts->rdidx2++] = *p++;
|
---|
| 159 | + if (ts->rdidx2 >= BUFSIZE)
|
---|
| 160 | + ts->rdidx2 = 0;
|
---|
| 161 | + ts->size2++;
|
---|
| 162 | + }
|
---|
| 163 | +
|
---|
| 164 | + ts->flowstate = control & TIOCPKT_DOSTOP;
|
---|
| 165 | + }
|
---|
| 166 | +#endif /* TIOCPKT */
|
---|
| 167 | ts->size2 += count;
|
---|
| 168 | ts->rdidx2 += count;
|
---|
| 169 | if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
|
---|
| 170 |
|
---|
| 171 | --Doug
|
---|
| 172 | _______________________________________________
|
---|
| 173 | busybox mailing list
|
---|
| 174 | busybox@busybox.net
|
---|
| 175 | http://lists.busybox.net/mailman/listinfo/busybox
|
---|