mirror of https://gerrit.osmocom.org/libtelnet
partial RFC1143 implementation; only reacts to negotiation, cannot handle initiating it yet
This commit is contained in:
parent
e7c442681a
commit
5b5bc92bc0
82
README
82
README
|
@ -226,20 +226,76 @@ IId. Event Handling
|
|||
The necessary processing depends on the specific commands; see
|
||||
the TELNET RFC for more information.
|
||||
|
||||
LIBTELNET_EV_NEGOTIATE:
|
||||
The NEGOTIATE event is sent when a TELNET neogitiation command
|
||||
is received.
|
||||
LIBTELNET_EV_WILL:
|
||||
LIBTELNET_EV_DO:
|
||||
The WILL and DO events are sent when a TELNET negotiation
|
||||
command of the same name is received.
|
||||
|
||||
The event->command value will be one of LIBTELNET_WILL,
|
||||
LIBTELNET_WONT, LIBTELNET_DO, or LIBTELNET_DONT. The
|
||||
event->telopt value will contain the option value being
|
||||
negotiated.
|
||||
WILL events are sent by the remote end when they wish to be
|
||||
allowed to turn an option on on their end, or in confirmation
|
||||
after you have sent a DO command to them.
|
||||
|
||||
libtelnet does not currently manage negotiation for you. For
|
||||
best practice in implementing TELNET negotiation, see:
|
||||
DO events are sent by the remote end when they wish for you
|
||||
to turn on an option on your end, or in confirmation after you
|
||||
have sent a WILL command to them.
|
||||
|
||||
In either case, the TELNET option under negotiation will be in
|
||||
event->telopt field.
|
||||
|
||||
If you support the option and wish for it to be enabled you
|
||||
must set the event->accept field to 1, unless this event is
|
||||
a confirmation for a previous WILL/DO command you sent to the
|
||||
remote end. If you do not set event->field to 1 then
|
||||
libtelnet will send a rejection command back to the other end.
|
||||
|
||||
libtelnet manages some of the pecularities of negotiation for
|
||||
you. For information on libtelnet's negotiation method, see:
|
||||
|
||||
http://www.faqs.org/rfcs/rfc1143.html
|
||||
|
||||
Examples:
|
||||
|
||||
You want remote end to use TTYPE, so you send DO TTYPE.
|
||||
Remote accepts and sends WILL TTYPE.
|
||||
|
||||
Remote end wants you to use SGA, so they send DO_SGA.
|
||||
You do not support SGA and set event->accept = 0.
|
||||
|
||||
Remote end wants to use ZMP, so they send WILL ZMP.
|
||||
You support ZMP, so you set event->accept = 1 and enable
|
||||
local ZMP support.
|
||||
|
||||
You want to use MCCP2, so you send WILL COMPRESS2.
|
||||
Remote end accepts and sends DO COMPRESS2.
|
||||
|
||||
Note that in PROXY mode libtelnet will do no processing of its
|
||||
own for you.
|
||||
|
||||
LIBTELNET_EV_WONT:
|
||||
LIBTELNET_EV_DONT:
|
||||
The WONT and DONT events are sent when the remote end of the
|
||||
connection wishes to disable an option, when they are
|
||||
refusing to a support an option that you have asked for, or
|
||||
in confirmation of an option you have asked to be disabled.
|
||||
|
||||
Most commonly WONT and DONT events are sent as rejections of
|
||||
features you requested by sending DO or WILL events. Receiving
|
||||
these events means the TELNET option is not or will not be
|
||||
supported by the remote end, so give up.
|
||||
|
||||
Sometimes WONT or DONT will be sent for TELNET options that are
|
||||
already enabled, but the remote end wishes to stop using. You
|
||||
cannot decline. These events are demands that must be complied
|
||||
with. libtelnet will always send the appropriate response back
|
||||
without consulting your application. These events are sent to
|
||||
allow your application to disable its own use of the features.
|
||||
|
||||
In either case, the TELNET option under negotiation will be in
|
||||
event->telopt field.
|
||||
|
||||
Note that in PROXY mode libtelnet will do no processing of its
|
||||
own for you.
|
||||
|
||||
LIBTELNET_EV_SUBNEGOTIATION:
|
||||
Triggered whenever a TELNET sub-negotiation has been received.
|
||||
Sub-negotiations include the NAWS option for communicating
|
||||
|
@ -347,11 +403,9 @@ option using libtelnet_send_negotiate(), for example:
|
|||
libtelnet_send_negotiate(&telnet, LIBTELNET_WILL,
|
||||
LIBTELNET_OPTION_COMPRESS2, user_data);
|
||||
|
||||
If a favorable DO COMPRESS2 is sent back from the client (processed
|
||||
in a LIBTELNET_EV_NEGOTIATE event, with event->command equal to
|
||||
LIBTELNET_DO and event->telopt equal to LIBTELNET_TELOPT_COMPRESS2),
|
||||
then the server application can begin compression at any time by
|
||||
calling libtelnet_begin_compress2().
|
||||
If a favorable DO COMPRESS2 is sent back from the client then the
|
||||
server application can begin compression at any time by calling
|
||||
libtelnet_begin_compress2().
|
||||
|
||||
If a connection is in PROXY mode and COMPRESS2 support is enabled
|
||||
then libtelnet will automatically detect the start of a COMPRESS2
|
||||
|
|
263
libtelnet.c
263
libtelnet.c
|
@ -22,6 +22,18 @@
|
|||
|
||||
#include "libtelnet.h"
|
||||
|
||||
/* RFC1143 state names */
|
||||
#define RFC1143_NO 0x00
|
||||
#define RFC1143_YES 0x01
|
||||
|
||||
#define RFC1143_WANT 0x02
|
||||
#define RFC1143_OP 0x04
|
||||
|
||||
#define RFC1143_WANTNO (RFC1143_WANT|RFC1143_YES)
|
||||
#define RFC1143_WANTYES (RFC1143_WANT|RFC1143_NO)
|
||||
#define RFC1143_WANTNO_OP (RFC1143_WANTNO|RFC1143_OP)
|
||||
#define RFC1143_WANTYES_OP (RFC1143_WANTYES|RFC1143_OP)
|
||||
|
||||
/* buffer sizes */
|
||||
static const unsigned int _buffer_sizes[] = {
|
||||
0,
|
||||
|
@ -33,18 +45,22 @@ static const unsigned int _buffer_sizes[] = {
|
|||
static const unsigned int _buffer_sizes_count =
|
||||
sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]);
|
||||
|
||||
/* event dispatch helper */
|
||||
static void _event(struct libtelnet_t *telnet,
|
||||
/* event dispatch helper; return value is value of the accept field of the
|
||||
* event struct after dispatch; used for the funky REQUEST event */
|
||||
static int _event(struct libtelnet_t *telnet,
|
||||
enum libtelnet_event_type_t type, unsigned char command,
|
||||
unsigned char telopt, unsigned char *buffer, unsigned int size) {
|
||||
struct libtelnet_event_t ev;
|
||||
ev.buffer = buffer;
|
||||
ev.size = size;
|
||||
ev.type = type;
|
||||
ev.command = command;
|
||||
ev.telopt = telopt;
|
||||
ev.buffer = buffer;
|
||||
ev.size = size;
|
||||
ev.accept = 0;
|
||||
|
||||
telnet->eh(telnet, &ev, telnet->ud);
|
||||
|
||||
return ev.accept;
|
||||
}
|
||||
|
||||
/* error generation function */
|
||||
|
@ -102,6 +118,182 @@ z_stream *_init_zlib(struct libtelnet_t *telnet, int deflate, int err_fatal) {
|
|||
return zlib;
|
||||
}
|
||||
|
||||
/* negotiation handling magic */
|
||||
static void _negotiate(struct libtelnet_t *telnet, unsigned char cmd,
|
||||
unsigned char telopt) {
|
||||
struct libtelnet_rfc1143_t *qtmp;
|
||||
int q;
|
||||
|
||||
/* in PROXY mode, just pass it thru and do nothing */
|
||||
if (telnet->mode == LIBTELNET_MODE_PROXY) {
|
||||
switch (cmd) {
|
||||
case LIBTELNET_WILL:
|
||||
_event(telnet, LIBTELNET_EV_WILL, cmd, telopt, 0, 0);
|
||||
break;
|
||||
case LIBTELNET_WONT:
|
||||
_event(telnet, LIBTELNET_EV_WONT, cmd, telopt, 0, 0);
|
||||
break;
|
||||
case LIBTELNET_DO:
|
||||
_event(telnet, LIBTELNET_EV_DO, cmd, telopt, 0, 0);
|
||||
break;
|
||||
case LIBTELNET_DONT:
|
||||
_event(telnet, LIBTELNET_EV_DONT, cmd, telopt, 0, 0);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* lookup the current state of the option */
|
||||
for (q = 0; q != telnet->q_size; ++q) {
|
||||
if (telnet->q[q].telopt == telopt)
|
||||
break;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
if (q == telnet->q_size) {
|
||||
/* if the option is unfound then it is off on both ends... and there
|
||||
* is no need thus to respond to a WONT/DONT */
|
||||
if (cmd == LIBTELNET_WONT || cmd == LIBTELNET_DONT)
|
||||
return;
|
||||
|
||||
/* we're going to need to track state for it, so grow the queue
|
||||
* and put the telopt into it; bail on allocation error
|
||||
*/
|
||||
if ((qtmp = (struct libtelnet_rfc1143_t *)malloc(sizeof(
|
||||
struct libtelnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, 0,
|
||||
"malloc() failed: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
telnet->q = qtmp;
|
||||
memset(&telnet->q[telnet->q_size], 0,
|
||||
sizeof(struct libtelnet_rfc1143_t));
|
||||
telnet->q[telnet->q_size].telopt = telopt;
|
||||
q = telnet->q_size;
|
||||
++telnet->q_size;
|
||||
}
|
||||
|
||||
/* start processing... */
|
||||
switch (cmd) {
|
||||
/* request to enable option on remote end or confirm DO */
|
||||
case LIBTELNET_WILL:
|
||||
switch (telnet->q[q].him) {
|
||||
case RFC1143_NO:
|
||||
if (_event(telnet, LIBTELNET_EV_WILL, cmd, telopt, 0, 0) == 1) {
|
||||
telnet->q[q].him = RFC1143_YES;
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_DO, telopt);
|
||||
} else
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_DONT, telopt);
|
||||
break;
|
||||
case RFC1143_YES:
|
||||
break;
|
||||
case RFC1143_WANTNO:
|
||||
telnet->q[q].him = RFC1143_NO;
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
|
||||
"DONT answered by WILL");
|
||||
break;
|
||||
case RFC1143_WANTNO_OP:
|
||||
telnet->q[q].him = RFC1143_YES;
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
|
||||
"DONT answered by WILL");
|
||||
break;
|
||||
case RFC1143_WANTYES:
|
||||
telnet->q[q].him = RFC1143_YES;
|
||||
break;
|
||||
case RFC1143_WANTYES_OP:
|
||||
telnet->q[q].him = RFC1143_WANTNO;
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_DONT, telopt);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
/* request to disable option on remote end, confirm DONT, reject DO */
|
||||
case LIBTELNET_WONT:
|
||||
switch (telnet->q[q].him) {
|
||||
case RFC1143_NO:
|
||||
break;
|
||||
case RFC1143_YES:
|
||||
telnet->q[q].him = RFC1143_NO;
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_DONT, telopt);
|
||||
_event(telnet, LIBTELNET_EV_WONT, 0, telopt,
|
||||
0, 0);
|
||||
break;
|
||||
case RFC1143_WANTNO:
|
||||
telnet->q[q].him = RFC1143_NO;
|
||||
_event(telnet, LIBTELNET_EV_WONT, 0, telopt,
|
||||
0, 0);
|
||||
break;
|
||||
case RFC1143_WANTNO_OP:
|
||||
telnet->q[q].him = RFC1143_WANTYES;
|
||||
_event(telnet, LIBTELNET_EV_DO, 0, telopt,
|
||||
0, 0);
|
||||
break;
|
||||
case RFC1143_WANTYES:
|
||||
case RFC1143_WANTYES_OP:
|
||||
telnet->q[q].him = RFC1143_NO;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
/* request to enable option on local end or confirm WILL */
|
||||
case LIBTELNET_DO:
|
||||
switch (telnet->q[q].us) {
|
||||
case RFC1143_NO:
|
||||
if (_event(telnet, LIBTELNET_EV_DO, cmd, telopt, 0, 0) == 1) {
|
||||
telnet->q[q].us = RFC1143_YES;
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_WILL, telopt);
|
||||
} else
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_WONT, telopt);
|
||||
break;
|
||||
case RFC1143_YES:
|
||||
break;
|
||||
case RFC1143_WANTNO:
|
||||
telnet->q[q].us = RFC1143_NO;
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
|
||||
"WONT answered by DO");
|
||||
break;
|
||||
case RFC1143_WANTNO_OP:
|
||||
telnet->q[q].us = RFC1143_YES;
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
|
||||
"WONT answered by DO");
|
||||
break;
|
||||
case RFC1143_WANTYES:
|
||||
telnet->q[q].us = RFC1143_YES;
|
||||
break;
|
||||
case RFC1143_WANTYES_OP:
|
||||
telnet->q[q].us = RFC1143_WANTNO;
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_WONT, telopt);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
/* request to disable option on local end, confirm WONT, reject WILL */
|
||||
case LIBTELNET_DONT:
|
||||
switch (telnet->q[q].us) {
|
||||
case RFC1143_NO:
|
||||
break;
|
||||
case RFC1143_YES:
|
||||
telnet->q[q].us = RFC1143_NO;
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_WONT, telopt);
|
||||
_event(telnet, LIBTELNET_EV_DONT, 0, telopt, 0, 0);
|
||||
break;
|
||||
case RFC1143_WANTNO:
|
||||
telnet->q[q].us = RFC1143_NO;
|
||||
_event(telnet, LIBTELNET_EV_WONT, 0, telopt, 0, 0);
|
||||
break;
|
||||
case RFC1143_WANTNO_OP:
|
||||
telnet->q[q].us = RFC1143_WANTYES;
|
||||
_event(telnet, LIBTELNET_EV_WILL, 0, telopt, 0, 0);
|
||||
break;
|
||||
case RFC1143_WANTYES:
|
||||
case RFC1143_WANTYES_OP:
|
||||
telnet->q[q].us = RFC1143_NO;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize a telnet state tracker */
|
||||
void libtelnet_init(struct libtelnet_t *telnet, libtelnet_event_handler_t eh,
|
||||
enum libtelnet_mode_t mode, void *user_data) {
|
||||
|
@ -117,8 +309,8 @@ void libtelnet_free(struct libtelnet_t *telnet) {
|
|||
if (telnet->buffer != 0) {
|
||||
free(telnet->buffer);
|
||||
telnet->buffer = 0;
|
||||
telnet->size = 0;
|
||||
telnet->length = 0;
|
||||
telnet->buffer_size = 0;
|
||||
telnet->buffer_pos = 0;
|
||||
}
|
||||
|
||||
/* free zlib box(es) */
|
||||
|
@ -132,6 +324,13 @@ void libtelnet_free(struct libtelnet_t *telnet) {
|
|||
free(telnet->z_deflate);
|
||||
telnet->z_deflate = 0;
|
||||
}
|
||||
|
||||
/* free RFC1143 queue */
|
||||
if (telnet->q) {
|
||||
free(telnet->q);
|
||||
telnet->q = 0;
|
||||
telnet->q_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* push a byte into the telnet buffer */
|
||||
|
@ -141,10 +340,10 @@ static enum libtelnet_error_t _buffer_byte(struct libtelnet_t *telnet,
|
|||
int i;
|
||||
|
||||
/* check if we're out of room */
|
||||
if (telnet->length == telnet->size) {
|
||||
if (telnet->buffer_pos == telnet->buffer_size) {
|
||||
/* find the next buffer size */
|
||||
for (i = 0; i != _buffer_sizes_count; ++i) {
|
||||
if (_buffer_sizes[i] == telnet->size)
|
||||
if (_buffer_sizes[i] == telnet->buffer_size)
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -167,11 +366,11 @@ static enum libtelnet_error_t _buffer_byte(struct libtelnet_t *telnet,
|
|||
}
|
||||
|
||||
telnet->buffer = new_buffer;
|
||||
telnet->size = _buffer_sizes[i + 1];
|
||||
telnet->buffer_size = _buffer_sizes[i + 1];
|
||||
}
|
||||
|
||||
/* push the byte, all set */
|
||||
telnet->buffer[telnet->length++] = byte;
|
||||
telnet->buffer[telnet->buffer_pos++] = byte;
|
||||
return LIBTELNET_EOK;
|
||||
}
|
||||
|
||||
|
@ -230,22 +429,22 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
|
||||
/* negotiation commands */
|
||||
case LIBTELNET_STATE_DO:
|
||||
_event(telnet, LIBTELNET_EV_NEGOTIATE, LIBTELNET_DO, byte, 0, 0);
|
||||
_negotiate(telnet, LIBTELNET_DO, byte);
|
||||
start = i + 1;
|
||||
telnet->state = LIBTELNET_STATE_DATA;
|
||||
break;
|
||||
case LIBTELNET_STATE_DONT:
|
||||
_event(telnet, LIBTELNET_EV_NEGOTIATE, LIBTELNET_DONT, byte, 0, 0);
|
||||
_negotiate(telnet, LIBTELNET_DONT, byte);
|
||||
start = i + 1;
|
||||
telnet->state = LIBTELNET_STATE_DATA;
|
||||
break;
|
||||
case LIBTELNET_STATE_WILL:
|
||||
_event(telnet, LIBTELNET_EV_NEGOTIATE, LIBTELNET_WILL, byte, 0, 0);
|
||||
_negotiate(telnet, LIBTELNET_WILL, byte);
|
||||
start = i + 1;
|
||||
telnet->state = LIBTELNET_STATE_DATA;
|
||||
break;
|
||||
case LIBTELNET_STATE_WONT:
|
||||
_event(telnet, LIBTELNET_EV_NEGOTIATE, LIBTELNET_WONT, byte, 0, 0);
|
||||
_negotiate(telnet, LIBTELNET_WONT, byte);
|
||||
start = i + 1;
|
||||
telnet->state = LIBTELNET_STATE_DATA;
|
||||
break;
|
||||
|
@ -253,7 +452,7 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
/* subnegotiation -- determine subnegotiation telopt */
|
||||
case LIBTELNET_STATE_SB:
|
||||
telnet->sb_telopt = byte;
|
||||
telnet->length = 0;
|
||||
telnet->buffer_pos = 0;
|
||||
telnet->state = LIBTELNET_STATE_SB_DATA;
|
||||
break;
|
||||
|
||||
|
@ -280,7 +479,7 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
|
||||
/* invoke callback */
|
||||
_event(telnet, LIBTELNET_EV_SUBNEGOTIATION, 0,
|
||||
telnet->sb_telopt, telnet->buffer, telnet->length);
|
||||
telnet->sb_telopt, telnet->buffer, telnet->buffer_pos);
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
/* if we are a client or a proxy and just received the
|
||||
|
@ -355,7 +554,8 @@ void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
telnet->z_inflate->avail_out = sizeof(inflate_buffer);
|
||||
|
||||
/* inflate until buffer exhausted and all output is produced */
|
||||
while (telnet->z_inflate->avail_in > 0 || telnet->z_inflate->avail_out == 0) {
|
||||
while (telnet->z_inflate->avail_in > 0 ||
|
||||
telnet->z_inflate->avail_out == 0) {
|
||||
/* reset output buffer */
|
||||
|
||||
/* decompress */
|
||||
|
@ -405,7 +605,8 @@ static void _send(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
telnet->z_deflate->avail_out = sizeof(deflate_buffer);
|
||||
|
||||
/* deflate until buffer exhausted and all output is produced */
|
||||
while (telnet->z_deflate->avail_in > 0 || telnet->z_deflate->avail_out == 0) {
|
||||
while (telnet->z_deflate->avail_in > 0 ||
|
||||
telnet->z_deflate->avail_out == 0) {
|
||||
/* compress */
|
||||
if ((rs = deflate(telnet->z_deflate, Z_SYNC_FLUSH)) != Z_OK) {
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, 1,
|
||||
|
@ -519,3 +720,29 @@ void libtelnet_begin_compress2(struct libtelnet_t *telnet) {
|
|||
telnet->z_deflate = zlib;
|
||||
#endif /* HAVE_ZLIB */
|
||||
}
|
||||
|
||||
/* get local state of specific telopt */
|
||||
int libtelnet_get_telopt_local (struct libtelnet_t *telnet,
|
||||
unsigned char telopt) {
|
||||
int i;
|
||||
/* search for entry, return state if found */
|
||||
for (i = 0; i != telnet->q_size; ++i)
|
||||
if (telnet->q[i].telopt == telopt)
|
||||
return telnet->q[i].us & RFC1143_YES;
|
||||
|
||||
/* not found... so it's off */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get remote state of specific telopt */
|
||||
int libtelnet_get_telopt_remote (struct libtelnet_t *telnet,
|
||||
unsigned char telopt) {
|
||||
int i;
|
||||
/* search for entry, return state if found */
|
||||
for (i = 0; i != telnet->q_size; ++i)
|
||||
if (telnet->q[i].telopt == telopt)
|
||||
return telnet->q[i].him & RFC1143_YES;
|
||||
|
||||
/* not found... so it's off */
|
||||
return 0;
|
||||
}
|
||||
|
|
43
libtelnet.h
43
libtelnet.h
|
@ -120,7 +120,10 @@ enum libtelnet_event_type_t {
|
|||
LIBTELNET_EV_DATA = 0,
|
||||
LIBTELNET_EV_SEND,
|
||||
LIBTELNET_EV_IAC,
|
||||
LIBTELNET_EV_NEGOTIATE,
|
||||
LIBTELNET_EV_WILL,
|
||||
LIBTELNET_EV_WONT,
|
||||
LIBTELNET_EV_DO,
|
||||
LIBTELNET_EV_DONT,
|
||||
LIBTELNET_EV_SUBNEGOTIATION,
|
||||
LIBTELNET_EV_COMPRESS,
|
||||
LIBTELNET_EV_WARNING,
|
||||
|
@ -129,15 +132,23 @@ enum libtelnet_event_type_t {
|
|||
|
||||
/* event information */
|
||||
struct libtelnet_event_t {
|
||||
/* type of event */
|
||||
enum libtelnet_event_type_t type;
|
||||
/* command info: only for IAC event */
|
||||
unsigned char command;
|
||||
/* telopt info: for NEGOTIATE and SUBNEGOTIATION events */
|
||||
unsigned char telopt;
|
||||
/* data buffer: for DATA, SEND, SUBNEGOTIATION, and ERROR events */
|
||||
unsigned char *buffer;
|
||||
unsigned int size;
|
||||
/* type of event */
|
||||
enum libtelnet_event_type_t type;
|
||||
/* IAC command */
|
||||
unsigned char command;
|
||||
/* telopt info: for negotiation events SUBNEGOTIATION */
|
||||
unsigned char telopt;
|
||||
/* accept status: for WILL and DO events */
|
||||
unsigned char accept;
|
||||
};
|
||||
|
||||
/* option negotiation state (RFC 1143) */
|
||||
struct libtelnet_rfc1143_t {
|
||||
unsigned char telopt;
|
||||
char us:4, him:4;
|
||||
};
|
||||
|
||||
/* event handler declaration */
|
||||
|
@ -155,18 +166,22 @@ struct libtelnet_t {
|
|||
z_stream *z_deflate;
|
||||
z_stream *z_inflate;
|
||||
#endif
|
||||
/* RFC1143 option negotiation states */
|
||||
struct libtelnet_rfc1143_t *q;
|
||||
/* sub-request buffer */
|
||||
unsigned char *buffer;
|
||||
/* current size of the buffer */
|
||||
unsigned int size;
|
||||
/* length of data in the buffer */
|
||||
unsigned int length;
|
||||
unsigned int buffer_size;
|
||||
/* current buffer write position (also length of buffer data) */
|
||||
unsigned int buffer_pos;
|
||||
/* current state */
|
||||
enum libtelnet_state_t state;
|
||||
/* processing mode */
|
||||
enum libtelnet_mode_t mode;
|
||||
/* current subnegotiation telopt */
|
||||
unsigned char sb_telopt;
|
||||
/* length of RFC1143 queue */
|
||||
unsigned char q_size;
|
||||
};
|
||||
|
||||
/* initialize a telnet state tracker */
|
||||
|
@ -200,4 +215,12 @@ extern void libtelnet_send_subnegotiation(struct libtelnet_t *telnet,
|
|||
/* begin sending compressed data (server only) */
|
||||
extern void libtelnet_begin_compress2(struct libtelnet_t *telnet);
|
||||
|
||||
/* return the status of a specific TELNET option on our end (US) */
|
||||
extern int libtelnet_get_telopt_local(struct libtelnet_t *telnet,
|
||||
unsigned char telopt);
|
||||
|
||||
/* return the status of a specific TELNET option on remote end (HIM) */
|
||||
extern int libtelnet_get_telopt_remote(struct libtelnet_t *telnet,
|
||||
unsigned char telopt);
|
||||
|
||||
#endif /* !defined(LIBTELNET_INCLUDE) */
|
||||
|
|
|
@ -89,55 +89,32 @@ static void _event_handler(struct libtelnet_t *telnet,
|
|||
case LIBTELNET_EV_SEND:
|
||||
_send(sock, ev->buffer, ev->size);
|
||||
break;
|
||||
/* accept any options we want */
|
||||
case LIBTELNET_EV_NEGOTIATE:
|
||||
switch (ev->command) {
|
||||
case LIBTELNET_WILL:
|
||||
switch (ev->telopt) {
|
||||
/* accept request to enable compression */
|
||||
case LIBTELNET_TELOPT_COMPRESS2:
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_DO, ev->telopt);
|
||||
break;
|
||||
/* server "promises" to echo, so turn off local echo */
|
||||
case LIBTELNET_TELOPT_ECHO:
|
||||
do_echo = 0;
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_DO, ev->telopt);
|
||||
break;
|
||||
/* unknown -- reject */
|
||||
default:
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_DONT, ev->telopt);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* request to enable remote feature (or receipt) */
|
||||
case LIBTELNET_EV_WILL:
|
||||
/* we accept COMPRESS2 (MCCP) */
|
||||
if (ev->telopt == LIBTELNET_TELOPT_COMPRESS2)
|
||||
ev->accept = 1;
|
||||
|
||||
case LIBTELNET_WONT:
|
||||
switch (ev->telopt) {
|
||||
/* server wants us to do echoing, by telling us it won't */
|
||||
case LIBTELNET_TELOPT_ECHO:
|
||||
do_echo = 1;
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_DONT, ev->telopt);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LIBTELNET_DO:
|
||||
switch (ev->telopt) {
|
||||
/* accept request to enable terminal-type requests */
|
||||
case LIBTELNET_TELOPT_TTYPE:
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_WILL, ev->telopt);
|
||||
break;
|
||||
/* unknown - reject */
|
||||
default:
|
||||
libtelnet_send_negotiate(telnet, LIBTELNET_WONT, ev->telopt);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LIBTELNET_DONT:
|
||||
/* ignore for now */
|
||||
break;
|
||||
/* we'll agree to turn off our echo if server wants us to stop */
|
||||
else if (ev->telopt == LIBTELNET_TELOPT_ECHO) {
|
||||
do_echo = 0;
|
||||
ev->accept = 1;
|
||||
}
|
||||
break;
|
||||
/* notification of disabling remote feature (or receipt) */
|
||||
case LIBTELNET_EV_WONT:
|
||||
if (ev->telopt == LIBTELNET_TELOPT_ECHO)
|
||||
do_echo = 1;
|
||||
break;
|
||||
/* request to enable local feature (or receipt) */
|
||||
case LIBTELNET_EV_DO:
|
||||
/* we support the TTYPE option */
|
||||
if (ev->telopt == LIBTELNET_TELOPT_TTYPE)
|
||||
ev->accept = 1;
|
||||
break;
|
||||
/* demand to disable local feature (or receipt) */
|
||||
case LIBTELNET_EV_DONT:
|
||||
break;
|
||||
/* respond to particular subnegotiations */
|
||||
case LIBTELNET_EV_SUBNEGOTIATION:
|
||||
/* respond with our terminal type */
|
||||
|
|
|
@ -191,12 +191,31 @@ static void _event_handler(struct libtelnet_t *telnet,
|
|||
|
||||
libtelnet_send_command(&conn->remote->telnet, ev->command);
|
||||
break;
|
||||
/* negotiation */
|
||||
case LIBTELNET_EV_NEGOTIATE:
|
||||
printf("%s IAC %s %d (%s)" COLOR_NORMAL "\n", conn->name,
|
||||
get_cmd(ev->command), (int)ev->telopt, get_opt(ev->telopt));
|
||||
|
||||
libtelnet_send_negotiate(&conn->remote->telnet, ev->command,
|
||||
/* negotiation, WILL */
|
||||
case LIBTELNET_EV_WILL:
|
||||
printf("%s IAC WILL %d (%s)" COLOR_NORMAL "\n", conn->name,
|
||||
(int)ev->telopt, get_opt(ev->telopt));
|
||||
libtelnet_send_negotiate(&conn->remote->telnet, LIBTELNET_WILL,
|
||||
ev->telopt);
|
||||
break;
|
||||
/* negotiation, WONT */
|
||||
case LIBTELNET_EV_WONT:
|
||||
printf("%s IAC WONT %d (%s)" COLOR_NORMAL "\n", conn->name,
|
||||
(int)ev->telopt, get_opt(ev->telopt));
|
||||
libtelnet_send_negotiate(&conn->remote->telnet, LIBTELNET_WONT,
|
||||
ev->telopt);
|
||||
break;
|
||||
/* negotiation, DO */
|
||||
case LIBTELNET_EV_DO:
|
||||
printf("%s IAC DO %d (%s)" COLOR_NORMAL "\n", conn->name,
|
||||
(int)ev->telopt, get_opt(ev->telopt));
|
||||
libtelnet_send_negotiate(&conn->remote->telnet, LIBTELNET_DO,
|
||||
ev->telopt);
|
||||
break;
|
||||
case LIBTELNET_EV_DONT:
|
||||
printf("%s IAC DONT %d (%s)" COLOR_NORMAL "\n", conn->name,
|
||||
(int)ev->telopt, get_opt(ev->telopt));
|
||||
libtelnet_send_negotiate(&conn->remote->telnet, LIBTELNET_DONT,
|
||||
ev->telopt);
|
||||
break;
|
||||
/* subnegotiation */
|
||||
|
|
Loading…
Reference in New Issue