mirror of https://gerrit.osmocom.org/libtelnet
require option table to fix negotiation "race" issie (experimental)
This commit is contained in:
parent
e2122b2653
commit
34bb0998c7
42
libtelnet.c
42
libtelnet.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
15
libtelnet.h
15
libtelnet.h
|
@ -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);
|
||||||
|
|
|
@ -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: ");
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue