Update libtelnet to 0.23 (portability issues) (#1005)

Split out work on libtelnet from #855 
Required to update libtelnet to introduce compatibility with Windows

This change contains vanilla code of
[libtelnet](https://github.com/seanmiddleditch/libtelnet) v0.23 (latest
tag)

Test Plan:
- run turnserver locally, set cli password, connect to the turnserver
cli interface
- run a few commands - get output
This commit is contained in:
Pavel Punsky 2022-10-07 14:51:46 -07:00 committed by GitHub
parent 3492644c11
commit 080ca02768
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 184 additions and 51 deletions

View File

@ -11,10 +11,6 @@
* all present and future rights to this code under copyright law.
*/
/**
* Minor fixes by Oleg Moskalenko
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@ -29,6 +25,15 @@
# define vsnprintf _vsnprintf
# define __func__ __FUNCTION__
# define ZLIB_WINAPI 1
# if defined(_MSC_VER)
/* va_copy() is directly supported starting in Visual Studio 2013
* https://msdn.microsoft.com/en-us/library/kb57fad8(v=vs.110).aspx
* https://msdn.microsoft.com/en-us/library/kb57fad8(v=vs.120).aspx
*/
# if _MSC_VER <= 1700
# define va_copy(dest, src) (dest = src)
# endif
# endif
#endif
#if defined(HAVE_ZLIB)
@ -58,6 +63,7 @@
/* telnet state codes */
enum telnet_state_t {
TELNET_STATE_DATA = 0,
TELNET_STATE_EOL,
TELNET_STATE_IAC,
TELNET_STATE_WILL,
TELNET_STATE_WONT,
@ -96,7 +102,9 @@ struct telnet_t {
/* current subnegotiation telopt */
unsigned char sb_telopt;
/* length of RFC1143 queue */
unsigned char q_size;
unsigned int q_size;
/* number of entries in RFC1143 queue */
unsigned int q_cnt;
};
/* RFC1143 option negotiation state */
@ -113,11 +121,18 @@ typedef struct telnet_rfc1143_t {
#define Q_WANTNO_OP 4
#define Q_WANTYES_OP 5
/* telnet NVT EOL sequences */
static const char CRLF[] = { '\r', '\n' };
static const char CRNUL[] = { '\r', '\0' };
/* buffer sizes */
static const size_t _buffer_sizes[] = { 0, 512, 2048, 8192, 16384, };
static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
sizeof(_buffer_sizes[0]);
/* RFC1143 option negotiation state table allocation quantum */
#define Q_BUFFER_GROWTH_QUANTUM 4
/* error generation function */
static telnet_error_t _error(telnet_t *telnet, unsigned line,
const char* func, telnet_error_t err, int fatal, const char *fmt,
@ -138,7 +153,7 @@ static telnet_error_t _error(telnet_t *telnet, unsigned line,
ev.error.line = line;
ev.error.msg = buffer;
telnet->eh(telnet, &ev, telnet->ud);
return err;
}
@ -198,7 +213,7 @@ static void _send(telnet_t *telnet, const char *buffer,
/* initialize z state */
telnet->z->next_in = (unsigned char *)buffer;
telnet->z->avail_in = size;
telnet->z->avail_in = (unsigned int)size;
telnet->z->next_out = (unsigned char *)deflate_buffer;
telnet->z->avail_out = sizeof(deflate_buffer);
@ -251,7 +266,7 @@ static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt,
if (telnet->telopts == 0)
return 0;
/* loop unti found or end marker (us and him both 0) */
/* loop until found or end marker (us and him both 0) */
for (i = 0; telnet->telopts[i].telopt != -1; ++i) {
if (telnet->telopts[i].telopt == telopt) {
if (us && telnet->telopts[i].us == TELNET_WILL)
@ -274,7 +289,7 @@ static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
int i;
/* search for entry */
for (i = 0; i != telnet->q_size; ++i) {
for (i = 0; i != telnet->q_cnt; ++i) {
if (telnet->q[i].telopt == telopt) {
return telnet->q[i];
}
@ -293,9 +308,17 @@ static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
int i;
/* search for entry */
for (i = 0; i != telnet->q_size; ++i) {
for (i = 0; i != telnet->q_cnt; ++i) {
if (telnet->q[i].telopt == telopt) {
telnet->q[i].state = Q_MAKE(us,him);
if (telopt != TELNET_TELOPT_BINARY)
return;
telnet->flags &= ~(TELNET_FLAG_TRANSMIT_BINARY |
TELNET_FLAG_RECEIVE_BINARY);
if (us == Q_YES)
telnet->flags |= TELNET_FLAG_TRANSMIT_BINARY;
if (him == Q_YES)
telnet->flags |= TELNET_FLAG_RECEIVE_BINARY;
return;
}
}
@ -306,17 +329,26 @@ static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
* to the number of enabled options for most simple code, and it
* allows for an acceptable number of reallocations for complex code.
*/
if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q,
sizeof(telnet_rfc1143_t) * (telnet->q_size + 4))) == 0) {
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
"realloc() failed: %s", strerror(errno));
return;
/* Did we reach the end of the table? */
if (telnet->q_cnt >= telnet->q_size) {
/* Expand the size */
if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q,
sizeof(telnet_rfc1143_t) *
(telnet->q_size + Q_BUFFER_GROWTH_QUANTUM))) == 0) {
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
"realloc() failed: %s", strerror(errno));
return;
}
memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) *
Q_BUFFER_GROWTH_QUANTUM);
telnet->q = qtmp;
telnet->q_size += Q_BUFFER_GROWTH_QUANTUM;
}
memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * 4);
telnet->q = qtmp;
telnet->q[telnet->q_size].telopt = telopt;
telnet->q[telnet->q_size].state = Q_MAKE(us, him);
telnet->q_size += 4;
/* Add entry to end of table */
telnet->q[telnet->q_cnt].telopt = telopt;
telnet->q[telnet->q_cnt].state = Q_MAKE(us, him);
++telnet->q_cnt;
}
/* send negotiation bytes */
@ -503,7 +535,7 @@ static int _environ_telnet(telnet_t *telnet, unsigned char type,
/* first byte must be a valid command */
if ((unsigned)buffer[0] != TELNET_ENVIRON_SEND &&
(unsigned)buffer[0] != TELNET_ENVIRON_IS &&
(unsigned)buffer[0] != TELNET_ENVIRON_IS &&
(unsigned)buffer[0] != TELNET_ENVIRON_INFO) {
_error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
"telopt %d subneg has invalid command", type);
@ -712,7 +744,7 @@ static int _mssp_telnet(telnet_t *telnet, char* buffer, size_t size) {
/* parse ZMP command subnegotiation buffers */
static int _zmp_telnet(telnet_t *telnet, const char* buffer, size_t size) {
telnet_event_t ev;
const char **argv;
char **argv;
const char *c;
size_t i, argc;
@ -728,7 +760,7 @@ static int _zmp_telnet(telnet_t *telnet, const char* buffer, size_t size) {
c += strlen(c) + 1;
/* allocate argument array, bail on error */
if ((argv = (const char **)calloc(argc, sizeof(char *))) == 0) {
if ((argv = (char **)calloc(argc, sizeof(char *))) == 0) {
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
"calloc() failed: %s", strerror(errno));
return 0;
@ -736,13 +768,13 @@ static int _zmp_telnet(telnet_t *telnet, const char* buffer, size_t size) {
/* populate argument array */
for (i = 0, c = buffer; i != argc; ++i) {
argv[i] = c;
argv[i] = (char *)c;
c += strlen(c) + 1;
}
/* invoke event with our arguments */
ev.type = TELNET_EV_ZMP;
ev.zmp.argv = argv;
ev.zmp.argv = (const char**)argv;
ev.zmp.argc = argc;
telnet->eh(telnet, &ev, telnet->ud);
@ -890,8 +922,9 @@ void telnet_free(telnet_t *telnet) {
/* free RFC1143 queue */
if (telnet->q) {
free(telnet->q);
telnet->q = 0;
telnet->q = NULL;
telnet->q_size = 0;
telnet->q_cnt = 0;
}
/* free the telnet structure itself */
@ -956,9 +989,38 @@ static void _process(telnet_t *telnet, const char *buffer, size_t size) {
telnet->eh(telnet, &ev, telnet->ud);
}
telnet->state = TELNET_STATE_IAC;
} else if (byte == '\r' &&
(telnet->flags & TELNET_FLAG_NVT_EOL) &&
!(telnet->flags & TELNET_FLAG_RECEIVE_BINARY)) {
if (i != start) {
ev.type = TELNET_EV_DATA;
ev.data.buffer = buffer + start;
ev.data.size = i - start;
telnet->eh(telnet, &ev, telnet->ud);
}
telnet->state = TELNET_STATE_EOL;
}
break;
/* NVT EOL to be translated */
case TELNET_STATE_EOL:
if (byte != '\n') {
byte = '\r';
ev.type = TELNET_EV_DATA;
ev.data.buffer = (char*)&byte;
ev.data.size = 1;
telnet->eh(telnet, &ev, telnet->ud);
byte = buffer[i];
}
// any byte following '\r' other than '\n' or '\0' is invalid,
// so pass both \r and the byte
start = i;
if (byte == '\0')
++start;
/* state update */
telnet->state = TELNET_STATE_DATA;
break;
/* IAC command */
case TELNET_STATE_IAC:
switch (byte) {
@ -1026,6 +1088,15 @@ static void _process(telnet_t *telnet, const char *buffer, size_t size) {
/* IAC command in subnegotiation -- either IAC SE or IAC IAC */
if (byte == TELNET_IAC) {
telnet->state = TELNET_STATE_SB_DATA_IAC;
} else if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS && byte == TELNET_WILL) {
/* In 1998 MCCP used TELOPT 85 and the protocol defined an invalid
* subnegotiation sequence (IAC SB 85 WILL SE) to start compression.
* Subsequently MCCP version 2 was created in 2000 using TELOPT 86
* and a valid subnegotiation (IAC SB 86 IAC SE). libtelnet for now
* just captures and discards MCCPv1 sequences.
*/
start = i + 2;
telnet->state = TELNET_STATE_DATA;
/* buffer the byte, or bail if we can't */
} else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
start = i + 1;
@ -1097,7 +1168,7 @@ static void _process(telnet_t *telnet, const char *buffer, size_t size) {
}
}
/* pass through any remaining bytes */
/* pass through any remaining bytes */
if (telnet->state == TELNET_STATE_DATA && i != start) {
ev.type = TELNET_EV_DATA;
ev.data.buffer = buffer + start;
@ -1117,7 +1188,7 @@ void telnet_recv(telnet_t *telnet, const char *buffer,
/* initialize zlib state */
telnet->z->next_in = (unsigned char*)buffer;
telnet->z->avail_in = size;
telnet->z->avail_in = (unsigned int)size;
telnet->z->next_out = (unsigned char *)inflate_buffer;
telnet->z->avail_out = sizeof(inflate_buffer);
@ -1186,7 +1257,7 @@ void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
_sendu(telnet, bytes, 3);
return;
}
/* get current option states */
q = _get_rfc1143(telnet, telopt);
@ -1282,6 +1353,49 @@ void telnet_send(telnet_t *telnet, const char *buffer,
}
}
/* send non-command text (escapes IAC bytes and does NVT translation) */
void telnet_send_text(telnet_t *telnet, const char *buffer,
size_t size) {
size_t i, l;
for (l = i = 0; i != size; ++i) {
/* dump prior portion of text, send escaped bytes */
if (buffer[i] == (char)TELNET_IAC) {
/* dump prior text if any */
if (i != l) {
_send(telnet, buffer + l, i - l);
}
l = i + 1;
/* send escape */
telnet_iac(telnet, TELNET_IAC);
}
/* special characters if not in BINARY mode */
else if (!(telnet->flags & TELNET_FLAG_TRANSMIT_BINARY) &&
(buffer[i] == '\r' || buffer[i] == '\n')) {
/* dump prior portion of text */
if (i != l) {
_send(telnet, buffer + l, i - l);
}
l = i + 1;
/* automatic translation of \r -> CRNUL */
if (buffer[i] == '\r') {
_send(telnet, CRNUL, 2);
}
/* automatic translation of \n -> CRLF */
else {
_send(telnet, CRLF, 2);
}
}
}
/* send whatever portion of buffer is left */
if (i != l) {
_send(telnet, buffer + l, i - l);
}
}
/* send subnegotiation header */
void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
unsigned char sb[3];
@ -1326,7 +1440,6 @@ void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
}
void telnet_begin_compress2(telnet_t *telnet) {
UNUSED_ARG(telnet);
#if defined(HAVE_ZLIB)
static const unsigned char compress2[] = { TELNET_IAC, TELNET_SB,
TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
@ -1355,23 +1468,25 @@ void telnet_begin_compress2(telnet_t *telnet) {
/* send formatted data with \r and \n translation in addition to IAC IAC */
int telnet_vprintf(telnet_t *telnet, const char *fmt, va_list va) {
static const char CRLF[] = { '\r', '\n' };
static const char CRNUL[] = { '\r', '\0' };
char buffer[1024];
char *output = buffer;
int rs, i, l;
/* format */
va_list va2;
va_copy(va2, va);
rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
if ((size_t)rs >= sizeof(buffer)) {
if (rs >= sizeof(buffer)) {
output = (char*)malloc(rs + 1);
if (output == 0) {
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
"malloc() failed: %s", strerror(errno));
return -1;
}
rs = vsnprintf(output, rs + 1, fmt, va);
rs = vsnprintf(output, rs + 1, fmt, va2);
}
va_end(va2);
va_end(va);
/* send */
for (l = i = 0; i != rs; ++i) {
@ -1427,16 +1542,20 @@ int telnet_raw_vprintf(telnet_t *telnet, const char *fmt, va_list va) {
int rs;
/* format; allocate more space if necessary */
va_list va2;
va_copy(va2, va);
rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
if ((size_t)rs >= sizeof(buffer)) {
if (rs >= sizeof(buffer)) {
output = (char*)malloc(rs + 1);
if (output == 0) {
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
"malloc() failed: %s", strerror(errno));
return -1;
}
rs = vsnprintf(output, rs + 1, fmt, va);
rs = vsnprintf(output, rs + 1, fmt, va2);
}
va_end(va2);
va_end(va);
/* send out the formatted data */
telnet_send(telnet, output, rs);
@ -1464,13 +1583,13 @@ int telnet_raw_printf(telnet_t *telnet, const char *fmt, ...) {
/* begin NEW-ENVIRON subnegotation */
void telnet_begin_newenviron(telnet_t *telnet, unsigned char cmd) {
telnet_begin_sb(telnet, TELNET_TELOPT_NEW_ENVIRON);
telnet_send(telnet, (char*)&cmd, 1);
telnet_send(telnet, (const char *)&cmd, 1);
}
/* send a NEW-ENVIRON value */
void telnet_newenviron_value(telnet_t *telnet, unsigned char type,
const char *string) {
telnet_send(telnet, (char*)&type, 1);
telnet_send(telnet, (const char*)&type, 1);
if (string != 0) {
telnet_send(telnet, string, strlen(string));

View File

@ -34,20 +34,17 @@
*
* \file libtelnet.h
*
* \version 0.21
* \version 0.23
*
* \author Sean Middleditch <sean@sourcemud.org>
*/
/**
* Minor fixes by Oleg Moskalenko
*/
#if !defined(LIBTELNET_INCLUDE)
#define LIBTELNET_INCLUDE 1
/* standard C headers necessary for the libtelnet API */
#include <stdarg.h>
#include <stddef.h>
/* C++ support */
#if defined(__cplusplus)
@ -57,10 +54,15 @@ extern "C" {
/* printf type checking feature in GCC and some other compilers */
#if __GNUC__
# define TELNET_GNU_PRINTF(f,a) __attribute__((format(printf, f, a))) /*!< internal helper */
# define TELNET_GNU_SENTINEL __attribute__((sentinel)) /*!< internal helper */
#else
# define TELNET_GNU_PRINTF(f,a) /*!< internal helper */
# define TELNET_GNU_SENTINEL /*!< internal helper */
#endif
/* Disable environ macro for Visual C++ 2015. */
#undef environ
/*! Telnet state tracker object type. */
typedef struct telnet_t telnet_t;
@ -139,6 +141,7 @@ typedef struct telnet_telopt_t telnet_telopt_t;
#define TELNET_TELOPT_ENCRYPT 38
#define TELNET_TELOPT_NEW_ENVIRON 39
#define TELNET_TELOPT_MSSP 70
#define TELNET_TELOPT_COMPRESS 85
#define TELNET_TELOPT_COMPRESS2 86
#define TELNET_TELOPT_ZMP 93
#define TELNET_TELOPT_EXOPL 255
@ -176,14 +179,14 @@ typedef struct telnet_telopt_t telnet_telopt_t;
/*@{*/
/*! Control behavior of telnet state tracker. */
#define TELNET_FLAG_PROXY (1<<0)
#define TELNET_FLAG_NVT_EOL (1<<1)
/* Internal-only bits in option flags */
#define TELNET_FLAG_TRANSMIT_BINARY (1<<5)
#define TELNET_FLAG_RECEIVE_BINARY (1<<6)
#define TELNET_PFLAG_DEFLATE (1<<7)
/*@}*/
#if !defined(UNUSED_ARG)
#define UNUSED_ARG(A) do { A=A; } while(0)
#endif
/*!
* error codes
*/
@ -224,8 +227,8 @@ typedef enum telnet_event_type_t telnet_event_type_t; /*!< Telnet event type. */
*/
struct telnet_environ_t {
unsigned char type; /*!< either TELNET_ENVIRON_VAR or TELNET_ENVIRON_USERVAR */
const char *var; /*!< name of the variable being set */
const char *value; /*!< value of variable being set; empty string if no value */
char *var; /*!< name of the variable being set */
char *value; /*!< value of variable being set; empty string if no value */
};
/*!
@ -376,7 +379,7 @@ struct telnet_t;
* \param eh Event handler function called for every event.
* \param flags 0 or TELNET_FLAG_PROXY.
* \param user_data Optional data pointer that will be passsed to eh.
* \return Telent state tracker object.
* \return Telnet state tracker object.
*/
extern telnet_t* telnet_init(const telnet_telopt_t *telopts,
telnet_event_handler_t eh, unsigned char flags, void *user_data);
@ -439,6 +442,17 @@ extern void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
extern void telnet_send(telnet_t *telnet,
const char *buffer, size_t size);
/*!
* Send non-command text (escapes IAC bytes and translates
* \\r -> CR-NUL and \\n -> CR-LF unless in BINARY mode.
*
* \param telnet Telnet state tracker object.
* \param buffer Buffer of bytes to send.
* \param size Number of bytes to send.
*/
extern void telnet_send_text(telnet_t *telnet,
const char *buffer, size_t size);
/*!
* \brief Begin a sub-negotiation command.
*
@ -634,7 +648,7 @@ extern void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv);
*
* \param telnet Telnet state tracker object.
*/
extern void telnet_send_zmpv(telnet_t *telnet, ...);
extern void telnet_send_zmpv(telnet_t *telnet, ...) TELNET_GNU_SENTINEL;
/*!
* \brief Send a ZMP command.