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) /
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);
}

View File

@ -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);

View File

@ -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: ");

View File

@ -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));

View File

@ -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 */