require option table to fix negotiation "race" issie (experimental)

This commit is contained in:
Sean Middleditch 2009-03-21 00:20:44 -04:00
parent e2122b2653
commit 34bb0998c7
5 changed files with 54 additions and 29 deletions

View File

@ -54,9 +54,8 @@ static const size_t _buffer_sizes[] = {
static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) / static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
sizeof(_buffer_sizes[0]); sizeof(_buffer_sizes[0]);
/* event dispatch helper; return value is value of the accept field of the /* event dispatch helper */
* event struct after dispatch; used for the funky REQUEST event */ static INLINE void _event(telnet_t *telnet, telnet_event_type_t type,
static INLINE int _event(telnet_t *telnet, telnet_event_type_t type,
unsigned char command, unsigned char telopt, unsigned char command, unsigned char telopt,
const char *buffer, size_t size) { const char *buffer, size_t size) {
telnet_event_t ev; telnet_event_t ev;
@ -65,11 +64,8 @@ static INLINE int _event(telnet_t *telnet, telnet_event_type_t type,
ev.type = type; ev.type = type;
ev.command = command; ev.command = command;
ev.telopt = telopt; ev.telopt = telopt;
ev.accept = 0;
telnet->eh(telnet, &ev, telnet->ud); telnet->eh(telnet, &ev, telnet->ud);
return ev.accept;
} }
/* error generation function */ /* 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); _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 */ /* retrieve RFC1143 option state */
static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet, static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
unsigned char telopt) { unsigned char telopt) {
@ -264,9 +281,10 @@ static void _negotiate(telnet_t *telnet, unsigned char telopt) {
case TELNET_STATE_WILL: case TELNET_STATE_WILL:
switch (q.him) { switch (q.him) {
case Q_NO: 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); _set_rfc1143(telnet, telopt, q.us, Q_YES);
_send_negotiate(telnet, TELNET_DO, telopt); _send_negotiate(telnet, TELNET_DO, telopt);
_event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
} else } else
_send_negotiate(telnet, TELNET_DONT, telopt); _send_negotiate(telnet, TELNET_DONT, telopt);
break; break;
@ -321,9 +339,10 @@ static void _negotiate(telnet_t *telnet, unsigned char telopt) {
case TELNET_STATE_DO: case TELNET_STATE_DO:
switch (q.us) { switch (q.us) {
case Q_NO: 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); _set_rfc1143(telnet, telopt, Q_YES, q.him);
_send_negotiate(telnet, TELNET_WILL, telopt); _send_negotiate(telnet, TELNET_WILL, telopt);
_event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
} else } else
_send_negotiate(telnet, TELNET_WONT, telopt); _send_negotiate(telnet, TELNET_WONT, telopt);
break; break;
@ -377,10 +396,11 @@ static void _negotiate(telnet_t *telnet, unsigned char telopt) {
} }
/* initialize a telnet state tracker */ /* initialize a telnet state tracker */
void telnet_init(telnet_t *telnet, telnet_event_handler_t eh, void telnet_init(telnet_t *telnet, const telnet_telopt_t *telopts,
unsigned char flags, void *user_data) { telnet_event_handler_t eh, unsigned char flags, void *user_data) {
memset(telnet, 0, sizeof(telnet_t)); memset(telnet, 0, sizeof(telnet_t));
telnet->ud = user_data; telnet->ud = user_data;
telnet->telopts = telopts;
telnet->eh = eh; telnet->eh = eh;
telnet->flags = flags; telnet->flags = flags;
} }
@ -775,7 +795,7 @@ void telnet_send(telnet_t *telnet, const char *buffer,
} }
/* send subnegotiation header */ /* 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 }; const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
_send(telnet, sb, 3); _send(telnet, sb, 3);
} }

View File

@ -15,6 +15,7 @@
/* forward declarations */ /* forward declarations */
typedef struct telnet_t telnet_t; typedef struct telnet_t telnet_t;
typedef struct telnet_event_t telnet_event_t; typedef struct telnet_event_t telnet_event_t;
typedef struct telnet_telopt_t telnet_telopt_t;
/* telnet special values */ /* telnet special values */
#define TELNET_IAC 255 #define TELNET_IAC 255
@ -144,18 +145,24 @@ struct telnet_event_t {
unsigned char command; unsigned char command;
/* telopt info: for negotiation events SUBNEGOTIATION */ /* telopt info: for negotiation events SUBNEGOTIATION */
unsigned char telopt; unsigned char telopt;
/* accept status: for WILL and DO events */
unsigned char accept;
}; };
/* event handler declaration */ /* event handler declaration */
typedef void (*telnet_event_handler_t)(telnet_t *telnet, typedef void (*telnet_event_handler_t)(telnet_t *telnet,
telnet_event_t *event, void *user_data); 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 */ /* state tracker */
struct telnet_t { struct telnet_t {
/* user data */ /* user data */
void *ud; void *ud;
/* telopt support table */
const telnet_telopt_t *telopts;
/* event handler */ /* event handler */
telnet_event_handler_t eh; telnet_event_handler_t eh;
#ifdef HAVE_ZLIB #ifdef HAVE_ZLIB
@ -181,8 +188,8 @@ struct telnet_t {
}; };
/* initialize a telnet state tracker */ /* initialize a telnet state tracker */
extern void telnet_init(telnet_t *telnet, telnet_event_handler_t eh, extern void telnet_init(telnet_t *telnet, const telnet_telopt_t *telopts,
unsigned char flags, void *user_data); telnet_event_handler_t eh, unsigned char flags, void *user_data);
/* free up any memory allocated by a state tracker */ /* free up any memory allocated by a state tracker */
extern void telnet_free(telnet_t *telnet); extern void telnet_free(telnet_t *telnet);

View File

@ -291,7 +291,7 @@ int main(int argc, char **argv) {
/* init, welcome */ /* init, welcome */
users[i].sock = rs; 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_negotiate(&users[i].telnet, TELNET_WILL,
TELNET_TELOPT_COMPRESS2); TELNET_TELOPT_COMPRESS2);
telnet_printf(&users[i].telnet, "Enter name: "); telnet_printf(&users[i].telnet, "Enter name: ");

View File

@ -32,6 +32,13 @@ static struct termios orig_tios;
static telnet_t telnet; static telnet_t telnet;
static int do_echo; 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) { static void _cleanup(void) {
tcsetattr(STDOUT_FILENO, TCSADRAIN, &orig_tios); tcsetattr(STDOUT_FILENO, TCSADRAIN, &orig_tios);
} }
@ -91,15 +98,9 @@ static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
break; break;
/* request to enable remote feature (or receipt) */ /* request to enable remote feature (or receipt) */
case TELNET_EV_WILL: 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 */ /* 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; do_echo = 0;
ev->accept = 1;
}
break; break;
/* notification of disabling remote feature (or receipt) */ /* notification of disabling remote feature (or receipt) */
case TELNET_EV_WONT: case TELNET_EV_WONT:
@ -108,9 +109,6 @@ static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
break; break;
/* request to enable local feature (or receipt) */ /* request to enable local feature (or receipt) */
case TELNET_EV_DO: case TELNET_EV_DO:
/* we support the TTYPE option */
if (ev->telopt == TELNET_TELOPT_TTYPE)
ev->accept = 1;
break; break;
/* demand to disable local feature (or receipt) */ /* demand to disable local feature (or receipt) */
case TELNET_EV_DONT: case TELNET_EV_DONT:
@ -202,7 +200,7 @@ int main(int argc, char **argv) {
do_echo = 1; do_echo = 1;
/* initialize telnet box */ /* initialize telnet box */
telnet_init(&telnet, _event_handler, 0, &sock); telnet_init(&telnet, telopts, _event_handler, 0, &sock);
/* initialize poll descriptors */ /* initialize poll descriptors */
memset(pfd, 0, sizeof(pfd)); memset(pfd, 0, sizeof(pfd));

View File

@ -357,9 +357,9 @@ int main(int argc, char **argv) {
client.remote = &server; client.remote = &server;
/* initialize telnet boxes */ /* initialize telnet boxes */
telnet_init(&server.telnet, _event_handler, TELNET_FLAG_PROXY, telnet_init(&server.telnet, 0, _event_handler, TELNET_FLAG_PROXY,
&server); &server);
telnet_init(&client.telnet, _event_handler, TELNET_FLAG_PROXY, telnet_init(&client.telnet, 0, _event_handler, TELNET_FLAG_PROXY,
&client); &client);
/* initialize poll descriptors */ /* initialize poll descriptors */