diff --git a/src/syncterm/conn_telnet.c b/src/syncterm/conn_telnet.c index d2eeee6fba6d41868515157e1d0c250613f60e9a..a6133da58b86d4a23e32063a8c6f25499ed6fef3 100644 --- a/src/syncterm/conn_telnet.c +++ b/src/syncterm/conn_telnet.c @@ -2,6 +2,7 @@ /* $Id: conn_telnet.c,v 1.18 2020/05/03 20:12:42 deuce Exp $ */ +#include <stdbool.h> #include <stdlib.h> #include "gen_defs.h" @@ -19,6 +20,65 @@ extern int telnet_log_level; +/*****************************************************************************/ +// Escapes Telnet IACs in 'inbuf' by doubling the IAC char +// 'result' may point to either inbuf (if there were no IACs) or outbuf +// Returns the final byte count of the result +/*****************************************************************************/ +size_t st_telnet_expand(const uchar* inbuf, size_t inlen, uchar* outbuf, size_t outlen, BOOL expand_cr, uchar** result) +{ + static bool last_was_lf = false; + BYTE* first_iac = (BYTE*)memchr(inbuf, TELNET_IAC, inlen); + BYTE* first_cr=NULL; + + if (inlen == 0) { + if (result != NULL) + *result = (uchar *)inbuf; + return 0; + } + if (last_was_lf && inbuf[0] == '\n') { + inbuf++; + inlen--; + } + last_was_lf = false; + if (expand_cr) + first_cr = (BYTE*)memchr(inbuf, '\r', inlen); + else + last_was_lf = false; + + if (first_iac == NULL && first_cr == NULL) { /* Nothing to expand */ + if (result != NULL) + *result = (uchar*)inbuf; + return inlen; + } + + size_t o; + + if(first_iac != NULL && (first_cr == NULL || first_iac < first_cr)) + o = first_iac - inbuf; + else + o = first_cr - inbuf; + memcpy(outbuf, inbuf, o); + + for (size_t i = o; i < inlen && o < outlen; i++) { + if (inbuf[i] == '\n' && last_was_lf) + continue; + last_was_lf = false; + if(inbuf[i] == TELNET_IAC) + outbuf[o++] = TELNET_IAC; + if(o >= outlen) + break; + outbuf[o++] = inbuf[i]; + if(expand_cr && inbuf[i] == '\r' && o < outlen) { + last_was_lf = true; + outbuf[o++] = '\n'; // See RFC5198 + } + } + if(result != NULL) + *result = outbuf; + return o; +} + void *telnet_rx_parse_cb(const void *buf, size_t inlen, size_t *olen) { // telnet_interpret() can add up to one byte to inbuf ('\r') @@ -36,7 +96,7 @@ void *telnet_tx_parse_cb(const void *buf, size_t len, size_t *olen) void *ret = malloc(len * 2); void *parsed; - *olen = telnet_expand(buf, len, ret, len * 2 + *olen = st_telnet_expand(buf, len, ret, len * 2 ,telnet_local_option[TELNET_BINARY_TX]!=TELNET_DO, (BYTE **)&parsed); if (parsed != ret)