From 34bb0998c7f3a8d01d7d3c5e248cddf8547c4473 Mon Sep 17 00:00:00 2001 From: Sean Middleditch Date: Sat, 21 Mar 2009 00:20:44 -0400 Subject: [PATCH] require option table to fix negotiation "race" issie (experimental) --- libtelnet.c | 42 +++++++++++++++++++++++++++++++----------- libtelnet.h | 15 +++++++++++---- telnet-chatd.c | 2 +- telnet-client.c | 20 +++++++++----------- telnet-proxy.c | 4 ++-- 5 files changed, 54 insertions(+), 29 deletions(-) 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[] = { 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 @@ static INLINE int _event(telnet_t *telnet, telnet_event_type_t type, 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 @@ static void _send(telnet_t *telnet, const char *buffer, _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 @@ static void _negotiate(telnet_t *telnet, unsigned char telopt) { 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 @@ static void _negotiate(telnet_t *telnet, unsigned char telopt) { 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 @@ static void _negotiate(telnet_t *telnet, unsigned char telopt) { } /* 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 @@ void telnet_send(telnet_t *telnet, const char *buffer, } /* 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); } diff --git a/libtelnet.h b/libtelnet.h index 4ea72c5..c9f2583 100644 --- a/libtelnet.h +++ b/libtelnet.h @@ -15,6 +15,7 @@ /* forward declarations */ typedef struct telnet_t telnet_t; typedef struct telnet_event_t telnet_event_t; +typedef struct telnet_telopt_t telnet_telopt_t; /* telnet special values */ #define TELNET_IAC 255 @@ -144,18 +145,24 @@ struct telnet_event_t { unsigned char command; /* telopt info: for negotiation events SUBNEGOTIATION */ unsigned char telopt; - /* accept status: for WILL and DO events */ - unsigned char accept; }; /* event handler declaration */ typedef void (*telnet_event_handler_t)(telnet_t *telnet, telnet_event_t *event, void *user_data); +/* telopt support table element; use telopt of -1 for end marker */ +struct telnet_telopt_t { + short telopt; + short us:1, him:1; +}; + /* state tracker */ struct telnet_t { /* user data */ void *ud; + /* telopt support table */ + const telnet_telopt_t *telopts; /* event handler */ telnet_event_handler_t eh; #ifdef HAVE_ZLIB @@ -181,8 +188,8 @@ struct telnet_t { }; /* initialize a telnet state tracker */ -extern void telnet_init(telnet_t *telnet, telnet_event_handler_t eh, - unsigned char flags, void *user_data); +extern void telnet_init(telnet_t *telnet, const telnet_telopt_t *telopts, + telnet_event_handler_t eh, unsigned char flags, void *user_data); /* free up any memory allocated by a state tracker */ extern void telnet_free(telnet_t *telnet); diff --git a/telnet-chatd.c b/telnet-chatd.c index c4981f7..5d1813a 100644 --- a/telnet-chatd.c +++ b/telnet-chatd.c @@ -291,7 +291,7 @@ int main(int argc, char **argv) { /* init, welcome */ users[i].sock = rs; - telnet_init(&users[i].telnet, _event_handler, 0, &users[i]); + telnet_init(&users[i].telnet, 0, _event_handler, 0, &users[i]); telnet_negotiate(&users[i].telnet, TELNET_WILL, TELNET_TELOPT_COMPRESS2); telnet_printf(&users[i].telnet, "Enter name: "); diff --git a/telnet-client.c b/telnet-client.c index e5534bd..26cfaa8 100644 --- a/telnet-client.c +++ b/telnet-client.c @@ -32,6 +32,13 @@ static struct termios orig_tios; static telnet_t telnet; static int do_echo; +static const telnet_telopt_t telopts[] = { + { TELNET_TELOPT_ECHO, 0, 1 }, + { TELNET_TELOPT_COMPRESS2, 0, 1 }, + { TELNET_TELOPT_TTYPE, 1, 0 }, + { -1, 0, 0 } +}; + static void _cleanup(void) { tcsetattr(STDOUT_FILENO, TCSADRAIN, &orig_tios); } @@ -91,15 +98,9 @@ static void _event_handler(telnet_t *telnet, telnet_event_t *ev, break; /* request to enable remote feature (or receipt) */ case TELNET_EV_WILL: - /* we accept COMPRESS2 (MCCP) */ - if (ev->telopt == TELNET_TELOPT_COMPRESS2) - ev->accept = 1; - /* we'll agree to turn off our echo if server wants us to stop */ - else if (ev->telopt == TELNET_TELOPT_ECHO) { + if (ev->telopt == TELNET_TELOPT_ECHO) do_echo = 0; - ev->accept = 1; - } break; /* notification of disabling remote feature (or receipt) */ case TELNET_EV_WONT: @@ -108,9 +109,6 @@ static void _event_handler(telnet_t *telnet, telnet_event_t *ev, break; /* request to enable local feature (or receipt) */ case TELNET_EV_DO: - /* we support the TTYPE option */ - if (ev->telopt == TELNET_TELOPT_TTYPE) - ev->accept = 1; break; /* demand to disable local feature (or receipt) */ case TELNET_EV_DONT: @@ -202,7 +200,7 @@ int main(int argc, char **argv) { do_echo = 1; /* initialize telnet box */ - telnet_init(&telnet, _event_handler, 0, &sock); + telnet_init(&telnet, telopts, _event_handler, 0, &sock); /* initialize poll descriptors */ memset(pfd, 0, sizeof(pfd)); diff --git a/telnet-proxy.c b/telnet-proxy.c index 3a33208..72e65b5 100644 --- a/telnet-proxy.c +++ b/telnet-proxy.c @@ -357,9 +357,9 @@ int main(int argc, char **argv) { client.remote = &server; /* initialize telnet boxes */ - telnet_init(&server.telnet, _event_handler, TELNET_FLAG_PROXY, + telnet_init(&server.telnet, 0, _event_handler, TELNET_FLAG_PROXY, &server); - telnet_init(&client.telnet, _event_handler, TELNET_FLAG_PROXY, + telnet_init(&client.telnet, 0, _event_handler, TELNET_FLAG_PROXY, &client); /* initialize poll descriptors */