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