require option table to fix negotiation "race" issie (experimental)
diff --git a/libtelnet.c b/libtelnet.c
index 1e9bcf8..4559385 100644
--- a/libtelnet.c
+++ b/libtelnet.c
@@ -54,9 +54,8 @@
static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
sizeof(_buffer_sizes[0]);
-/* event dispatch helper; return value is value of the accept field of the
- * event struct after dispatch; used for the funky REQUEST event */
-static INLINE int _event(telnet_t *telnet, telnet_event_type_t type,
+/* event dispatch helper */
+static INLINE void _event(telnet_t *telnet, telnet_event_type_t type,
unsigned char command, unsigned char telopt,
const char *buffer, size_t size) {
telnet_event_t ev;
@@ -65,11 +64,8 @@
ev.type = type;
ev.command = command;
ev.telopt = telopt;
- ev.accept = 0;
telnet->eh(telnet, &ev, telnet->ud);
-
- return ev.accept;
}
/* error generation function */
@@ -179,6 +175,27 @@
_event(telnet, TELNET_EV_SEND, 0, 0, buffer, size);
}
+/* check if we support a particular telopt; if us is non-zero, we
+ * check if we (local) supports it, otherwise we check if he (remote)
+ * supports it. return non-zero if supported, zero if not supported.
+ */
+static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt,
+ int us) {
+ int i;
+
+ /* if we have no telopts table, we obviously don't support it */
+ if (telnet->telopts == 0)
+ return 0;
+
+ /* loop unti found or end marker (us and him both 0) */
+ for (i = 0; telnet->telopts[i].telopt != -1; ++i)
+ if (telnet->telopts[i].telopt == telopt)
+ return us ? telnet->telopts[i].us : telnet->telopts[i].him;
+
+ /* not found, so not supported */
+ return 0;
+}
+
/* retrieve RFC1143 option state */
static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
unsigned char telopt) {
@@ -264,9 +281,10 @@
case TELNET_STATE_WILL:
switch (q.him) {
case Q_NO:
- if (_event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0) == 1) {
+ if (_check_telopt(telnet, telopt, 0)) {
_set_rfc1143(telnet, telopt, q.us, Q_YES);
_send_negotiate(telnet, TELNET_DO, telopt);
+ _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
} else
_send_negotiate(telnet, TELNET_DONT, telopt);
break;
@@ -321,9 +339,10 @@
case TELNET_STATE_DO:
switch (q.us) {
case Q_NO:
- if (_event(telnet, TELNET_EV_DO, 0, telopt, 0, 0) == 1) {
+ if (_check_telopt(telnet, telopt, 1)) {
_set_rfc1143(telnet, telopt, Q_YES, q.him);
_send_negotiate(telnet, TELNET_WILL, telopt);
+ _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
} else
_send_negotiate(telnet, TELNET_WONT, telopt);
break;
@@ -377,10 +396,11 @@
}
/* initialize a telnet state tracker */
-void telnet_init(telnet_t *telnet, telnet_event_handler_t eh,
- unsigned char flags, void *user_data) {
+void telnet_init(telnet_t *telnet, const telnet_telopt_t *telopts,
+ telnet_event_handler_t eh, unsigned char flags, void *user_data) {
memset(telnet, 0, sizeof(telnet_t));
telnet->ud = user_data;
+ telnet->telopts = telopts;
telnet->eh = eh;
telnet->flags = flags;
}
@@ -775,7 +795,7 @@
}
/* send subnegotiation header */
-void telnet_begin_subnegotiation(telnet_t *telnet, unsigned char telopt) {
+void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
_send(telnet, sb, 3);
}