diff options
author | Elliott Hughes <enh@google.com> | 2021-04-22 18:54:41 -0700 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2021-04-24 04:50:25 -0500 |
commit | dbd7a975ec64dc52af704970109f8c637600b088 (patch) | |
tree | b6753f7a1ee517241287cfa4e3d484cb7c57c74c /toys | |
parent | 35c484489b06134fb1ac56801c621020b935b2a7 (diff) | |
download | toybox-dbd7a975ec64dc52af704970109f8c637600b088.tar.gz |
telnetd: handle TIME_WAIT better.
After a network outage, a long-running telnetd was spinning trying to
read from a socket that was in TIME_WAIT. It's easy to reproduce this by
using the regular telnet client and typing ^]^D to exit abruptly.
I don't think these sockets should ever have been non-blocking, and we
want to give up on the client if we hit EOF. All of this needs
rewriting to be less complicated (and not use select(2)), but this seems
to be a minimal fix for the spin without harming normal usage (where by
"usage" I mean "testing the telnet client").
Diffstat (limited to 'toys')
-rw-r--r-- | toys/pending/telnetd.c | 22 |
1 files changed, 12 insertions, 10 deletions
diff --git a/toys/pending/telnetd.c b/toys/pending/telnetd.c index 7bfbf316..032104f2 100644 --- a/toys/pending/telnetd.c +++ b/toys/pending/telnetd.c @@ -149,23 +149,16 @@ static int new_session(int sockfd) { char *argv_login[] = {NULL, "-h", NULL, NULL}; char tty_name[30]; //tty name length. - int fd, flags, i = 1; + int fd, i = 1; char intial_iacs[] = {IAC, DO, TELOPT_ECHO, IAC, DO, TELOPT_NAWS, IAC, WILL, TELOPT_ECHO, IAC, WILL, TELOPT_SGA }; struct sockaddr_storage sa; socklen_t sl = sizeof(sa); setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)); - flags = fcntl(sockfd, F_GETFL); - fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); - if (FLAG(i)) fcntl((sockfd + 1), F_SETFL, flags | O_NONBLOCK); writeall(FLAG(i)?1:sockfd, intial_iacs, sizeof(intial_iacs)); - if ((TT.fork_pid = forkpty(&fd, tty_name, NULL, NULL)) > 0) { - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); - return fd; - } + if ((TT.fork_pid = forkpty(&fd, tty_name, NULL, NULL)) > 0) return fd; if (TT.fork_pid < 0) perror_exit("fork"); if (getpeername(sockfd, (void *)&sa, &sl)) perror_exit("getpeername"); @@ -362,7 +355,15 @@ void telnetd_main(void) } if (FD_ISSET(tm->new_fd, &rd)) { if ((c = read(tm->new_fd, tm->buff2+tm->buff2_avail, - BUFSIZE-tm->buff2_avail)) <= 0) break; + BUFSIZE-tm->buff2_avail)) <= 0) { + // The other side went away without a proper shutdown. Happens if + // you exit telnet via ^]^D, leaving the socket in TIME_WAIT. + xclose(tm->new_fd); + tm->new_fd = -1; + xclose(tm->pty_fd); + tm->pty_fd = -1; + break; + } c = handle_iacs(tm, c, tm->pty_fd); tm->buff2_avail += c; if ((w = write(tm->pty_fd, tm->buff2+ tm->buff2_written, @@ -405,6 +406,7 @@ void telnetd_main(void) if (!tm) error_exit("unexpected reparenting of %d", pid); if (FLAG(i)) exit(EXIT_SUCCESS); + if (!prev) session_list = session_list->next; else prev->next = tm->next; xclose(tm->pty_fd); |