mirror of https://gerrit.osmocom.org/libtelnet
commit
94738a4b85
55
README
55
README
|
@ -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.
|
||||
|
|
47
libtelnet.c
47
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, 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;
|
||||
}
|
||||
|
|
17
libtelnet.h
17
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
|
||||
|
@ -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);
|
||||
|
|
|
@ -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: ");
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue