aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2021-04-22 18:54:41 -0700
committerRob Landley <rob@landley.net>2021-04-24 04:50:25 -0500
commitdbd7a975ec64dc52af704970109f8c637600b088 (patch)
treeb6753f7a1ee517241287cfa4e3d484cb7c57c74c
parent35c484489b06134fb1ac56801c621020b935b2a7 (diff)
downloadtoybox-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").
-rw-r--r--toys/pending/telnetd.c22
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);