AmendHub

Download:

jcs

/

subtext

/

amendments

/

41

telnet: Separate TCP input buffering from IAC processing

Do IAC processing from the user's session, since it may need to send
data and we need to be able to wait for their output buffer to flush
while on their uthread

jcs made amendment 41 over 2 years ago
--- telnet.c Tue Dec 28 22:01:27 2021 +++ telnet.c Sat Jan 1 21:47:57 2022 @@ -88,11 +88,11 @@ enum { struct telnet_node { short id; short state; - TCPiopb pb, listen_pb; + TCPiopb rcv_pb, send_pb, listen_pb; StreamPtr stream; wdsEntry tcp_wds[2]; unsigned short obuflen; - unsigned char obuf[512]; /* this must be session.obuf*2 */ + unsigned char obuf[512]; unsigned char ibuf[64]; unsigned short ibuflen; unsigned short sending_iac; @@ -114,11 +114,12 @@ TCPStatusPB telnet_status_pb; void telnet_atexit(void); void telnet_setup(struct session *session); +short telnet_process_input(struct session *session); void telnet_output_iac(struct session *session, char *iacs, size_t len); struct node_funcs telnet_node_funcs = { telnet_setup, - telnet_input, + telnet_process_input, /* we'll receive input outside of session */ telnet_output, telnet_close }; @@ -182,7 +183,7 @@ telnet_idle(void) node = xmalloczero(sizeof(struct telnet_node)); node->id = n; - error = _TCPCreate(&node->pb, &node->stream, + error = _TCPCreate(&node->send_pb, &node->stream, (Ptr)&node->tcp_buf, sizeof(node->tcp_buf), nil, nil, nil, false); if (error) @@ -202,8 +203,8 @@ telnet_idle(void) switch (node->state) { case TELNET_PB_STATE_LISTENING: - error = _TCPStatus(&node->pb, node->stream, &telnet_status_pb, - nil, nil, false); + error = _TCPStatus(&node->send_pb, node->stream, + &telnet_status_pb, nil, nil, false); if (error == connectionDoesntExist) continue; if (error != noErr) @@ -262,14 +263,14 @@ telnet_input(struct session *session) if (session->ending) return 0; - if (session->ibuflen >= sizeof(session->ibuf)) + if (node->ibuflen >= sizeof(node->ibuf)) return 0; - if (node->pb.ioResult > 0) + if (node->rcv_pb.ioResult > 0) /* previous _TCPSend/_TCPRecv has not completed yet */ return 0; - error = _TCPStatus(&node->pb, node->stream, &telnet_status_pb, nil, + error = _TCPStatus(&node->rcv_pb, node->stream, &telnet_status_pb, nil, nil, false); if (error || telnet_status_pb.connectionState != ConnectionStateEstablished) { @@ -281,20 +282,42 @@ telnet_input(struct session *session) return 0; rlen = telnet_status_pb.amtUnreadData; - if (session->ibuflen + rlen >= sizeof(session->ibuf)) - rlen = sizeof(session->ibuf) - session->ibuflen; + if (node->ibuflen + rlen >= sizeof(node->ibuf)) + rlen = sizeof(node->ibuf) - node->ibuflen; - error = _TCPRcv(&node->pb, node->stream, (Ptr)(node->ibuf), &rlen, - nil, nil, false); + error = _TCPRcv(&node->rcv_pb, node->stream, + (Ptr)(node->ibuf + node->ibuflen), &rlen, nil, nil, false); if (error) { warn("TCPRecv[%d] failed: %d", node->id, error); session->ending = 1; return 0; } + + node->ibuflen += rlen; + + return rlen; +} +short +telnet_process_input(struct session *session) +{ + struct telnet_node *node = (struct telnet_node *)session->cookie; + short error, n; + unsigned char c; + char iac_out[8] = { IAC, 0 }; + + if (node->ibuflen == 0) + return 0; + + if (session->ending) + return 0; + session->last_input_at = Time; - - for (n = 0; n < rlen; n++) { + + for (n = 0; n < node->ibuflen; n++) { + if (session->ibuflen >= sizeof(session->ibuf) - 5) + break; + c = node->ibuf[n]; switch (node->iac_state) { @@ -461,8 +484,16 @@ telnet_input(struct session *session) break; } } - - return rlen; + + if (n) { + node->ibuflen -= n; + if (node->ibuflen == 1) + node->ibuf[0] = node->ibuf[n]; + else if (node->ibuflen != 0) + memmove(node->ibuf, node->ibuf + n, node->ibuflen); + } + + return n; } short @@ -475,17 +506,18 @@ telnet_output(struct session *session) if (session->obuflen == 0 || session->ending) return 0; - if (node->pb.ioResult > 0) + if (node->send_pb.ioResult > 0) /* previous _TCPSend has not completed yet */ return 0; +process_result: if (node->tcp_wds[0].length) { /* previous _TCPSend completed, shift out those bytes */ session->obuflen -= node->obuflen; if (session->obuflen < 0) warn("bogus obuflen %d", session->obuflen); if (session->obuflen > 0) - BlockMove(session->obuf + node->obuflen, session->obuf, + memmove(session->obuf, session->obuf + node->obuflen, session->obuflen); node->tcp_wds[0].length = 0; @@ -496,7 +528,7 @@ telnet_output(struct session *session) /* copy obuf to node buffer, escaping IACs */ for (node->escaped_obuflen = 0, node->obuflen = 0; - node->obuflen < session->obuflen; + node->obuflen < session->obuflen && node->escaped_obuflen < sizeof(node->obuf); node->obuflen++) { c = session->obuf[node->obuflen]; @@ -517,13 +549,17 @@ telnet_output(struct session *session) node->tcp_wds[0].ptr = (Ptr)&node->obuf; node->tcp_wds[0].length = node->escaped_obuflen; - error = _TCPSend(&node->pb, node->stream, node->tcp_wds, nil, nil, + 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 = 1; } + /* if we sent quickly enough, we won't have to cycle again */ + if (node->send_pb.ioResult <= 0) + goto process_result; + return 0; } @@ -532,20 +568,13 @@ telnet_output_iac(struct session *session, char *iacs, { struct telnet_node *node = (struct telnet_node *)session->cookie; - /* flush output buffer */ - while (session->obuflen && !session->ending) - telnet_output(session); - + session_flush(session); if (session->ending) return; node->sending_iac = 1; - session_output(session, iacs, len); - - while (session->obuflen && !session->ending) - telnet_output(session); - + session_flush(session); node->sending_iac = 0; }