telnetd: do not advertise TELNET_LFLOW, we do not support it properly.
No binary (build failed)
—
(log yok)
| @@ -35,8 +35,8 @@ | |
| /* Structure that describes a session */ | |
| struct tsession { | |
| struct tsession *next; | |
| + | pid_t shell_pid; |
| int sockfd_read, sockfd_write, ptyfd; | |
| - | int shell_pid; |
| /* two circular buffers */ | |
| /*char *buf1, *buf2;*/ | |
| @@ -247,7 +247,8 @@ make_new_session( | |
| static const char iacs_to_send[] ALIGN1 = { | |
| IAC, DO, TELOPT_ECHO, | |
| IAC, DO, TELOPT_NAWS, | |
| - | IAC, DO, TELOPT_LFLOW, |
| + | /* This requires telnetd.ctrlSQ.patch (incomplete) */ |
| + | /* IAC, DO, TELOPT_LFLOW, */ |
| IAC, WILL, TELOPT_ECHO, | |
| IAC, WILL, TELOPT_SGA | |
| }; | |
| @@ -284,13 +285,13 @@ make_new_session( | |
| /* Child */ | |
| /* Careful - we are after vfork! */ | |
| - | /* make new session and process group */ |
| - | setsid(); |
| - | |
| - | /* Restore default signal handling */ |
| + | /* Restore default signal handling ASAP */ |
| bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL); | |
| - | /* open the child's side of the tty. */ |
| + | /* Make new session and process group */ |
| + | setsid(); |
| + | |
| + | /* Open the child's side of the tty. */ |
| /* NB: setsid() disconnects from any previous ctty's. Therefore | |
| * we must open child's side of the tty AFTER setsid! */ | |
| close(0); | |
| @@ -360,7 +361,7 @@ free_session(struct tsession *ts) | |
| * doesn't send SIGKILL. When we close ptyfd, | |
| * kernel sends SIGHUP to processes having slave side opened. */ | |
| kill(ts->shell_pid, SIGKILL); | |
| - | wait4(ts->shell_pid, NULL, 0, NULL); |
| + | waitpid(ts->shell_pid, NULL, 0); |
| #endif | |
| close(ts->ptyfd); | |
| close(ts->sockfd_read); | |
| @@ -0,0 +1,175 @@ | |
| + | From: "Doug Graham" <[email protected]> |
| + | Date: 2009-01-22 07:20 |
| + | |
| + | Hello, |
| + | |
| + | Busybox's telnetd does not disable local (client-side) flow control |
| + | properly. It does not put the pty into packet mode and then notify the |
| + | client whenever flow control is disabled by an application running under |
| + | its control. The result is that ^S/^Q are not passed through to the |
| + | application, which is painful when the application is an emacs variant. |
| + | |
| + | I suppose that support for this might be considered bloat, but the |
| + | included patch only adds about 200 bytes of text to x86 busybox and 300 |
| + | bytes to mipsel busybox. Please consider applying. |
| + | |
| + | ============================= |
| + | |
| + | NB: the patch doesn't work as-is because we now have iac_safe_write() |
| + | which quotes IACs on output. |
| + | |
| + | ============================= |
| + | Docs: |
| + | |
| + | The following ioctl(2) calls apply only to pseudo terminals: |
| + | |
| + | TIOCSTOP Stops output to a terminal (e.g. like typing ^S). Takes no parameter. |
| + | |
| + | TIOCSTART Restarts output (stopped by TIOCSTOP or by typing ^S). Takes no parameter. |
| + | |
| + | TIOCPKT Enable/disable packet mode. When applied to the master side of a pseudo terminal, each |
| + | subsequent read(2) from the terminal will return data written on the slave part of the pseudo terminal preceded by a |
| + | zero byte (symbolically defined as TIOCPKT_DATA), or a single byte reflecting control status information. |
| + | In the latter case, the byte is an inclusive-or of zero or more of the bits: |
| + | |
| + | TIOCPKT_FLUSHREAD whenever the read queue for the terminal is flushed. |
| + | TIOCPKT_FLUSHWRITE whenever the write queue for the terminal is flushed. |
| + | TIOCPKT_STOP whenever output to the terminal is stopped a la ^S. |
| + | TIOCPKT_START whenever output to the terminal is restarted. |
| + | TIOCPKT_DOSTOP whenever t_stopc is ^S and t_startc is ^Q. |
| + | TIOCPKT_NOSTOP whenever the start and stop characters are not ^S/^Q. |
| + | |
| + | While this mode is in use, the presence of control status information to be read from the master side may be detected |
| + | by a select(2) for exceptional conditions. |
| + | |
| + | This mode is used by rlogin(1) and rlogind(8) to implement a remote-echoed, locally ^S/^Q flow-controlled remote login |
| + | with proper back-flushing of output; it can be used by other similar programs. |
| + | |
| + | TIOCUCNTL Enable/disable a mode that allows a small number of simple user ioctl(2) commands to be passed through |
| + | the pseudo-terminal, using a protocol similar to that of TIOCPKT. The TIOCUCNTL and TIOCPKT modes are mutually |
| + | exclusive. This mode is enabled from the master side of a pseudo terminal. Each subsequent read(2) from the master side |
| + | will return data written on the slave part of the pseudo terminal preceded by a zero byte, or a single byte reflecting a |
| + | user control operation on the slave side. A user control command consists of a special ioctl(2) operation with no data; |
| + | 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 |
| + | 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 |
| + | for the existence of this facility. As with TIOCPKT mode, command operations may be detected with a select(2) for |
| + | exceptional conditions. |
| + | |
| + | --- busybox-1.13.2/networking/telnetd.c 2009/01/21 20:02:39 1.1 |
| + | +++ busybox-1.13.2/networking/telnetd.c 2009/01/22 00:35:28 |
| + | @@ -38,6 +38,9 @@ |
| + | int sockfd_read, sockfd_write, ptyfd; |
| + | int shell_pid; |
| + | |
| + | +#ifdef TIOCPKT |
| + | + int flowstate; |
| + | +#endif |
| + | /* two circular buffers */ |
| + | /*char *buf1, *buf2;*/ |
| + | /*#define TS_BUF1 ts->buf1*/ |
| + | @@ -170,6 +173,9 @@ |
| + | int fd, pid; |
| + | char tty_name[GETPTY_BUFSIZE]; |
| + | struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2); |
| + | +#ifdef TIOCPKT |
| + | + int on = 1; |
| + | +#endif |
| + | |
| + | /*ts->buf1 = (char *)(ts + 1);*/ |
| + | /*ts->buf2 = ts->buf1 + BUFSIZE;*/ |
| + | @@ -180,6 +186,10 @@ |
| + | maxfd = fd; |
| + | ts->ptyfd = fd; |
| + | ndelay_on(fd); |
| + | +#ifdef TIOCPKT |
| + | + ioctl(fd, TIOCPKT, &on); |
| + | + ts->flowstate = TIOCPKT_DOSTOP; |
| + | +#endif |
| + | #if ENABLE_FEATURE_TELNETD_STANDALONE |
| + | ts->sockfd_read = sock; |
| + | /* SO_KEEPALIVE by popular demand */ |
| + | @@ -385,6 +395,16 @@ |
| + | portnbr = 23, |
| + | }; |
| + | #endif |
| + | +#ifdef TIOCPKT |
| + | + int control; |
| + | + static const char lflow_on[] = |
| + | + {IAC, SB, TELOPT_LFLOW, LFLOW_ON, IAC, SE}; |
| + | + static const char lflow_off[] = |
| + | + {IAC, SB, TELOPT_LFLOW, LFLOW_OFF, IAC, SE}; |
| + | +# define RESERVED sizeof(lflow_on) |
| + | +#else |
| + | +# define RESERVED 0 |
| + | +#endif |
| + | /* Even if !STANDALONE, we accept (and ignore) -i, thus people |
| + | * don't need to guess whether it's ok to pass -i to us */ |
| + | opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"), |
| + | @@ -475,7 +495,7 @@ |
| + | FD_SET(ts->sockfd_read, &rdfdset); |
| + | if (ts->size2 > 0) /* can write to socket */ |
| + | FD_SET(ts->sockfd_write, &wrfdset); |
| + | - if (ts->size2 < BUFSIZE) /* can read from pty */ |
| + | + if (ts->size2 < (BUFSIZE - RESERVED)) /* can read from pty */ |
| + | FD_SET(ts->ptyfd, &rdfdset); |
| + | } |
| + | ts = next; |
| + | @@ -593,6 +613,52 @@ |
| + | goto skip4; |
| + | goto kill_session; |
| + | } |
| + | +#ifdef TIOCPKT |
| + | + control = TS_BUF2[ts->rdidx2]; |
| + | + if (--count > 0 && control == TIOCPKT_DATA) { |
| + | + /* |
| + | + * If we are in packet mode, and we have |
| + | + * just read a chunk of actual data from |
| + | + * the pty, then there is the TIOCPKT_DATA |
| + | + * byte (zero) that we have got to remove |
| + | + * somehow. If there were no chars in |
| + | + * TS_BUF2 before we did this read, then |
| + | + * we can optimize by just advancing wridx2. |
| + | + * Otherwise we have to copy the new data down |
| + | + * to close the gap (Could use readv() instead). |
| + | + */ |
| + | + if (ts->size2 == 0) |
| + | + ts->wridx2++; |
| + | + else { |
| + | + memmove(TS_BUF2 + ts->rdidx2, |
| + | + TS_BUF2 + ts->rdidx2 + 1, count); |
| + | + } |
| + | + } |
| + | + |
| + | + /* |
| + | + * If the flow control state changed, notify |
| + | + * the client. If "control" is not TIOCPKT_DATA, |
| + | + * then there are no data bytes to worry about. |
| + | + */ |
| + | + if ((control & (TIOCPKT_DOSTOP|TIOCPKT_NOSTOP)) != 0 |
| + | + && ts->flowstate != (control & TIOCPKT_DOSTOP)) { |
| + | + const char *p = ts->flowstate ? lflow_off : lflow_on; |
| + | + |
| + | + /* |
| + | + * We know we have enough free slots available |
| + | + * (see RESERVED) but they are not necessarily |
| + | + * contiguous; we may have to wrap. |
| + | + */ |
| + | + for (count = sizeof(lflow_on); count > 0; count--) { |
| + | + TS_BUF2[ts->rdidx2++] = *p++; |
| + | + if (ts->rdidx2 >= BUFSIZE) |
| + | + ts->rdidx2 = 0; |
| + | + ts->size2++; |
| + | + } |
| + | + |
| + | + ts->flowstate = control & TIOCPKT_DOSTOP; |
| + | + } |
| + | +#endif /* TIOCPKT */ |
| + | ts->size2 += count; |
| + | ts->rdidx2 += count; |
| + | if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */ |
| + | |
| + | --Doug |
| + | _______________________________________________ |
| + | busybox mailing list |
| + | [email protected] |
| + | http://lists.busybox.net/mailman/listinfo/busybox |