AmendHub

Download:

jcs

/

subtext

/

amendments

/

573

telnet: Speed up initial IAC negotiation to avoid delay after connect

Extract output IAC escaping from telnet_output so telnet_output_iac
can call it to put data into node->obuf without actually flushing it
to the network. Since the initial IAC negotiation will have a bunch
of back and forth, this lets us queue up a bunch of outgoing data
that we can flush once.
 
Also avoid sending DO NEWENV unless we're talking to the trusted
proxy, since we only need that to receive REMOTE_ADDR. Otherwise
we'll waste time and packets receiving junk like the telnet user's
DISPLAY or XAUTHORITY variables.

jcs made amendment 573 11 months ago
--- telnet.c Mon Nov 27 21:17:05 2023 +++ telnet.c Sat Dec 2 20:26:57 2023 @@ -100,7 +100,6 @@ struct telnet_node { unsigned short ibuflen; unsigned short ibufoff; unsigned short sending_iac; - unsigned short escaped_obuflen; short iac_state; unsigned char iac_sb[128]; short sb_len; @@ -142,6 +141,7 @@ static const char * vt100_terms[] = { void telnet_deinit(void); void telnet_setup(struct session *session); void telnet_listen_on_node(struct telnet_node *node); +short telnet_escape_output(struct session *session); void telnet_output_iac(struct session *session, const char *iacs, size_t len); void telnet_print_busy(struct telnet_node *node); @@ -424,11 +424,20 @@ telnet_setup(struct session *session) IAC, DO, IAC_NAWS, /* send your NAWS */ IAC, DO, IAC_TTYPE, /* send your TTYPE */ IAC, DO, IAC_NEWENV, /* send your NEWENV vars */ - - /* IAC, DO, IAC_LINEMODE, */ }; + struct telnet_node *node = (struct telnet_node *)session->cookie; + size_t size = sizeof(iacs); - telnet_output_iac(session, iacs, sizeof(iacs)); + if (!node->from_trusted_proxy) + /* we only care about NEWENV when we need REMOTE_ADDR */ + size -= 3; + + telnet_output_iac(session, iacs, size); + session_flush(session); + + /* see if we can quickly read and parse any incoming IAC */ + telnet_input(session); + session_flush(session); } short @@ -492,7 +501,7 @@ telnet_input(struct session *session) if (error) { session_logf(session, "TCP read failed (%d), closing connection", error); - session->ending = 1; + session->ending = true; return 0; } @@ -686,8 +695,7 @@ telnet_input(struct session *session) if (tspeed > 0) { if (tspeed > 57600) tspeed = 57600; - node->session->tspeed = - node->session->log.tspeed = + node->session->tspeed = node->session->log.tspeed = (unsigned short)tspeed; } break; @@ -716,6 +724,8 @@ telnet_input(struct session *session) telnet_output_iac(session, iac_out, 3); break; case IAC_NEWENV: + if (!node->from_trusted_proxy) + break; case IAC_TTYPE: case IAC_TSPEED: iac_out[1] = SB; @@ -787,102 +797,125 @@ telnet_input(struct session *session) node->ibuflen -= used; } + if (node->obuflen) + session_flush(session); + return rlen; } short -telnet_output(struct session *session) +telnet_escape_output(struct session *session) { struct telnet_node *node = (struct telnet_node *)session->cookie; - short error; unsigned char c; - unsigned long now; + unsigned long added = 0; if (session->obuflen == 0 || session->ending) return 0; - if (node->send_pb.ioResult > 0) - /* previous _TCPSend has not completed yet */ - return 0; - -process_result: - if (node->tcp_wds[0].length) { - if (node->tcp_wds[0].length != node->escaped_obuflen && - node->escaped_obuflen != node->obuflen) - warn("bug: tried to send %d (escaped from %d) but TCP says " - "we sent %d, how much to shift out?", node->escaped_obuflen, - node->obuflen, node->tcp_wds[0].length); - - /* previous _TCPSend completed, shift out those bytes */ - if (session->obuflen < node->obuflen) { - warn("bogus obuflen %d", session->obuflen); - session->obuflen = 0; - } else - session->obuflen -= node->obuflen; - if (session->obuflen > 0) - memmove(session->obuf, session->obuf + node->obuflen, - session->obuflen); - - node->tcp_wds[0].length = 0; - - if (session->obuflen == 0) - return node->obuflen; - } - if (session->obuflen == 1 && session->obuf[0] != IAC) { - node->obuf[0] = session->obuf[0]; - node->escaped_obuflen = node->obuflen = 1; + node->obuf[node->obuflen++] = session->obuf[0]; + session->obuflen = 0; } else if (session->direct_output) { - memcpy(node->obuf, session->obuf, session->obuflen); - node->escaped_obuflen = node->obuflen = session->obuflen; + added = MIN(session->obuflen, sizeof(node->obuf) - node->obuflen); + memcpy(node->obuf + node->obuflen, session->obuf, added); + node->obuflen += added; } else { - /* copy obuf to node buffer, escaping IACs */ - for (node->escaped_obuflen = 0, node->obuflen = 0; - node->obuflen < session->obuflen && - node->escaped_obuflen < sizeof(node->obuf) - 1; - node->obuflen++) { - c = session->obuf[node->obuflen]; + /* copy session obuf to node buffer, escaping IACs */ + for (added = 0; added < session->obuflen; added++) { + if (node->obuflen >= sizeof(node->obuf) - 1) + break; + + c = session->obuf[added]; if (!node->sending_iac && c == IAC) { - node->obuf[node->escaped_obuflen++] = IAC; - node->obuf[node->escaped_obuflen++] = IAC; + node->obuf[node->obuflen++] = IAC; + node->obuf[node->obuflen++] = IAC; } else - node->obuf[node->escaped_obuflen++] = c; + node->obuf[node->obuflen++] = c; } } - if (node->escaped_obuflen > sizeof(node->obuf)) { - warn("bogus obuflen %d > %ld", node->escaped_obuflen, - sizeof(node->obuf)); - session->ending = true; - session_close(session); + if (added > 0) { + if (added < session->obuflen) + memmove(session->obuf, session->obuf + added, + session->obuflen - added); + + session->obuflen -= added; + } + + return added; +} + +short +telnet_output(struct session *session) +{ + struct telnet_node *node = (struct telnet_node *)session->cookie; + unsigned long now = Ticks; + short error, ret = 0; + + if (session->obuflen != 0) + telnet_escape_output(session); + + if (node->obuflen == 0 || session->ending) return 0; + + /* wait for previous _TCPSend to complete */ + while (node->send_pb.ioResult > 0) { + if (uthread_current == NULL) + /* this was an opportunistic flush anyway, no sense waiting */ + return 0; + + uthread_yield(); + + if (session->ending) + return 0; } + +process_result: + if (node->tcp_wds[0].length) { + /* shift out old data */ + if (node->obuflen < node->tcp_wds[0].length) + panic("data in tcp_wds > obuflen"); + + if (node->obuflen > node->tcp_wds[0].length) { + memmove(node->obuf, node->obuf + node->tcp_wds[0].length, + node->obuflen - node->tcp_wds[0].length); + node->obuflen -= node->tcp_wds[0].length; + } else + node->obuflen = 0; + node->tcp_wds[0].length = 0; + + if (node->obuflen == 0) + return ret; + } + /* * _TCPSend only knows how many wds pointers were passed in when it * reads the next one and its pointer is zero (or size is zero?) */ node->tcp_wds[0].ptr = (Ptr)&node->obuf; - node->tcp_wds[0].length = node->escaped_obuflen; + node->tcp_wds[0].length = node->obuflen; node->tcp_wds[1].ptr = 0; node->tcp_wds[1].length = 0; + + ret = node->tcp_wds[0].length; - now = Ticks; error = _TCPSend(&node->send_pb, node->stream, node->tcp_wds, nil, nil, true); if (error) { warn("TCPSend[%d] failed: %d", node->id, error); session->ending = true; } - + /* if we can send in less than 500ms, avoid a uthread switch */ while (Ticks - now <= 30) { if (node->send_pb.ioResult <= 0) goto process_result; } - return 0; + return ret; } void @@ -890,13 +923,14 @@ telnet_output_iac(struct session *session, const char { struct telnet_node *node = (struct telnet_node *)session->cookie; - session_flush(session); if (session->ending) return; + + telnet_escape_output(session); node->sending_iac = 1; session_output(session, iacs, len); - session_flush(session); + telnet_escape_output(session); node->sending_iac = 0; }