Merge branch 'master' into rfc1143
diff --git a/README b/README
index bcf98fe..f6b234d 100644
--- a/README
+++ b/README
@@ -222,20 +222,76 @@
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
@@ -343,11 +399,9 @@
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
diff --git a/libtelnet.c b/libtelnet.c
index 8b07f6c..1860ece 100644
--- a/libtelnet.c
+++ b/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_count = sizeof(_buffer_sizes) /
sizeof(_buffer_sizes[0]);
-/* event dispatch helper */
-static void _event(libtelnet_t *telnet, libtelnet_event_type_t type,
+/* 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(libtelnet_t *telnet, libtelnet_event_type_t type,
unsigned char command, unsigned char telopt, unsigned char *buffer,
unsigned int size) {
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 */
@@ -109,6 +125,182 @@
return LIBTELNET_EOK;
}
+/* 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->flags & LIBTELNET_FLAG_PROXY) {
+ switch (cmd) {
+ case LIBTELNET_WILL:
+ _event(telnet, LIBTELNET_EV_WILL, 0, telopt, 0, 0);
+ break;
+ case LIBTELNET_WONT:
+ _event(telnet, LIBTELNET_EV_WONT, 0, telopt, 0, 0);
+ break;
+ case LIBTELNET_DO:
+ _event(telnet, LIBTELNET_EV_DO, 0, telopt, 0, 0);
+ break;
+ case LIBTELNET_DONT:
+ _event(telnet, LIBTELNET_EV_DONT, 0, 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(libtelnet_t *telnet, libtelnet_event_handler_t eh,
unsigned char flags, void *user_data) {
@@ -124,8 +316,8 @@
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 */
@@ -137,6 +329,13 @@
free(telnet->z);
telnet->z = 0;
}
+
+ /* free RFC1143 queue */
+ if (telnet->q) {
+ free(telnet->q);
+ telnet->q = 0;
+ telnet->q_size = 0;
+ }
}
/* push a byte into the telnet buffer */
@@ -146,10 +345,10 @@
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;
}
@@ -172,11 +371,11 @@
}
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;
}
@@ -235,22 +434,22 @@
/* 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;
@@ -258,7 +457,7 @@
/* 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;
@@ -285,7 +484,7 @@
/* 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
@@ -517,3 +716,29 @@
_event(telnet, LIBTELNET_EV_SEND, 0, 0, compress2, sizeof(compress2));
#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;
+}
diff --git a/libtelnet.h b/libtelnet.h
index f926320..2ac577b 100644
--- a/libtelnet.h
+++ b/libtelnet.h
@@ -122,7 +122,10 @@
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,
@@ -131,15 +134,23 @@
/* 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 */
@@ -156,18 +167,22 @@
/* zlib (mccp2) compression */
z_stream *z;
#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;
/* option flags */
unsigned char flags;
/* current subnegotiation telopt */
unsigned char sb_telopt;
+ /* length of RFC1143 queue */
+ unsigned char q_size;
};
/* initialize a telnet state tracker */
@@ -199,4 +214,12 @@
/* begin sending compressed data (server only) */
extern void libtelnet_begin_compress2(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) */
diff --git a/telnet-client.c b/telnet-client.c
index c79977b..ef1b8e6 100644
--- a/telnet-client.c
+++ b/telnet-client.c
@@ -89,55 +89,32 @@
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 */
diff --git a/telnet-proxy.c b/telnet-proxy.c
index 078806d..45480d8 100644
--- a/telnet-proxy.c
+++ b/telnet-proxy.c
@@ -191,12 +191,31 @@
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 */