support ttype/environ/new-environ/mssp parsing (using alloca; fixme?)

This commit is contained in:
Sean Middleditch 2009-03-22 22:21:42 -04:00
parent 1e80f52f80
commit 2d5c499c36
5 changed files with 116 additions and 16 deletions

View File

@ -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

View File

@ -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;
} }

View File

@ -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)

View File

@ -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 */

View File

@ -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));