Merge branch 'master' into zmp

Conflicts:
	libtelnet.c
This commit is contained in:
Sean Middleditch 2009-03-22 16:33:22 -04:00
commit 94738a4b85
6 changed files with 115 additions and 36 deletions

55
README
View File

@ -53,19 +53,60 @@ interface.
IIa. Initialization
struct telnet_t;
This structure represents the state of the TELNET protocol for a
single connection. Each connection utilizing TELNET must have its
own telnet_t structure, which is passed to all libtelnet API
calls.
Using libtelnet requires the initialization of a telnet_t structure
which stores all current state for a single TELNET connection.
void telnet_init(telnet_t *telnet, telnet_event_handler_t handler,
unsigned char flags, void *user_data);
Initializing a telnet_t structure requires several pieces of data.
One of these is the telopt support table, which specifies which
TELNET options your application supports both locally and remotely.
This table is comprised of telnet_telopt_t structures, one for each
supported option. Each entry specifies the option supported,
whether the option is supported locally or remotely.
struct telnet_telopt_t {
short telopt;
unsigned char us;
unsigned char him;
};
The us field denotes whether your application supporst the telopt
locally. It should be set to TELNET_WILL if you support it and to
TELNET_WONT if you don't. The him field denotes whether the telopt
is supported on the remote end, and should be TELNET_DO if yes and
TELNET_DONT if not.
When definition the telopt table you must include an end marker
entry, which is simply an entry with telopt set to -1. For
example:
static const telnet_telopt_t my_telopts[] = {
{ TELNET_TELOPT_ECHO, TELNET_WILL, TELNET_DONT },
{ TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT },
{ TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DO },
{ TELNET_TELOPT_ZMP, TELNET_WONT, TELNET_DO },
{ TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DO },
{ TELNET_TELOPT_BINARY, TELNET_WILL, TELNET_DO },
{ TELNET_TELOPT_NAWS, TELNET_WILL, TELNET_DONT },
{ -1, 0, 0 }
};
If you need to dynamically alter supported options on a
per-connection basis then you may use a different tables
(dynamically allocated if necessary) per call to telnet_init() or
you share a single constant table like the above example between
all connections if you support a fixed set of options. Most
applications will support only a fixed set of options.
void telnet_init(telnet_t *telnet, const telnet_telopts_t *telopts,
telnet_event_handler_t handler, unsigned char flags,
void *user_data);
The telnet_init() function is responsible for initializing the
data in a telnet_t structure. It must be called immediately after
establishing a connection and before any other libtelnet API calls
are made.
The telopts field is the telopt support table as described above.
The handler parameter must be a function matching the
telnet_event_handler_t definition. More information about events
can be found in section IId.

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, const char **argv, size_t argc) {
telnet_event_t ev;
@ -67,11 +66,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 */
@ -181,6 +177,34 @@ static void _send(telnet_t *telnet, const char *buffer,
_event(telnet, TELNET_EV_SEND, 0, 0, buffer, size, 0, 0);
}
/* 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) {
if (us && telnet->telopts[i].us == TELNET_WILL)
return 1;
else if (!us && telnet->telopts[i].him == TELNET_DO)
return 1;
else
return 0;
}
}
/* not found, so not supported */
return 0;
}
/* retrieve RFC1143 option state */
static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
unsigned char telopt) {
@ -266,9 +290,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, 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, 0, 0);
} else
_send_negotiate(telnet, TELNET_DONT, telopt);
break;
@ -323,9 +348,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, 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, 0, 0);
} else
_send_negotiate(telnet, TELNET_WONT, telopt);
break;
@ -441,10 +467,11 @@ static int _subnegotiate(telnet_t *telnet) {
}
/* 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;
}

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
@ -23,7 +24,6 @@ typedef struct telnet_event_t telnet_event_t;
#define TELNET_WONT 252
#define TELNET_WILL 251
#define TELNET_SB 250
#define TELNET_SB 250
#define TELNET_GA 249
#define TELNET_EL 248
#define TELNET_EC 247
@ -148,18 +148,25 @@ 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; /* one of the TELOPT codes or -1 */
unsigned char us; /* TELNET_WILL or TELNET_WONT */
unsigned char him; /* TELNET_DO or TELNET_DONT */
};
/* 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
@ -185,8 +192,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

@ -29,6 +29,11 @@
#define MAX_USERS 64
#define LINEBUFFER_SIZE 256
const telnet_telopt_t telopts[] = {
{ TELNET_TELOPT_COMPRESS2, TELNET_WILL, TELNET_DONT },
{ -1, 0, 0 }
};
struct user_t {
char *name;
int sock;
@ -291,7 +296,8 @@ 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, telopts, _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, TELNET_WONT, TELNET_DO },
{ TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DO },
{ TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT },
{ -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

@ -360,9 +360,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 */