mirror of https://gerrit.osmocom.org/libtelnet
support ttype/environ/new-environ/mssp parsing (using alloca; fixme?)
This commit is contained in:
parent
1e80f52f80
commit
2d5c499c36
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
||||||
CFLAGS = -Wall -g -O0 -DHAVE_ZLIB -DENABLE_COLOR
|
CFLAGS = -Wall -g -O0 -DHAVE_ZLIB -DHAVE_ALLOCA -DENABLE_COLOR
|
||||||
LFLAGS = -L. -ltelnet -lz
|
LFLAGS = -L. -ltelnet -lz
|
||||||
|
|
||||||
all: telnet-proxy telnet-client telnet-chatd
|
all: telnet-proxy telnet-client telnet-chatd
|
||||||
|
|
97
libtelnet.c
97
libtelnet.c
|
@ -16,8 +16,12 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_ALLOCA
|
||||||
|
#include <alloca.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
#include "zlib.h"
|
#include <zlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "libtelnet.h"
|
#include "libtelnet.h"
|
||||||
|
@ -247,12 +251,12 @@ static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
|
||||||
"malloc() failed: %s", strerror(errno));
|
"malloc() failed: %s", strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t *) * 4);
|
memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * 4);
|
||||||
telnet->q = qtmp;
|
telnet->q = qtmp;
|
||||||
telnet->q[telnet->q_size].telopt = telopt;
|
telnet->q[telnet->q_size].telopt = telopt;
|
||||||
telnet->q[telnet->q_size].us = us;
|
telnet->q[telnet->q_size].us = us;
|
||||||
telnet->q[telnet->q_size].him = him;
|
telnet->q[telnet->q_size].him = him;
|
||||||
++telnet->q_size;
|
telnet->q_size += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send negotiation bytes */
|
/* send negotiation bytes */
|
||||||
|
@ -412,8 +416,10 @@ static void _negotiate(telnet_t *telnet, unsigned char telopt) {
|
||||||
* must be aborted and reprocessed due to COMPRESS2 being activated
|
* must be aborted and reprocessed due to COMPRESS2 being activated
|
||||||
*/
|
*/
|
||||||
static int _subnegotiate(telnet_t *telnet) {
|
static int _subnegotiate(telnet_t *telnet) {
|
||||||
const char **argv;
|
const char **argv; /* for ZMP */
|
||||||
|
char **argv2; /* for everything else */
|
||||||
const char *c;
|
const char *c;
|
||||||
|
const char *l;
|
||||||
size_t i, argc;
|
size_t i, argc;
|
||||||
|
|
||||||
switch (telnet->sb_telopt) {
|
switch (telnet->sb_telopt) {
|
||||||
|
@ -424,7 +430,7 @@ static int _subnegotiate(telnet_t *telnet) {
|
||||||
case TELNET_TELOPT_COMPRESS2:
|
case TELNET_TELOPT_COMPRESS2:
|
||||||
if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
|
if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
|
||||||
if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
|
if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
|
||||||
return 0;
|
break;
|
||||||
|
|
||||||
/* standard SB notification */
|
/* standard SB notification */
|
||||||
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
||||||
|
@ -436,6 +442,7 @@ static int _subnegotiate(telnet_t *telnet) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif /* HAVE_ZLIB */
|
#endif /* HAVE_ZLIB */
|
||||||
|
#ifdef HAVE_ALLOCA
|
||||||
/* ZMP command */
|
/* ZMP command */
|
||||||
case TELNET_TELOPT_ZMP:
|
case TELNET_TELOPT_ZMP:
|
||||||
/* make sure this is a valid ZMP buffer */
|
/* make sure this is a valid ZMP buffer */
|
||||||
|
@ -445,7 +452,7 @@ static int _subnegotiate(telnet_t *telnet) {
|
||||||
"incomplete ZMP frame");
|
"incomplete ZMP frame");
|
||||||
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
||||||
telnet->buffer, telnet->buffer_pos, 0, 0);
|
telnet->buffer, telnet->buffer_pos, 0, 0);
|
||||||
return 0;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* count arguments */
|
/* count arguments */
|
||||||
|
@ -454,11 +461,12 @@ static int _subnegotiate(telnet_t *telnet) {
|
||||||
c += strlen(c) + 1;
|
c += strlen(c) + 1;
|
||||||
|
|
||||||
/* allocate argument array, bail on error */
|
/* allocate argument array, bail on error */
|
||||||
if ((argv = (const char **)malloc(sizeof(char *) * argc)) == 0) {
|
if ((argv = (const char **)alloca(sizeof(char *) * argc)) == 0) {
|
||||||
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
|
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
|
||||||
"malloc() failed: %s", strerror(errno));
|
"alloca() failed: %s", strerror(errno));
|
||||||
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
||||||
telnet->buffer, telnet->buffer_pos, 0, 0);
|
telnet->buffer, telnet->buffer_pos, 0, 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* populate argument array */
|
/* populate argument array */
|
||||||
|
@ -470,10 +478,75 @@ static int _subnegotiate(telnet_t *telnet) {
|
||||||
/* invoke event with our arguments */
|
/* invoke event with our arguments */
|
||||||
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
||||||
telnet->buffer, telnet->buffer_pos, argv, argc);
|
telnet->buffer, telnet->buffer_pos, argv, argc);
|
||||||
|
|
||||||
/* free argument array */
|
|
||||||
free(argv);
|
|
||||||
break;
|
break;
|
||||||
|
/* any of a number of commands that use the form <BYTE>data<BYTE>data,
|
||||||
|
* including TTYPE, ENVIRON, NEW-ENVIRON, and MSSP
|
||||||
|
*/
|
||||||
|
case TELNET_TELOPT_TTYPE:
|
||||||
|
case TELNET_TELOPT_ENVIRON:
|
||||||
|
case TELNET_TELOPT_NEW_ENVIRON:
|
||||||
|
case TELNET_TELOPT_MSSP:
|
||||||
|
/* if we have no data, just pass it through */
|
||||||
|
if (telnet->buffer_pos == 0) {
|
||||||
|
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
||||||
|
telnet->buffer, telnet->buffer_pos, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* very first byte must be in range 0-3 */
|
||||||
|
if ((unsigned)telnet->buffer[0] > 3) {
|
||||||
|
_error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
|
||||||
|
"telopt %d subneg has invalid data", telnet->sb_telopt);
|
||||||
|
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
||||||
|
telnet->buffer, telnet->buffer_pos, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* count arguments; each argument is preceded by a byte in the
|
||||||
|
* range 0-3, so just count those.
|
||||||
|
* NOTE: we don't support the ENVIRON/NEW-ENVIRON ESC handling
|
||||||
|
* properly at all. guess that's a FIXME.
|
||||||
|
*/
|
||||||
|
for (argc = 0, i = 0; i != telnet->buffer_pos; ++i)
|
||||||
|
if ((unsigned)telnet->buffer[i] <= 3)
|
||||||
|
++argc;
|
||||||
|
|
||||||
|
/* allocate argument array, bail on error */
|
||||||
|
if ((argv2 = (char **)alloca(sizeof(char *) * argc)) == 0) {
|
||||||
|
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
|
||||||
|
"alloca() failed: %s", strerror(errno));
|
||||||
|
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
||||||
|
telnet->buffer, telnet->buffer_pos, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate strings in argument array */
|
||||||
|
for (i = 0, l = telnet->buffer; i != argc; ++i) {
|
||||||
|
c = l + 1;
|
||||||
|
while (c != telnet->buffer + telnet->buffer_pos &&
|
||||||
|
(unsigned)*c > 3)
|
||||||
|
++c;
|
||||||
|
argv2[i] = (char *)alloca(c - l + 1);
|
||||||
|
l = c;
|
||||||
|
/* FIXME: check failure */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* populate argument array */
|
||||||
|
for (i = 0, l = telnet->buffer; i != argc; ++i) {
|
||||||
|
c = l + 1;
|
||||||
|
while (c != telnet->buffer + telnet->buffer_pos &&
|
||||||
|
(unsigned)*c > 3)
|
||||||
|
++c;
|
||||||
|
memcpy(argv2[i], l, c - l);
|
||||||
|
argv2[i][c - l] = 0;
|
||||||
|
l = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* invoke event with our arguments */
|
||||||
|
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
||||||
|
telnet->buffer, telnet->buffer_pos, (const char **)argv2, argc);
|
||||||
|
break;
|
||||||
|
#endif /* HAVE_ALLOCA */
|
||||||
/* other generic subnegotiation */
|
/* other generic subnegotiation */
|
||||||
default:
|
default:
|
||||||
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
|
||||||
|
@ -542,7 +615,6 @@ static telnet_error_t _buffer_byte(telnet_t *telnet,
|
||||||
if (i >= _buffer_sizes_count - 1) {
|
if (i >= _buffer_sizes_count - 1) {
|
||||||
_error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
|
_error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
|
||||||
"subnegotiation buffer size limit reached");
|
"subnegotiation buffer size limit reached");
|
||||||
telnet_free(telnet);
|
|
||||||
return TELNET_EOVERFLOW;
|
return TELNET_EOVERFLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,7 +623,6 @@ static telnet_error_t _buffer_byte(telnet_t *telnet,
|
||||||
if (new_buffer == 0) {
|
if (new_buffer == 0) {
|
||||||
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
|
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
|
||||||
"realloc() failed");
|
"realloc() failed");
|
||||||
telnet_free(telnet);
|
|
||||||
return TELNET_ENOMEM;
|
return TELNET_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
libtelnet.h
15
libtelnet.h
|
@ -96,6 +96,21 @@ typedef struct telnet_telopt_t telnet_telopt_t;
|
||||||
|
|
||||||
#define TELNET_TELOPT_MCCP2 86
|
#define TELNET_TELOPT_MCCP2 86
|
||||||
|
|
||||||
|
/* special codes for the subnegotiation commands for certain telopts */
|
||||||
|
#define TELNET_TTYPE_IS 0
|
||||||
|
#define TELNET_TTYPE_SEND 1
|
||||||
|
|
||||||
|
#define TELNET_ENVIRON_IS 0
|
||||||
|
#define TELNET_ENVIRON_SEND 1
|
||||||
|
#define TELNET_ENVIRON_INFO 2
|
||||||
|
#define TELNET_ENVIRON_VAR 0
|
||||||
|
#define TELNET_ENVIRON_VALUE 1
|
||||||
|
#define TELNET_ENVIRON_ESC 2
|
||||||
|
#define TELNET_ENVIRON_USERVAR 3
|
||||||
|
|
||||||
|
#define TELNET_MSSP_VAR 1
|
||||||
|
#define TELNET_MSSP_VAL 2
|
||||||
|
|
||||||
/* libtelnet feature flags */
|
/* libtelnet feature flags */
|
||||||
#define TELNET_FLAG_PROXY (1<<0)
|
#define TELNET_FLAG_PROXY (1<<0)
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,9 @@ static int do_echo;
|
||||||
|
|
||||||
static const telnet_telopt_t telopts[] = {
|
static const telnet_telopt_t telopts[] = {
|
||||||
{ TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DO },
|
{ TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DO },
|
||||||
{ TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DO },
|
|
||||||
{ TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT },
|
{ TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT },
|
||||||
|
{ TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DO },
|
||||||
|
{ TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DO },
|
||||||
{ -1, 0, 0 }
|
{ -1, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -125,7 +126,7 @@ static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
|
||||||
buffer[0] = 0; /* IS code for RFC 1091 */
|
buffer[0] = 0; /* IS code for RFC 1091 */
|
||||||
snprintf(buffer + 1, sizeof(buffer) - 1, "%s", getenv("TERM"));
|
snprintf(buffer + 1, sizeof(buffer) - 1, "%s", getenv("TERM"));
|
||||||
telnet_subnegotiation(telnet, TELNET_TELOPT_TTYPE, buffer,
|
telnet_subnegotiation(telnet, TELNET_TELOPT_TTYPE, buffer,
|
||||||
1 + strlen(buffer + 1));
|
strlen(getenv("TERM")) + 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* error */
|
/* error */
|
||||||
|
|
|
@ -240,6 +240,19 @@ static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
|
||||||
print_buffer(ev->buffer, ev->size);
|
print_buffer(ev->buffer, ev->size);
|
||||||
printf(COLOR_NORMAL "\n");
|
printf(COLOR_NORMAL "\n");
|
||||||
}
|
}
|
||||||
|
} else if (ev->telopt == TELNET_TELOPT_TTYPE ||
|
||||||
|
ev->telopt == TELNET_TELOPT_ENVIRON ||
|
||||||
|
ev->telopt == TELNET_TELOPT_NEW_ENVIRON ||
|
||||||
|
ev->telopt == TELNET_TELOPT_MSSP) {
|
||||||
|
size_t i;
|
||||||
|
printf("%s %s [%zi parts]", conn->name, get_opt(ev->telopt),
|
||||||
|
ev->argc);
|
||||||
|
for (i = 0; i != ev->argc; ++i) {
|
||||||
|
printf(" \"");
|
||||||
|
print_buffer(ev->argv[i], strlen(ev->argv[i] + 1) + 1);
|
||||||
|
printf("\"");
|
||||||
|
}
|
||||||
|
printf(COLOR_NORMAL "\n");
|
||||||
} else {
|
} else {
|
||||||
printf("%s SUB %d (%s)", conn->name, (int)ev->telopt,
|
printf("%s SUB %d (%s)", conn->name, (int)ev->telopt,
|
||||||
get_opt(ev->telopt));
|
get_opt(ev->telopt));
|
||||||
|
|
Loading…
Reference in New Issue