mirror of https://gerrit.osmocom.org/libtelnet
remove the lib/LIB from identifier prefixes; also, swap printf and printf2 so the more common one is the one with the shorter name
This commit is contained in:
parent
156f5862a6
commit
f65f27d77d
357
README
357
README
|
@ -29,8 +29,8 @@ I. INTRODUCTION
|
||||||
|
|
||||||
libtelnet provides safe and correct handling of the core TELNET
|
libtelnet provides safe and correct handling of the core TELNET
|
||||||
protocol. In addition to the base TELNET protocol, libtelnet also
|
protocol. In addition to the base TELNET protocol, libtelnet also
|
||||||
implements the Q method of TELNET option negotiation. libtelnet
|
implements the Q method of TELNET option negotiation. libtelnet can
|
||||||
can be used for writing servers, clients, or proxies.
|
be used for writing servers, clients, or proxies.
|
||||||
|
|
||||||
For more information on the TELNET protocol, see:
|
For more information on the TELNET protocol, see:
|
||||||
|
|
||||||
|
@ -42,65 +42,65 @@ II. LIBTELNET API
|
||||||
|
|
||||||
The libtelnet API contains several distinct parts. The first part is
|
The libtelnet API contains several distinct parts. The first part is
|
||||||
the basic initialization and deinitialization routines. The second
|
the basic initialization and deinitialization routines. The second
|
||||||
part is a single function for pushing received data into the
|
part is a single function for pushing received data into the telnet
|
||||||
libtelnet processor. The third part is the libtelnet_send_*()
|
processor. The third part is the telnet_send_*() functions, which
|
||||||
functions, which generate TELNET commands and ensure data is properly
|
generate TELNET commands and ensure data is properly formatted before
|
||||||
formatted before sending over the wire. The final part is the event
|
sending over the wire. The final part is the event handler
|
||||||
handler interface.
|
interface.
|
||||||
|
|
||||||
IIa. Initialization
|
IIa. Initialization
|
||||||
|
|
||||||
struct libtelnet_t;
|
struct telnet_t;
|
||||||
This structure represents the state of the TELNET protocol for a
|
This structure represents the state of the TELNET protocol for a
|
||||||
single connection. Each connection utilizing TELNET must have
|
single connection. Each connection utilizing TELNET must have its
|
||||||
its own libtelnet_t structure, which is passed to all libtelnet
|
own telnet_t structure, which is passed to all libtelnet API
|
||||||
API calls.
|
calls.
|
||||||
|
|
||||||
void libtelnet_init(libtelnet_t *telnet, libtelnet_event_handler_t handler,
|
void telnet_init(telnet_t *telnet, telnet_event_handler_t handler,
|
||||||
unsigned char flags, void *user_data);
|
unsigned char flags, void *user_data);
|
||||||
The libtelnet_init() function is responsible for initializing
|
The telnet_init() function is responsible for initializing the
|
||||||
the data in a libtelnet_t structure. It must be called
|
data in a telnet_t structure. It must be called immediately after
|
||||||
immediately after establishing a connection and before any other
|
establishing a connection and before any other libtelnet API calls
|
||||||
libtelnet API calls are made.
|
are made.
|
||||||
|
|
||||||
The handler parameter must be a function matching the
|
The handler parameter must be a function matching the
|
||||||
libtelnet_event_handler_t definition. More information about
|
telnet_event_handler_t definition. More information about events
|
||||||
events can be found in section IId.
|
can be found in section IId.
|
||||||
|
|
||||||
The user_data parameter is passed to the event handler whenver it
|
The user_data parameter is passed to the event handler whenver it
|
||||||
is invoked. This will usually be a structure container
|
is invoked. This will usually be a structure container
|
||||||
information about the connection, including a socket descriptor
|
information about the connection, including a socket descriptor
|
||||||
for implementing LIBTELNET_EV_SEND event handling.
|
for implementing TELNET_EV_SEND event handling.
|
||||||
|
|
||||||
The flags parameter can be any of the following flag constants
|
The flags parameter can be any of the following flag constants
|
||||||
bit-or'd together, or 0 to leave all options disabled.
|
bit-or'd together, or 0 to leave all options disabled.
|
||||||
|
|
||||||
LIBTELNET_FLAG_PROXY
|
TELNET_FLAG_PROXY
|
||||||
Operate in proxy mode. This disables the RFC1143 support and
|
Operate in proxy mode. This disables the RFC1143 support and
|
||||||
enables automatic detection of COMPRESS2 streams.
|
enables automatic detection of COMPRESS2 streams.
|
||||||
|
|
||||||
boid libtelnet_free(libtelnet_t *telnet);
|
boid telnet_free(telnet_t *telnet);
|
||||||
Releases any internal memory allocated by libtelnet. This must
|
Releases any internal memory allocated by libtelnet. This must be
|
||||||
be called whenever a connection is closed, or you will incur
|
called whenever a connection is closed, or you will incur memory
|
||||||
memory leaks.
|
leaks.
|
||||||
|
|
||||||
IIb. Receiving Data
|
IIb. Receiving Data
|
||||||
|
|
||||||
void libtelnet_push(libtelnet_t *telnet,
|
void telnet_push(telnet_t *telnet,
|
||||||
const char *buffer, unsigned int size, void *user_data);
|
const char *buffer, unsigned int size, void *user_data);
|
||||||
When your application receives data over the socket from the
|
When your application receives data over the socket from the
|
||||||
remote end, it must pass the received bytes into this function.
|
remote end, it must pass the received bytes into this function.
|
||||||
|
|
||||||
As the TELNET stream is parsed, events will be generated and
|
As the TELNET stream is parsed, events will be generated and
|
||||||
passed to the event handler given to libtelnet_init(). Of
|
passed to the event handler given to telnet_init(). Of particular
|
||||||
particular interest for data receiving is the LIBTELNET_EV_DATA
|
interest for data receiving is the TELNET_EV_DATA event, which is
|
||||||
event, which is triggered for any regular data such as user
|
triggered for any regular data such as user input or server
|
||||||
input or server process output.
|
process output.
|
||||||
|
|
||||||
IIc. Sending Data
|
IIc. Sending Data
|
||||||
|
|
||||||
All of the libtelnet_send_*() functions will invoke the
|
All of the telnet_send_*() functions will invoke the TELNET_EV_SEND
|
||||||
LIBTELNET_EV_SEND event.
|
event.
|
||||||
|
|
||||||
Note: it is very important that ALL data sent to the remote end of
|
Note: it is very important that ALL data sent to the remote end of
|
||||||
the connection be passed through libtelnet. All user input or
|
the connection be passed through libtelnet. All user input or
|
||||||
|
@ -108,186 +108,185 @@ IIc. Sending Data
|
||||||
to one of the following functions. Do NOT send or buffer
|
to one of the following functions. Do NOT send or buffer
|
||||||
unprocessed output data directly!
|
unprocessed output data directly!
|
||||||
|
|
||||||
void libtelnet_send_command(libtelnet_t *telnet, unsigned char cmd);
|
void telnet_send_command(telnet_t *telnet, unsigned char cmd);
|
||||||
Sends a single "simple" TELNET command, such as the GO-AHEAD
|
Sends a single "simple" TELNET command, such as the GO-AHEAD
|
||||||
commands (255 249).
|
commands (255 249).
|
||||||
|
|
||||||
void libtelnet_send_command(libtelnet_t *telnet, unsigned char cmd,
|
void telnet_send_telopt(telnet_t *telnet, unsigned char cmd,
|
||||||
unsigned char telopt);
|
unsigned char telopt);
|
||||||
Sends a TELNET command with an option code following. This is
|
Sends a TELNET command with an option code following. This is
|
||||||
only useful for the WILL, WONT, DO, DONT, and SB commands.
|
only useful for the WILL, WONT, DO, DONT, and SB commands.
|
||||||
|
|
||||||
void libtelnet_send_negotiate(libtelnet_t *telnet,
|
void telnet_send_negotiate(telnet_t *telnet, unsigned char cmd,
|
||||||
unsigned char cmd, unsigned char opt);
|
unsigned char opt);
|
||||||
Sends a TELNET negotiation command. The cmd parameter must be
|
Sends a TELNET negotiation command. The cmd parameter must be one
|
||||||
one of LIBTELNET_WILL, LIBTELNET_DONT, LIBTELNET_DO, or
|
of TELNET_WILL, TELNET_DONT, TELNET_DO, or TELNET_DONT. The opt
|
||||||
LIBTELNET_DONT. The opt parameter is the option to
|
parameter is the option to negotiate.
|
||||||
negotiate.
|
|
||||||
|
|
||||||
void libtelnet_send_data(libtelnet_t *telnet,
|
Unless in PROXY mode, the RFC1143 support may delay or ellide the
|
||||||
const char *buffer, unsigned int size);
|
request entirely, as appropriate. It will ignore duplicate
|
||||||
Sends raw data, which would be either the process output from
|
invocations, such as asking for WILL NAWS when NAWS is already on
|
||||||
a server or the user input from a client.
|
or is currently awaiting response from the remote end.
|
||||||
|
|
||||||
void libtelnet_send_subnegotiation(libtelnet_t *telnet,
|
void telnet_send_data(telnet_t *telnet, const char *buffer,
|
||||||
unsigned char telopt, const char *buffer,
|
|
||||||
unsigned int size);
|
unsigned int size);
|
||||||
Sends a TELNET sub-negotiation command. The telopt parameter
|
Sends raw data, which would be either the process output from a
|
||||||
is the sub-negotiation option.
|
server or the user input from a client.
|
||||||
|
|
||||||
|
For sending regular text is may be more convenient to use
|
||||||
|
telnet_printf().
|
||||||
|
|
||||||
|
void telnet_send_subnegotiation(telnet_t *telnet,
|
||||||
|
unsigned char telopt, const char *buffer, unsigned int size);
|
||||||
|
Sends a TELNET sub-negotiation command. The telopt parameter is
|
||||||
|
the sub-negotiation option.
|
||||||
|
|
||||||
Note that the above function is just a shorthand for:
|
Note that the above function is just a shorthand for:
|
||||||
libtelnet_send_telopt(telnet, LIBTELNET_SB, telopt);
|
telnet_send_telopt(telnet, TELNET_SB, telopt);
|
||||||
libtelnet_send_data(telnet, buffer, size);
|
telnet_send_data(telnet, buffer, size);
|
||||||
libtelnet_send_command(telnet, LIBTELNET_SE);
|
telnet_send_command(telnet, TELNET_SE);
|
||||||
|
|
||||||
For some subnegotiations that involve a lot of complex formatted
|
For some subnegotiations that involve a lot of complex formatted
|
||||||
data to be sent, it may be easier to manually send the SB telopt
|
data to be sent, it may be easier to manually send the SB telopt
|
||||||
header and SE footer around mulitple calls to send_data.
|
header and SE footer around mulitple calls to send_data.
|
||||||
|
|
||||||
NOTE: libtelnet_send_subnegotiation() does have special behavior
|
NOTE: telnet_send_subnegotiation() does have special behavior in
|
||||||
in PROXY mode, as in that mode this function will automatically
|
PROXY mode, as in that mode this function will automatically
|
||||||
detect the COMPRESS2 marker and enable zlib compression.
|
detect the COMPRESS2 marker and enable zlib compression.
|
||||||
|
|
||||||
int libtelnet_printf(libtelnet_t *telnet, const char *fmt, ...);
|
int telnet_printf(telnet_t *telnet, const char *fmt, ...);
|
||||||
This functions very similarly to fprintf, except that output
|
This functions very similarly to fprintf, except that output is
|
||||||
is sent through libtelnet for processing. This is equivalent
|
sent through libtelnet for processing. IAC bytes are properly
|
||||||
to using snprintf() to format data into a buffer and then
|
escaped, C newlines (\n) are translated into CR LF, and C carriage
|
||||||
sending the buffer to libtelnet_send_data(). The return code
|
returns (\r) are translated into CR NUL, all as required by
|
||||||
is the length of the formatted text.
|
RFC854. The return code is the length of the formatted text.
|
||||||
|
|
||||||
NOTE: due to an internal implementation detail, the maximum
|
NOTE: due to an internal implementation detail, the maximum
|
||||||
lenth of the formatted text is 4096 characters.
|
lenth of the formatted text is 4096 characters.
|
||||||
|
|
||||||
int libtelnet_printf2(libtelnet_T *telnet, const char *fmt, ...);
|
int telnet_printf2(telnet_t *telnet, const char *fmt, ...);
|
||||||
Identical to libtelnet_print(), except that this variant will
|
Identical to telnet_printf() except that \r and \n are not
|
||||||
also translate C newlines (\n) into a CRLF and translates
|
translated. This should be used if you are attempting to send
|
||||||
carriage returns (\r) into CRNUL, as required by TELNET.
|
raw data inside a subnegotiation or if you have already manually
|
||||||
|
escaped newlines.
|
||||||
NOTE: this function should only be used for regular data such
|
|
||||||
as user input (in client applications) or process output (in
|
|
||||||
server applications). If you are formatting data that is part
|
|
||||||
of a subnegotiation, you should always use libtelnet_printf()
|
|
||||||
instead, as you will rarely want newline translation inside of
|
|
||||||
subnegotiations.
|
|
||||||
|
|
||||||
IId. Event Handling
|
IId. Event Handling
|
||||||
|
|
||||||
libtelnet relies on an event-handling mechanism for processing
|
libtelnet relies on an event-handling mechanism for processing the
|
||||||
the parsed TELNET protocol stream as well as for buffering and
|
parsed TELNET protocol stream as well as for buffering and sending
|
||||||
sending output data.
|
output data.
|
||||||
|
|
||||||
When you initialize a libtelnet_t structure with libtelnet_init()
|
When you initialize a telnet_t structure with telnet_init() you had
|
||||||
you had to pass in an event handler function. This function must
|
to pass in an event handler function. This function must meet the
|
||||||
meet the following prototype:
|
following prototype:
|
||||||
|
|
||||||
void (libtelnet_t *telnet, libtelnet_event_t *event,
|
void (telnet_t *telnet, telnet_event_t *event, void *user_data);
|
||||||
void *user_data);
|
|
||||||
|
|
||||||
The event structure is detailed below. The user_data value is the
|
The event structure is detailed below. The user_data value is the
|
||||||
pointer passed to libtelnet_init().
|
pointer passed to telnet_init().
|
||||||
|
|
||||||
struct libtelnet_event_t {
|
struct telnet_event_t {
|
||||||
const char *buffer;
|
const char *buffer;
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
libtelnet_event_type_t type;
|
telnet_event_type_t type;
|
||||||
unsigned char command;
|
unsigned char command;
|
||||||
unsigned char telopt;
|
unsigned char telopt;
|
||||||
unsigned char accept;
|
unsigned char accept;
|
||||||
};
|
};
|
||||||
|
|
||||||
The enumeration values of libtelnet_event_type_t are described in
|
The enumeration values of telnet_event_type_t are described in
|
||||||
detail below. Whenever the the event handler is invoked, the
|
detail below. Whenever the the event handler is invoked, the
|
||||||
application must look at the event->type value and do any
|
application must look at the event->type value and do any necessary
|
||||||
necessary processing.
|
processing.
|
||||||
|
|
||||||
The only event that MUST be implemented is LIBTELNET_EV_SEND.
|
The only event that MUST be implemented is TELNET_EV_SEND. Most
|
||||||
Most applications will also always want to implement the event
|
applications will also always want to implement the event
|
||||||
LIBTELNET_EV_DATA.
|
TELNET_EV_DATA.
|
||||||
|
|
||||||
Here is an example event handler implementation which includes
|
Here is an example event handler implementation which includes
|
||||||
handlers for several important events.
|
handlers for several important events.
|
||||||
|
|
||||||
void my_event_handler(libtelnet_t *telnet, libtelnet_event_t *ev,
|
void my_event_handler(telnet_t *telnet, telnet_event_t *ev,
|
||||||
void *user_data) {
|
void *user_data) {
|
||||||
struct user_info *user = (struct user_info *)user_data;
|
struct user_info *user = (struct user_info *)user_data;
|
||||||
|
|
||||||
switch (ev->type) {
|
switch (ev->type) {
|
||||||
case LIBTELNET_EV_DATA:
|
case TELNET_EV_DATA:
|
||||||
process_user_input(user, event->buffer, event->size);
|
process_user_input(user, event->buffer, event->size);
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_EV_SEND:
|
case TELNET_EV_SEND:
|
||||||
write_to_descriptor(user, event->buffer, event->size);
|
write_to_descriptor(user, event->buffer, event->size);
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_EV_ERROR:
|
case TELNET_EV_ERROR:
|
||||||
fatal_error("TELNET error: %s", event->buffer);
|
fatal_error("TELNET error: %s", event->buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LIBTELNET_EV_DATA:
|
TELNET_EV_DATA:
|
||||||
The DATA event is triggered whenever regular data (not part of
|
The DATA event is triggered whenever regular data (not part of any
|
||||||
any special TELNET command) is received. For a client, this
|
special TELNET command) is received. For a client, this will be
|
||||||
will be process output from the server. For a server, this will
|
process output from the server. For a server, this will be input
|
||||||
be input typed by the user.
|
typed by the user.
|
||||||
|
|
||||||
The event->buffer value will contain the bytes received and the
|
The event->buffer value will contain the bytes received and the
|
||||||
event->size value will contain the number of bytes received.
|
event->size value will contain the number of bytes received. Note
|
||||||
Note that event->buffer is not NUL terminated!
|
that event->buffer is not NUL terminated!
|
||||||
|
|
||||||
NOTE: there is no guarantee that user input or server output
|
NOTE: there is no guarantee that user input or server output
|
||||||
will be received in whole lines. If you wish to process data
|
will be received in whole lines. If you wish to process data
|
||||||
a line at a time, you are responsible for buffering the data and
|
a line at a time, you are responsible for buffering the data and
|
||||||
checking for line terminators yourself!
|
checking for line terminators yourself!
|
||||||
|
|
||||||
LIBTELNET_EV_SEND:
|
TELNET_EV_SEND:
|
||||||
This event is sent whenever libtelnet has generated data that
|
This event is sent whenever libtelnet has generated data that must
|
||||||
must be sent over the wire to the remove end. Generally that
|
be sent over the wire to the remove end. Generally that means
|
||||||
means calling send() or adding the data to your application's
|
calling send() or adding the data to your application's output
|
||||||
output buffer.
|
buffer.
|
||||||
|
|
||||||
The event->buffer value will contain the bytes to send and the
|
The event->buffer value will contain the bytes to send and the
|
||||||
event->size value will contain the number of bytes to send.
|
event->size value will contain the number of bytes to send. Note
|
||||||
Note that event->buffer is not NUL terminated, and may include
|
that event->buffer is not NUL terminated, and may include NUL
|
||||||
NUL characters in its data, so always use event->size!
|
characters in its data, so always use event->size!
|
||||||
|
|
||||||
NOTE: Your SEND event handler must send or buffer the data in
|
NOTE: Your SEND event handler must send or buffer the data in
|
||||||
its raw form as provided by libtelnet. If you wish to perform
|
its raw form as provided by libtelnet. If you wish to perform
|
||||||
any kind of preprocessing on data you want to send to the other
|
any kind of preprocessing on data you want to send to the other
|
||||||
|
|
||||||
LIBTELNET_EV_IAC:
|
TELNET_EV_IAC:
|
||||||
The IAC event is triggered whenever a simple IAC command is
|
The IAC event is triggered whenever a simple IAC command is
|
||||||
received, such as the IAC EOR (end of record, also called
|
received, such as the IAC EOR (end of record, also called go ahead
|
||||||
go ahead or GA) command.
|
or GA) command.
|
||||||
|
|
||||||
The command received is in the event->command value.
|
The command received is in the event->command value.
|
||||||
|
|
||||||
The necessary processing depends on the specific commands; see
|
The necessary processing depends on the specific commands; see
|
||||||
the TELNET RFC for more information.
|
the TELNET RFC for more information.
|
||||||
|
|
||||||
LIBTELNET_EV_WILL:
|
TELNET_EV_WILL:
|
||||||
LIBTELNET_EV_DO:
|
TELNET_EV_DO:
|
||||||
The WILL and DO events are sent when a TELNET negotiation
|
The WILL and DO events are sent when a TELNET negotiation command
|
||||||
command of the same name is received.
|
of the same name is received.
|
||||||
|
|
||||||
WILL events are sent by the remote end when they wish to be
|
WILL events are sent by the remote end when they wish to be
|
||||||
allowed to turn an option on on their end, or in confirmation
|
allowed to turn an option on on their end, or in confirmation
|
||||||
after you have sent a DO command to them.
|
after you have sent a DO command to them.
|
||||||
|
|
||||||
DO events are sent by the remote end when they wish for you
|
DO events are sent by the remote end when they wish for you to
|
||||||
to turn on an option on your end, or in confirmation after you
|
turn on an option on your end, or in confirmation after you have
|
||||||
have sent a WILL command to them.
|
sent a WILL command to them.
|
||||||
|
|
||||||
In either case, the TELNET option under negotiation will be in
|
In either case, the TELNET option under negotiation will be in
|
||||||
event->telopt field.
|
event->telopt field.
|
||||||
|
|
||||||
If you support the option and wish for it to be enabled you
|
If you support the option and wish for it to be enabled you must
|
||||||
must set the event->accept field to 1, unless this event is
|
set the event->accept field to 1, unless this event is a
|
||||||
a confirmation for a previous WILL/DO command you sent to the
|
confirmation for a previous WILL/DO command you sent to the remote
|
||||||
remote end. If you do not set event->field to 1 then
|
end. If you do not set event->field to 1 then libtelnet will send
|
||||||
libtelnet will send a rejection command back to the other end.
|
a rejection command back to the other end.
|
||||||
|
|
||||||
libtelnet manages some of the pecularities of negotiation for
|
libtelnet manages some of the pecularities of negotiation for you.
|
||||||
you. For information on libtelnet's negotiation method, see:
|
For information on libtelnet's negotiation method, see:
|
||||||
|
|
||||||
http://www.faqs.org/rfcs/rfc1143.html
|
http://www.faqs.org/rfcs/rfc1143.html
|
||||||
|
|
||||||
|
@ -309,12 +308,12 @@ IId. Event Handling
|
||||||
Note that in PROXY mode libtelnet will do no processing of its
|
Note that in PROXY mode libtelnet will do no processing of its
|
||||||
own for you.
|
own for you.
|
||||||
|
|
||||||
LIBTELNET_EV_WONT:
|
TELNET_EV_WONT:
|
||||||
LIBTELNET_EV_DONT:
|
TELNET_EV_DONT:
|
||||||
The WONT and DONT events are sent when the remote end of the
|
The WONT and DONT events are sent when the remote end of the
|
||||||
connection wishes to disable an option, when they are
|
connection wishes to disable an option, when they are refusing to
|
||||||
refusing to a support an option that you have asked for, or
|
a support an option that you have asked for, or in confirmation of
|
||||||
in confirmation of an option you have asked to be disabled.
|
an option you have asked to be disabled.
|
||||||
|
|
||||||
Most commonly WONT and DONT events are sent as rejections of
|
Most commonly WONT and DONT events are sent as rejections of
|
||||||
features you requested by sending DO or WILL events. Receiving
|
features you requested by sending DO or WILL events. Receiving
|
||||||
|
@ -334,27 +333,27 @@ IId. Event Handling
|
||||||
Note that in PROXY mode libtelnet will do no processing of its
|
Note that in PROXY mode libtelnet will do no processing of its
|
||||||
own for you.
|
own for you.
|
||||||
|
|
||||||
LIBTELNET_EV_SUBNEGOTIATION:
|
TELNET_EV_SUBNEGOTIATION:
|
||||||
Triggered whenever a TELNET sub-negotiation has been received.
|
Triggered whenever a TELNET sub-negotiation has been received.
|
||||||
Sub-negotiations include the NAWS option for communicating
|
Sub-negotiations include the NAWS option for communicating
|
||||||
terminal size to a server, the NEW-ENVIRON and TTYPE options
|
terminal size to a server, the NEW-ENVIRON and TTYPE options for
|
||||||
for negotiating terminal features, and MUD-centric protocols
|
negotiating terminal features, and MUD-centric protocols such as
|
||||||
such as ZMP, MSSP, and MCCP2.
|
ZMP, MSSP, and MCCP2.
|
||||||
|
|
||||||
The event->telopt value is the option under sub-negotiation.
|
The event->telopt value is the option under sub-negotiation. The
|
||||||
The remaining data (if any) is passed in event->buffer and
|
remaining data (if any) is passed in event->buffer and
|
||||||
event->size. Note that most subnegotiation commands can
|
event->size. Note that most subnegotiation commands can include
|
||||||
include embedded NUL bytes in the subnegotiation data, and
|
embedded NUL bytes in the subnegotiation data, and the data
|
||||||
the data event->buffer is not NUL terminated, so always use
|
event->buffer is not NUL terminated, so always use the event->size
|
||||||
the event->size value!
|
value!
|
||||||
|
|
||||||
The meaning and necessary processing for subnegotiations are
|
The meaning and necessary processing for subnegotiations are
|
||||||
defined in various TELNET RFCs and other informal
|
defined in various TELNET RFCs and other informal specifications.
|
||||||
specifications. A subnegotiation should never be sent unless
|
A subnegotiation should never be sent unless the specific option
|
||||||
the specific option has been enabled through the use of the
|
has been enabled through the use of the telnet negotiation
|
||||||
telnet negotiation feature.
|
feature.
|
||||||
|
|
||||||
LIBTELNET_EV_COMPRESS
|
TELNET_EV_COMPRESS
|
||||||
The COMPRESS event notifies the app that COMPRESS2/MCCP2
|
The COMPRESS event notifies the app that COMPRESS2/MCCP2
|
||||||
compression has begun or ended. Only servers can send compressed
|
compression has begun or ended. Only servers can send compressed
|
||||||
data, and hence only clients will receive compressed data.
|
data, and hence only clients will receive compressed data.
|
||||||
|
@ -362,25 +361,24 @@ IId. Event Handling
|
||||||
The event->command value will be 1 if compression has started and
|
The event->command value will be 1 if compression has started and
|
||||||
will be 0 if compression has ended.
|
will be 0 if compression has ended.
|
||||||
|
|
||||||
LIBTELNET_EV_WARNING
|
TELNET_EV_WARNING
|
||||||
The WARNING event is sent whenever something has gone wrong
|
The WARNING event is sent whenever something has gone wrong inside
|
||||||
inside of libtelnet (possibly due to malformed data sent by the
|
of libtelnet (possibly due to malformed data sent by the other
|
||||||
other end) but which recovery is (likely) possible. It may be
|
end) but which recovery is (likely) possible. It may be safe to
|
||||||
safe to continue using the connection, but some data may have
|
continue using the connection, but some data may have been lost or
|
||||||
been lost or incorrectly interpreted.
|
incorrectly interpreted.
|
||||||
|
|
||||||
The event->buffer value will contain a NUL terminated string
|
The event->buffer value will contain a NUL terminated string
|
||||||
explaining the error, and the event->size value containers the
|
explaining the error, and the event->size value containers the
|
||||||
length of the string.
|
length of the string.
|
||||||
|
|
||||||
LIBTELNET_EV_ERROR
|
TELNET_EV_ERROR
|
||||||
Similar to the WARNING event, the ERROR event is sent whenever
|
Similar to the WARNING event, the ERROR event is sent whenever
|
||||||
something has gone wrong. ERROR events are non-recoverable,
|
something has gone wrong. ERROR events are non-recoverable,
|
||||||
however, and the application should immediately close the
|
however, and the application should immediately close the
|
||||||
connection. Whatever has happened is likely going only to
|
connection. Whatever has happened is likely going only to result
|
||||||
result in garbage from libtelnet. This is most likely to
|
in garbage from libtelnet. This is most likely to happen when a
|
||||||
happen when a COMPRESS2 stream fails, but other problems can
|
COMPRESS2 stream fails, but other problems can occur.
|
||||||
occur.
|
|
||||||
|
|
||||||
The event->buffer value will contain a NUL terminated string
|
The event->buffer value will contain a NUL terminated string
|
||||||
explaining the error, and the event->size value containers the
|
explaining the error, and the event->size value containers the
|
||||||
|
@ -396,30 +394,30 @@ IV. SAFETY AND CORRECTNESS CONSIDERATIONS
|
||||||
=====================================================================
|
=====================================================================
|
||||||
|
|
||||||
Your existing application may make heavy use of its own output
|
Your existing application may make heavy use of its own output
|
||||||
buffering and transmission commands, including hand-made routines
|
buffering and transmission commands, including hand-made routines for
|
||||||
for sending TELNET commands and sub-negotiation requests. There are
|
sending TELNET commands and sub-negotiation requests. There are at
|
||||||
at times subtle issues that need to be handled when communication
|
times subtle issues that need to be handled when communication over
|
||||||
over the TELNET protocol, not least of which is the need to escape
|
the TELNET protocol, not least of which is the need to escape any
|
||||||
any byte value 0xFF with a special TELNET command.
|
byte value 0xFF with a special TELNET command.
|
||||||
|
|
||||||
For these reasons, it is very important that applications making use
|
For these reasons, it is very important that applications making use
|
||||||
of libtelnet always make use of the libtelnet_send_*() family of
|
of libtelnet always make use of the telnet_send_*() family of
|
||||||
functions for all data being sent over the TELNET connection.
|
functions for all data being sent over the TELNET connection.
|
||||||
|
|
||||||
In particular, if you are writing a client, all user input must be
|
In particular, if you are writing a client, all user input must be
|
||||||
passed through to libtelnet_send_data(). This also includes any
|
passed through to telnet_send_data(). This also includes any input
|
||||||
input generated automatically by scripts, triggers, or macros.
|
generated automatically by scripts, triggers, or macros.
|
||||||
|
|
||||||
For a server, any and all output -- including ANSI/VT100 escape
|
For a server, any and all output -- including ANSI/VT100 escape
|
||||||
codes, regular text, newlines, and so on -- must be passed through
|
codes, regular text, newlines, and so on -- must be passed through to
|
||||||
to libtelnet_send_data().
|
telnet_send_data().
|
||||||
|
|
||||||
Any TELNET commands that are to be sent must be given to one of the
|
Any TELNET commands that are to be sent must be given to one of the
|
||||||
following: libtelnet_send_command, libtelnet_send_negotiate, or
|
following: telnet_send_command, telnet_send_negotiate, or
|
||||||
libtelnet_send_subnegotiation().
|
telnet_send_subnegotiation().
|
||||||
|
|
||||||
If you are attempting to enable COMPRESS2/MCCP2, you must use the
|
If you are attempting to enable COMPRESS2/MCCP2, you must use the
|
||||||
libtelnet_begin_compress2() function.
|
telnet_begin_compress2() function.
|
||||||
|
|
||||||
V. MCCP2 COMPRESSION
|
V. MCCP2 COMPRESSION
|
||||||
=====================================================================
|
=====================================================================
|
||||||
|
@ -435,15 +433,15 @@ when compiling libtelnet.c and pass -lz to the linker to link in the
|
||||||
zlib shared library.
|
zlib shared library.
|
||||||
|
|
||||||
libtelnet transparently supports MCCP2. For a server to support
|
libtelnet transparently supports MCCP2. For a server to support
|
||||||
MCCP2, the application must begin negotiation of the COMPRESS2
|
MCCP2, the application must begin negotiation of the COMPRESS2 option
|
||||||
option using libtelnet_send_negotiate(), for example:
|
using telnet_send_negotiate(), for example:
|
||||||
|
|
||||||
libtelnet_send_negotiate(&telnet, LIBTELNET_WILL,
|
telnet_send_negotiate(&telnet, TELNET_WILL,
|
||||||
LIBTELNET_OPTION_COMPRESS2, user_data);
|
TELNET_OPTION_COMPRESS2, user_data);
|
||||||
|
|
||||||
If a favorable DO COMPRESS2 is sent back from the client then the
|
If a favorable DO COMPRESS2 is sent back from the client then the
|
||||||
server application can begin compression at any time by calling
|
server application can begin compression at any time by calling
|
||||||
libtelnet_begin_compress2().
|
telnet_begin_compress2().
|
||||||
|
|
||||||
If a connection is in PROXY mode and COMPRESS2 support is enabled
|
If a connection is in PROXY mode and COMPRESS2 support is enabled
|
||||||
then libtelnet will automatically detect the start of a COMPRESS2
|
then libtelnet will automatically detect the start of a COMPRESS2
|
||||||
|
@ -452,8 +450,8 @@ stream, in either the sending or receiving direction.
|
||||||
VI. TELNET PROXY UTILITY
|
VI. TELNET PROXY UTILITY
|
||||||
=====================================================================
|
=====================================================================
|
||||||
|
|
||||||
The telnet-proxy utility is a small application that serves both as
|
The telnet-proxy utility is a small application that serves both as a
|
||||||
a testbed for libtelnet and as a powerful debugging tool for TELNET
|
testbed for libtelnet and as a powerful debugging tool for TELNET
|
||||||
servers and clients.
|
servers and clients.
|
||||||
|
|
||||||
To use telnet-proxy, you must first compile it using:
|
To use telnet-proxy, you must first compile it using:
|
||||||
|
@ -464,8 +462,8 @@ If you do not have zlib installed and wish to disable MCCP2 support
|
||||||
then you must first edit the Makefile and remove the -DHAVE_ZLIB and
|
then you must first edit the Makefile and remove the -DHAVE_ZLIB and
|
||||||
the -lz from the compile flags.
|
the -lz from the compile flags.
|
||||||
|
|
||||||
To run telnet-proxy, you simply give it the server's host name or
|
To run telnet-proxy, you simply give it the server's host name or IP
|
||||||
IP address, the server's port number, and the port number that
|
address, the server's port number, and the port number that
|
||||||
telnet-proxy should listen on. For example, to connect to the server
|
telnet-proxy should listen on. For example, to connect to the server
|
||||||
on mud.example.com port 7800 and to listen on port 5000, run:
|
on mud.example.com port 7800 and to listen on port 5000, run:
|
||||||
|
|
||||||
|
@ -475,8 +473,7 @@ You can then connect to the host telnet-proxy is running on (e.g.
|
||||||
127.0.0.1) on port 500 and you will automatically be proxied into
|
127.0.0.1) on port 500 and you will automatically be proxied into
|
||||||
mud.example.com.
|
mud.example.com.
|
||||||
|
|
||||||
telnet-proxy will display status information about the data
|
telnet-proxy will display status information about the data passing
|
||||||
passing through both ends of the tunnel. telnet-proxy can only
|
through both ends of the tunnel. telnet-proxy can only support a
|
||||||
support a single tunnel at a time. It will continue running until
|
single tunnel at a time. It will continue running until an error
|
||||||
an error occurs or a terminating signal is sent to the proxy
|
occurs or a terminating signal is sent to the proxy process.
|
||||||
process.
|
|
||||||
|
|
378
libtelnet.c
378
libtelnet.c
|
@ -47,10 +47,10 @@ static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
|
||||||
|
|
||||||
/* event dispatch helper; return value is value of the accept field of the
|
/* event dispatch helper; return value is value of the accept field of the
|
||||||
* event struct after dispatch; used for the funky REQUEST event */
|
* event struct after dispatch; used for the funky REQUEST event */
|
||||||
static int _event(libtelnet_t *telnet, libtelnet_event_type_t type,
|
static 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) {
|
||||||
libtelnet_event_t ev;
|
telnet_event_t ev;
|
||||||
ev.buffer = buffer;
|
ev.buffer = buffer;
|
||||||
ev.size = size;
|
ev.size = size;
|
||||||
ev.type = type;
|
ev.type = type;
|
||||||
|
@ -64,8 +64,8 @@ static int _event(libtelnet_t *telnet, libtelnet_event_type_t type,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* error generation function */
|
/* error generation function */
|
||||||
static libtelnet_error_t _error(libtelnet_t *telnet, unsigned line,
|
static telnet_error_t _error(telnet_t *telnet, unsigned line,
|
||||||
const char* func, libtelnet_error_t err, int fatal, const char *fmt,
|
const char* func, telnet_error_t err, int fatal, const char *fmt,
|
||||||
...) {
|
...) {
|
||||||
char buffer[512];
|
char buffer[512];
|
||||||
va_list va;
|
va_list va;
|
||||||
|
@ -78,7 +78,7 @@ static libtelnet_error_t _error(libtelnet_t *telnet, unsigned line,
|
||||||
fmt, va);
|
fmt, va);
|
||||||
va_end(va);
|
va_end(va);
|
||||||
|
|
||||||
_event(telnet, fatal ? LIBTELNET_EV_ERROR : LIBTELNET_EV_WARNING, err,
|
_event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
|
||||||
0, (char *)buffer, strlen(buffer));
|
0, (char *)buffer, strlen(buffer));
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -87,52 +87,52 @@ static libtelnet_error_t _error(libtelnet_t *telnet, unsigned line,
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
/* initialize the zlib box for a telnet box; if deflate is non-zero, it
|
/* initialize the zlib box for a telnet box; if deflate is non-zero, it
|
||||||
* initializes zlib for delating (compression), otherwise for inflating
|
* initializes zlib for delating (compression), otherwise for inflating
|
||||||
* (decompression). returns LIBTELNET_EOK on success, something else on
|
* (decompression). returns TELNET_EOK on success, something else on
|
||||||
* failure.
|
* failure.
|
||||||
*/
|
*/
|
||||||
libtelnet_error_t _init_zlib(libtelnet_t *telnet, int deflate, int err_fatal) {
|
telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
|
||||||
z_stream *z;
|
z_stream *z;
|
||||||
int rs;
|
int rs;
|
||||||
|
|
||||||
/* if compression is already enabled, fail loudly */
|
/* if compression is already enabled, fail loudly */
|
||||||
if (telnet->z != 0)
|
if (telnet->z != 0)
|
||||||
return _error(telnet, __LINE__, __func__, LIBTELNET_EBADVAL,
|
return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
|
||||||
err_fatal, "cannot initialize compression twice");
|
err_fatal, "cannot initialize compression twice");
|
||||||
|
|
||||||
/* allocate zstream box */
|
/* allocate zstream box */
|
||||||
if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
|
if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
|
||||||
return _error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, err_fatal,
|
return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
|
||||||
"malloc() failed: %s", strerror(errno));
|
"malloc() failed: %s", strerror(errno));
|
||||||
|
|
||||||
/* initialize */
|
/* initialize */
|
||||||
if (deflate) {
|
if (deflate) {
|
||||||
if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
|
if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
|
||||||
free(z);
|
free(z);
|
||||||
return _error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS,
|
return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
|
||||||
err_fatal, "deflateInit() failed: %s", zError(rs));
|
err_fatal, "deflateInit() failed: %s", zError(rs));
|
||||||
}
|
}
|
||||||
telnet->flags |= LIBTELNET_PFLAG_DEFLATE;
|
telnet->flags |= TELNET_PFLAG_DEFLATE;
|
||||||
} else {
|
} else {
|
||||||
if ((rs = inflateInit(z)) != Z_OK) {
|
if ((rs = inflateInit(z)) != Z_OK) {
|
||||||
free(z);
|
free(z);
|
||||||
return _error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS,
|
return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
|
||||||
err_fatal, "inflateInit() failed: %s", zError(rs));
|
err_fatal, "inflateInit() failed: %s", zError(rs));
|
||||||
}
|
}
|
||||||
telnet->flags &= ~LIBTELNET_PFLAG_DEFLATE;
|
telnet->flags &= ~TELNET_PFLAG_DEFLATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
telnet->z = z;
|
telnet->z = z;
|
||||||
|
|
||||||
return LIBTELNET_EOK;
|
return TELNET_EOK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* push bytes out, compressing them first if need be */
|
/* push bytes out, compressing them first if need be */
|
||||||
static void _send(libtelnet_t *telnet, const char *buffer,
|
static void _send(telnet_t *telnet, const char *buffer,
|
||||||
size_t size) {
|
size_t size) {
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
/* if we have a deflate (compression) zlib box, use it */
|
/* if we have a deflate (compression) zlib box, use it */
|
||||||
if (telnet->z != 0 && telnet->flags & LIBTELNET_PFLAG_DEFLATE) {
|
if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
|
||||||
char deflate_buffer[1024];
|
char deflate_buffer[1024];
|
||||||
int rs;
|
int rs;
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ static void _send(libtelnet_t *telnet, const char *buffer,
|
||||||
while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
|
while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
|
||||||
/* compress */
|
/* compress */
|
||||||
if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
|
if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
|
||||||
_error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, 1,
|
_error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
|
||||||
"deflate() failed: %s", zError(rs));
|
"deflate() failed: %s", zError(rs));
|
||||||
deflateEnd(telnet->z);
|
deflateEnd(telnet->z);
|
||||||
free(telnet->z);
|
free(telnet->z);
|
||||||
|
@ -154,7 +154,7 @@ static void _send(libtelnet_t *telnet, const char *buffer,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_event(telnet, LIBTELNET_EV_SEND, 0, 0, deflate_buffer,
|
_event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
|
||||||
sizeof(deflate_buffer) - telnet->z->avail_out);
|
sizeof(deflate_buffer) - telnet->z->avail_out);
|
||||||
|
|
||||||
/* prepare output buffer for next run */
|
/* prepare output buffer for next run */
|
||||||
|
@ -165,12 +165,12 @@ static void _send(libtelnet_t *telnet, const char *buffer,
|
||||||
/* COMPRESS2 is not negotiated, just send */
|
/* COMPRESS2 is not negotiated, just send */
|
||||||
} else
|
} else
|
||||||
#endif /* HAVE_ZLIB */
|
#endif /* HAVE_ZLIB */
|
||||||
_event(telnet, LIBTELNET_EV_SEND, 0, 0, buffer, size);
|
_event(telnet, TELNET_EV_SEND, 0, 0, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* retrieve RFC1143 option state */
|
/* retrieve RFC1143 option state */
|
||||||
libtelnet_rfc1143_t _get_rfc1143(libtelnet_t *telnet, unsigned char telopt) {
|
telnet_rfc1143_t _get_rfc1143(telnet_t *telnet, unsigned char telopt) {
|
||||||
static const libtelnet_rfc1143_t empty = { 0, 0, 0};
|
static const telnet_rfc1143_t empty = { 0, 0, 0};
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* search for entry */
|
/* search for entry */
|
||||||
|
@ -183,8 +183,8 @@ libtelnet_rfc1143_t _get_rfc1143(libtelnet_t *telnet, unsigned char telopt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save RFC1143 option state */
|
/* save RFC1143 option state */
|
||||||
void _set_rfc1143(libtelnet_t *telnet, libtelnet_rfc1143_t q) {
|
void _set_rfc1143(telnet_t *telnet, telnet_rfc1143_t q) {
|
||||||
libtelnet_rfc1143_t *qtmp;
|
telnet_rfc1143_t *qtmp;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* search for entry */
|
/* search for entry */
|
||||||
|
@ -198,9 +198,9 @@ void _set_rfc1143(libtelnet_t *telnet, libtelnet_rfc1143_t q) {
|
||||||
/* we're going to need to track state for it, so grow the queue
|
/* we're going to need to track state for it, so grow the queue
|
||||||
* and put the telopt into it; bail on allocation error
|
* and put the telopt into it; bail on allocation error
|
||||||
*/
|
*/
|
||||||
if ((qtmp = (libtelnet_rfc1143_t *)malloc(sizeof(
|
if ((qtmp = (telnet_rfc1143_t *)malloc(sizeof(
|
||||||
libtelnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
|
telnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
|
||||||
_error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, 0,
|
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
|
||||||
"malloc() failed: %s", strerror(errno));
|
"malloc() failed: %s", strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -209,24 +209,24 @@ void _set_rfc1143(libtelnet_t *telnet, libtelnet_rfc1143_t q) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* negotiation handling magic for RFC1143 */
|
/* negotiation handling magic for RFC1143 */
|
||||||
static void _negotiate(libtelnet_t *telnet, unsigned char cmd,
|
static void _negotiate(telnet_t *telnet, unsigned char cmd,
|
||||||
unsigned char telopt) {
|
unsigned char telopt) {
|
||||||
libtelnet_rfc1143_t q;
|
telnet_rfc1143_t q;
|
||||||
|
|
||||||
/* in PROXY mode, just pass it thru and do nothing */
|
/* in PROXY mode, just pass it thru and do nothing */
|
||||||
if (telnet->flags & LIBTELNET_FLAG_PROXY) {
|
if (telnet->flags & TELNET_FLAG_PROXY) {
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case LIBTELNET_WILL:
|
case TELNET_WILL:
|
||||||
_event(telnet, LIBTELNET_EV_WILL, 0, telopt, 0, 0);
|
_event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_WONT:
|
case TELNET_WONT:
|
||||||
_event(telnet, LIBTELNET_EV_WONT, 0, telopt, 0, 0);
|
_event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_DO:
|
case TELNET_DO:
|
||||||
_event(telnet, LIBTELNET_EV_DO, 0, telopt, 0, 0);
|
_event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_DONT:
|
case TELNET_DONT:
|
||||||
_event(telnet, LIBTELNET_EV_DONT, 0, telopt, 0, 0);
|
_event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -238,28 +238,28 @@ static void _negotiate(libtelnet_t *telnet, unsigned char cmd,
|
||||||
/* start processing... */
|
/* start processing... */
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
/* request to enable option on remote end or confirm DO */
|
/* request to enable option on remote end or confirm DO */
|
||||||
case LIBTELNET_WILL:
|
case TELNET_WILL:
|
||||||
switch (q.him) {
|
switch (q.him) {
|
||||||
case RFC1143_NO:
|
case RFC1143_NO:
|
||||||
if (_event(telnet, LIBTELNET_EV_WILL, cmd, telopt, 0, 0) == 1) {
|
if (_event(telnet, TELNET_EV_WILL, cmd, telopt, 0, 0) == 1) {
|
||||||
q.him = RFC1143_YES;
|
q.him = RFC1143_YES;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
libtelnet_send_telopt(telnet, LIBTELNET_DO, telopt);
|
telnet_send_telopt(telnet, TELNET_DO, telopt);
|
||||||
} else
|
} else
|
||||||
libtelnet_send_telopt(telnet, LIBTELNET_DONT, telopt);
|
telnet_send_telopt(telnet, TELNET_DONT, telopt);
|
||||||
break;
|
break;
|
||||||
case RFC1143_YES:
|
case RFC1143_YES:
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTNO:
|
case RFC1143_WANTNO:
|
||||||
q.him = RFC1143_NO;
|
q.him = RFC1143_NO;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
|
_error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
|
||||||
"DONT answered by WILL");
|
"DONT answered by WILL");
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTNO_OP:
|
case RFC1143_WANTNO_OP:
|
||||||
q.him = RFC1143_YES;
|
q.him = RFC1143_YES;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
|
_error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
|
||||||
"DONT answered by WILL");
|
"DONT answered by WILL");
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTYES:
|
case RFC1143_WANTYES:
|
||||||
|
@ -269,33 +269,33 @@ static void _negotiate(libtelnet_t *telnet, unsigned char cmd,
|
||||||
case RFC1143_WANTYES_OP:
|
case RFC1143_WANTYES_OP:
|
||||||
q.him = RFC1143_WANTNO;
|
q.him = RFC1143_WANTNO;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
libtelnet_send_telopt(telnet, LIBTELNET_DONT, telopt);
|
telnet_send_telopt(telnet, TELNET_DONT, telopt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* request to disable option on remote end, confirm DONT, reject DO */
|
/* request to disable option on remote end, confirm DONT, reject DO */
|
||||||
case LIBTELNET_WONT:
|
case TELNET_WONT:
|
||||||
switch (q.him) {
|
switch (q.him) {
|
||||||
case RFC1143_NO:
|
case RFC1143_NO:
|
||||||
break;
|
break;
|
||||||
case RFC1143_YES:
|
case RFC1143_YES:
|
||||||
q.him = RFC1143_NO;
|
q.him = RFC1143_NO;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
libtelnet_send_telopt(telnet, LIBTELNET_DONT, telopt);
|
telnet_send_telopt(telnet, TELNET_DONT, telopt);
|
||||||
_event(telnet, LIBTELNET_EV_WONT, 0, telopt,
|
_event(telnet, TELNET_EV_WONT, 0, telopt,
|
||||||
0, 0);
|
0, 0);
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTNO:
|
case RFC1143_WANTNO:
|
||||||
q.him = RFC1143_NO;
|
q.him = RFC1143_NO;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_event(telnet, LIBTELNET_EV_WONT, 0, telopt,
|
_event(telnet, TELNET_EV_WONT, 0, telopt,
|
||||||
0, 0);
|
0, 0);
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTNO_OP:
|
case RFC1143_WANTNO_OP:
|
||||||
q.him = RFC1143_WANTYES;
|
q.him = RFC1143_WANTYES;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_event(telnet, LIBTELNET_EV_DO, 0, telopt,
|
_event(telnet, TELNET_EV_DO, 0, telopt,
|
||||||
0, 0);
|
0, 0);
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTYES:
|
case RFC1143_WANTYES:
|
||||||
|
@ -307,28 +307,28 @@ static void _negotiate(libtelnet_t *telnet, unsigned char cmd,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* request to enable option on local end or confirm WILL */
|
/* request to enable option on local end or confirm WILL */
|
||||||
case LIBTELNET_DO:
|
case TELNET_DO:
|
||||||
switch (q.us) {
|
switch (q.us) {
|
||||||
case RFC1143_NO:
|
case RFC1143_NO:
|
||||||
if (_event(telnet, LIBTELNET_EV_DO, cmd, telopt, 0, 0) == 1) {
|
if (_event(telnet, TELNET_EV_DO, cmd, telopt, 0, 0) == 1) {
|
||||||
q.us = RFC1143_YES;
|
q.us = RFC1143_YES;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
libtelnet_send_telopt(telnet, LIBTELNET_WILL, telopt);
|
telnet_send_telopt(telnet, TELNET_WILL, telopt);
|
||||||
} else
|
} else
|
||||||
libtelnet_send_telopt(telnet, LIBTELNET_WONT, telopt);
|
telnet_send_telopt(telnet, TELNET_WONT, telopt);
|
||||||
break;
|
break;
|
||||||
case RFC1143_YES:
|
case RFC1143_YES:
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTNO:
|
case RFC1143_WANTNO:
|
||||||
q.us = RFC1143_NO;
|
q.us = RFC1143_NO;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
|
_error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
|
||||||
"WONT answered by DO");
|
"WONT answered by DO");
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTNO_OP:
|
case RFC1143_WANTNO_OP:
|
||||||
q.us = RFC1143_YES;
|
q.us = RFC1143_YES;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
|
_error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
|
||||||
"WONT answered by DO");
|
"WONT answered by DO");
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTYES:
|
case RFC1143_WANTYES:
|
||||||
|
@ -338,31 +338,31 @@ static void _negotiate(libtelnet_t *telnet, unsigned char cmd,
|
||||||
case RFC1143_WANTYES_OP:
|
case RFC1143_WANTYES_OP:
|
||||||
q.us = RFC1143_WANTNO;
|
q.us = RFC1143_WANTNO;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
libtelnet_send_telopt(telnet, LIBTELNET_WONT, telopt);
|
telnet_send_telopt(telnet, TELNET_WONT, telopt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* request to disable option on local end, confirm WONT, reject WILL */
|
/* request to disable option on local end, confirm WONT, reject WILL */
|
||||||
case LIBTELNET_DONT:
|
case TELNET_DONT:
|
||||||
switch (q.us) {
|
switch (q.us) {
|
||||||
case RFC1143_NO:
|
case RFC1143_NO:
|
||||||
break;
|
break;
|
||||||
case RFC1143_YES:
|
case RFC1143_YES:
|
||||||
q.us = RFC1143_NO;
|
q.us = RFC1143_NO;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
libtelnet_send_telopt(telnet, LIBTELNET_WONT, telopt);
|
telnet_send_telopt(telnet, TELNET_WONT, telopt);
|
||||||
_event(telnet, LIBTELNET_EV_DONT, 0, telopt, 0, 0);
|
_event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTNO:
|
case RFC1143_WANTNO:
|
||||||
q.us = RFC1143_NO;
|
q.us = RFC1143_NO;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_event(telnet, LIBTELNET_EV_WONT, 0, telopt, 0, 0);
|
_event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTNO_OP:
|
case RFC1143_WANTNO_OP:
|
||||||
q.us = RFC1143_WANTYES;
|
q.us = RFC1143_WANTYES;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_event(telnet, LIBTELNET_EV_WILL, 0, telopt, 0, 0);
|
_event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTYES:
|
case RFC1143_WANTYES:
|
||||||
case RFC1143_WANTYES_OP:
|
case RFC1143_WANTYES_OP:
|
||||||
|
@ -375,16 +375,16 @@ static void _negotiate(libtelnet_t *telnet, unsigned char cmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize a telnet state tracker */
|
/* initialize a telnet state tracker */
|
||||||
void libtelnet_init(libtelnet_t *telnet, libtelnet_event_handler_t eh,
|
void telnet_init(telnet_t *telnet, telnet_event_handler_t eh,
|
||||||
unsigned char flags, void *user_data) {
|
unsigned char flags, void *user_data) {
|
||||||
memset(telnet, 0, sizeof(libtelnet_t));
|
memset(telnet, 0, sizeof(telnet_t));
|
||||||
telnet->ud = user_data;
|
telnet->ud = user_data;
|
||||||
telnet->eh = eh;
|
telnet->eh = eh;
|
||||||
telnet->flags = flags;
|
telnet->flags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* free up any memory allocated by a state tracker */
|
/* free up any memory allocated by a state tracker */
|
||||||
void libtelnet_free(libtelnet_t *telnet) {
|
void telnet_free(telnet_t *telnet) {
|
||||||
/* free sub-request buffer */
|
/* free sub-request buffer */
|
||||||
if (telnet->buffer != 0) {
|
if (telnet->buffer != 0) {
|
||||||
free(telnet->buffer);
|
free(telnet->buffer);
|
||||||
|
@ -396,7 +396,7 @@ void libtelnet_free(libtelnet_t *telnet) {
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
/* free zlib box */
|
/* free zlib box */
|
||||||
if (telnet->z != 0) {
|
if (telnet->z != 0) {
|
||||||
if (telnet->flags & LIBTELNET_PFLAG_DEFLATE)
|
if (telnet->flags & TELNET_PFLAG_DEFLATE)
|
||||||
deflateEnd(telnet->z);
|
deflateEnd(telnet->z);
|
||||||
else
|
else
|
||||||
inflateEnd(telnet->z);
|
inflateEnd(telnet->z);
|
||||||
|
@ -414,7 +414,7 @@ void libtelnet_free(libtelnet_t *telnet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* push a byte into the telnet buffer */
|
/* push a byte into the telnet buffer */
|
||||||
static libtelnet_error_t _buffer_byte(libtelnet_t *telnet,
|
static telnet_error_t _buffer_byte(telnet_t *telnet,
|
||||||
unsigned char byte) {
|
unsigned char byte) {
|
||||||
char *new_buffer;
|
char *new_buffer;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
@ -429,20 +429,20 @@ static libtelnet_error_t _buffer_byte(libtelnet_t *telnet,
|
||||||
|
|
||||||
/* overflow -- can't grow any more */
|
/* overflow -- can't grow any more */
|
||||||
if (i >= _buffer_sizes_count - 1) {
|
if (i >= _buffer_sizes_count - 1) {
|
||||||
_error(telnet, __LINE__, __func__, LIBTELNET_EOVERFLOW, 0,
|
_error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
|
||||||
"subnegotiation buffer size limit reached");
|
"subnegotiation buffer size limit reached");
|
||||||
libtelnet_free(telnet);
|
telnet_free(telnet);
|
||||||
return LIBTELNET_EOVERFLOW;
|
return TELNET_EOVERFLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (re)allocate buffer */
|
/* (re)allocate buffer */
|
||||||
new_buffer = (char *)realloc(telnet->buffer,
|
new_buffer = (char *)realloc(telnet->buffer,
|
||||||
_buffer_sizes[i + 1]);
|
_buffer_sizes[i + 1]);
|
||||||
if (new_buffer == 0) {
|
if (new_buffer == 0) {
|
||||||
_error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, 0,
|
_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
|
||||||
"realloc() failed");
|
"realloc() failed");
|
||||||
libtelnet_free(telnet);
|
telnet_free(telnet);
|
||||||
return LIBTELNET_ENOMEM;
|
return TELNET_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
telnet->buffer = new_buffer;
|
telnet->buffer = new_buffer;
|
||||||
|
@ -451,10 +451,10 @@ static libtelnet_error_t _buffer_byte(libtelnet_t *telnet,
|
||||||
|
|
||||||
/* push the byte, all set */
|
/* push the byte, all set */
|
||||||
telnet->buffer[telnet->buffer_pos++] = byte;
|
telnet->buffer[telnet->buffer_pos++] = byte;
|
||||||
return LIBTELNET_EOK;
|
return TELNET_EOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _process(libtelnet_t *telnet, const char *buffer,
|
static void _process(telnet_t *telnet, const char *buffer,
|
||||||
size_t size) {
|
size_t size) {
|
||||||
unsigned char byte;
|
unsigned char byte;
|
||||||
size_t i, start;
|
size_t i, start;
|
||||||
|
@ -462,146 +462,146 @@ static void _process(libtelnet_t *telnet, const char *buffer,
|
||||||
byte = buffer[i];
|
byte = buffer[i];
|
||||||
switch (telnet->state) {
|
switch (telnet->state) {
|
||||||
/* regular data */
|
/* regular data */
|
||||||
case LIBTELNET_STATE_DATA:
|
case TELNET_STATE_DATA:
|
||||||
/* on an IAC byte, pass through all pending bytes and
|
/* on an IAC byte, pass through all pending bytes and
|
||||||
* switch states */
|
* switch states */
|
||||||
if (byte == LIBTELNET_IAC) {
|
if (byte == TELNET_IAC) {
|
||||||
if (i != start)
|
if (i != start)
|
||||||
_event(telnet, LIBTELNET_EV_DATA, 0, 0, &buffer[start],
|
_event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
|
||||||
i - start);
|
i - start);
|
||||||
telnet->state = LIBTELNET_STATE_IAC;
|
telnet->state = TELNET_STATE_IAC;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* IAC command */
|
/* IAC command */
|
||||||
case LIBTELNET_STATE_IAC:
|
case TELNET_STATE_IAC:
|
||||||
switch (byte) {
|
switch (byte) {
|
||||||
/* subnegotiation */
|
/* subnegotiation */
|
||||||
case LIBTELNET_SB:
|
case TELNET_SB:
|
||||||
telnet->state = LIBTELNET_STATE_SB;
|
telnet->state = TELNET_STATE_SB;
|
||||||
break;
|
break;
|
||||||
/* negotiation commands */
|
/* negotiation commands */
|
||||||
case LIBTELNET_WILL:
|
case TELNET_WILL:
|
||||||
telnet->state = LIBTELNET_STATE_WILL;
|
telnet->state = TELNET_STATE_WILL;
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_WONT:
|
case TELNET_WONT:
|
||||||
telnet->state = LIBTELNET_STATE_WONT;
|
telnet->state = TELNET_STATE_WONT;
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_DO:
|
case TELNET_DO:
|
||||||
telnet->state = LIBTELNET_STATE_DO;
|
telnet->state = TELNET_STATE_DO;
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_DONT:
|
case TELNET_DONT:
|
||||||
telnet->state = LIBTELNET_STATE_DONT;
|
telnet->state = TELNET_STATE_DONT;
|
||||||
break;
|
break;
|
||||||
/* IAC escaping */
|
/* IAC escaping */
|
||||||
case LIBTELNET_IAC:
|
case TELNET_IAC:
|
||||||
_event(telnet, LIBTELNET_EV_DATA, 0, 0, (char*)&byte, 1);
|
_event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1);
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
telnet->state = LIBTELNET_STATE_DATA;
|
telnet->state = TELNET_STATE_DATA;
|
||||||
break;
|
break;
|
||||||
/* some other command */
|
/* some other command */
|
||||||
default:
|
default:
|
||||||
_event(telnet, LIBTELNET_EV_IAC, byte, 0, 0, 0);
|
_event(telnet, TELNET_EV_IAC, byte, 0, 0, 0);
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
telnet->state = LIBTELNET_STATE_DATA;
|
telnet->state = TELNET_STATE_DATA;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* negotiation commands */
|
/* negotiation commands */
|
||||||
case LIBTELNET_STATE_DO:
|
case TELNET_STATE_DO:
|
||||||
_negotiate(telnet, LIBTELNET_DO, byte);
|
_negotiate(telnet, TELNET_DO, byte);
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
telnet->state = LIBTELNET_STATE_DATA;
|
telnet->state = TELNET_STATE_DATA;
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_STATE_DONT:
|
case TELNET_STATE_DONT:
|
||||||
_negotiate(telnet, LIBTELNET_DONT, byte);
|
_negotiate(telnet, TELNET_DONT, byte);
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
telnet->state = LIBTELNET_STATE_DATA;
|
telnet->state = TELNET_STATE_DATA;
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_STATE_WILL:
|
case TELNET_STATE_WILL:
|
||||||
_negotiate(telnet, LIBTELNET_WILL, byte);
|
_negotiate(telnet, TELNET_WILL, byte);
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
telnet->state = LIBTELNET_STATE_DATA;
|
telnet->state = TELNET_STATE_DATA;
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_STATE_WONT:
|
case TELNET_STATE_WONT:
|
||||||
_negotiate(telnet, LIBTELNET_WONT, byte);
|
_negotiate(telnet, TELNET_WONT, byte);
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
telnet->state = LIBTELNET_STATE_DATA;
|
telnet->state = TELNET_STATE_DATA;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* subnegotiation -- determine subnegotiation telopt */
|
/* subnegotiation -- determine subnegotiation telopt */
|
||||||
case LIBTELNET_STATE_SB:
|
case TELNET_STATE_SB:
|
||||||
telnet->sb_telopt = byte;
|
telnet->sb_telopt = byte;
|
||||||
telnet->buffer_pos = 0;
|
telnet->buffer_pos = 0;
|
||||||
telnet->state = LIBTELNET_STATE_SB_DATA;
|
telnet->state = TELNET_STATE_SB_DATA;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* subnegotiation -- buffer bytes until end request */
|
/* subnegotiation -- buffer bytes until end request */
|
||||||
case LIBTELNET_STATE_SB_DATA:
|
case TELNET_STATE_SB_DATA:
|
||||||
/* IAC command in subnegotiation -- either IAC SE or IAC IAC */
|
/* IAC command in subnegotiation -- either IAC SE or IAC IAC */
|
||||||
if (byte == LIBTELNET_IAC) {
|
if (byte == TELNET_IAC) {
|
||||||
telnet->state = LIBTELNET_STATE_SB_DATA_IAC;
|
telnet->state = TELNET_STATE_SB_DATA_IAC;
|
||||||
/* buffer the byte, or bail if we can't */
|
/* buffer the byte, or bail if we can't */
|
||||||
} else if (_buffer_byte(telnet, byte) != LIBTELNET_EOK) {
|
} else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
telnet->state = LIBTELNET_STATE_DATA;
|
telnet->state = TELNET_STATE_DATA;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* IAC escaping inside a subnegotiation */
|
/* IAC escaping inside a subnegotiation */
|
||||||
case LIBTELNET_STATE_SB_DATA_IAC:
|
case TELNET_STATE_SB_DATA_IAC:
|
||||||
switch (byte) {
|
switch (byte) {
|
||||||
/* end subnegotiation */
|
/* end subnegotiation */
|
||||||
case LIBTELNET_SE:
|
case TELNET_SE:
|
||||||
/* return to default state */
|
/* return to default state */
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
telnet->state = LIBTELNET_STATE_DATA;
|
telnet->state = TELNET_STATE_DATA;
|
||||||
|
|
||||||
/* invoke callback */
|
/* invoke callback */
|
||||||
_event(telnet, LIBTELNET_EV_SUBNEGOTIATION, 0,
|
_event(telnet, TELNET_EV_SUBNEGOTIATION, 0,
|
||||||
telnet->sb_telopt, telnet->buffer, telnet->buffer_pos);
|
telnet->sb_telopt, telnet->buffer, telnet->buffer_pos);
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
/* received COMPRESS2 begin marker, setup our zlib box and
|
/* received COMPRESS2 begin marker, setup our zlib box and
|
||||||
* start handling the compressed stream if it's not already.
|
* start handling the compressed stream if it's not already.
|
||||||
*/
|
*/
|
||||||
if (telnet->sb_telopt == LIBTELNET_TELOPT_COMPRESS2) {
|
if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
|
||||||
if (_init_zlib(telnet, 0, 1) != LIBTELNET_EOK)
|
if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* notify app that compression was enabled */
|
/* notify app that compression was enabled */
|
||||||
_event(telnet, LIBTELNET_EV_COMPRESS, 1, 0, 0, 0);
|
_event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
|
||||||
|
|
||||||
/* any remaining bytes in the buffer are compressed.
|
/* any remaining bytes in the buffer are compressed.
|
||||||
* we have to re-invoke libtelnet_push to get those
|
* we have to re-invoke telnet_push to get those
|
||||||
* bytes inflated and abort trying to process the
|
* bytes inflated and abort trying to process the
|
||||||
* remaining compressed bytes in the current _process
|
* remaining compressed bytes in the current _process
|
||||||
* buffer argument
|
* buffer argument
|
||||||
*/
|
*/
|
||||||
libtelnet_push(telnet, &buffer[start], size - start);
|
telnet_push(telnet, &buffer[start], size - start);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif /* HAVE_ZLIB */
|
#endif /* HAVE_ZLIB */
|
||||||
|
|
||||||
break;
|
break;
|
||||||
/* escaped IAC byte */
|
/* escaped IAC byte */
|
||||||
case LIBTELNET_IAC:
|
case TELNET_IAC:
|
||||||
/* push IAC into buffer */
|
/* push IAC into buffer */
|
||||||
if (_buffer_byte(telnet, LIBTELNET_IAC) !=
|
if (_buffer_byte(telnet, TELNET_IAC) !=
|
||||||
LIBTELNET_EOK) {
|
TELNET_EOK) {
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
telnet->state = LIBTELNET_STATE_DATA;
|
telnet->state = TELNET_STATE_DATA;
|
||||||
} else {
|
} else {
|
||||||
telnet->state = LIBTELNET_STATE_SB_DATA;
|
telnet->state = TELNET_STATE_SB_DATA;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* something else -- protocol error */
|
/* something else -- protocol error */
|
||||||
default:
|
default:
|
||||||
_error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
|
_error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
|
||||||
"unexpected byte after IAC inside SB: %d",
|
"unexpected byte after IAC inside SB: %d",
|
||||||
byte);
|
byte);
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
telnet->state = LIBTELNET_STATE_DATA;
|
telnet->state = TELNET_STATE_DATA;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -609,16 +609,16 @@ static void _process(libtelnet_t *telnet, const char *buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pass through any remaining bytes */
|
/* pass through any remaining bytes */
|
||||||
if (telnet->state == LIBTELNET_STATE_DATA && i != start)
|
if (telnet->state == TELNET_STATE_DATA && i != start)
|
||||||
_event(telnet, LIBTELNET_EV_DATA, 0, 0, buffer + start, i - start);
|
_event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* push a bytes into the state tracker */
|
/* push a bytes into the state tracker */
|
||||||
void libtelnet_push(libtelnet_t *telnet, const char *buffer,
|
void telnet_push(telnet_t *telnet, const char *buffer,
|
||||||
size_t size) {
|
size_t size) {
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
/* if we have an inflate (decompression) zlib stream, use it */
|
/* if we have an inflate (decompression) zlib stream, use it */
|
||||||
if (telnet->z != 0 && !(telnet->flags & LIBTELNET_PFLAG_DEFLATE)) {
|
if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
|
||||||
char inflate_buffer[4096];
|
char inflate_buffer[4096];
|
||||||
int rs;
|
int rs;
|
||||||
|
|
||||||
|
@ -640,7 +640,7 @@ void libtelnet_push(libtelnet_t *telnet, const char *buffer,
|
||||||
_process(telnet, inflate_buffer, sizeof(inflate_buffer) -
|
_process(telnet, inflate_buffer, sizeof(inflate_buffer) -
|
||||||
telnet->z->avail_out);
|
telnet->z->avail_out);
|
||||||
else
|
else
|
||||||
_error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, 1,
|
_error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
|
||||||
"inflate() failed: %s", zError(rs));
|
"inflate() failed: %s", zError(rs));
|
||||||
|
|
||||||
/* prepare output buffer for next run */
|
/* prepare output buffer for next run */
|
||||||
|
@ -649,7 +649,7 @@ void libtelnet_push(libtelnet_t *telnet, const char *buffer,
|
||||||
|
|
||||||
/* on error (or on end of stream) disable further inflation */
|
/* on error (or on end of stream) disable further inflation */
|
||||||
if (rs != Z_OK) {
|
if (rs != Z_OK) {
|
||||||
_event(telnet, LIBTELNET_EV_COMPRESS, 0, 0, 0, 0);
|
_event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0);
|
||||||
|
|
||||||
inflateEnd(telnet->z);
|
inflateEnd(telnet->z);
|
||||||
free(telnet->z);
|
free(telnet->z);
|
||||||
|
@ -665,26 +665,26 @@ void libtelnet_push(libtelnet_t *telnet, const char *buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send an iac command */
|
/* send an iac command */
|
||||||
void libtelnet_send_command(libtelnet_t *telnet, unsigned char cmd) {
|
void telnet_send_command(telnet_t *telnet, unsigned char cmd) {
|
||||||
char bytes[2] = { LIBTELNET_IAC, cmd };
|
char bytes[2] = { TELNET_IAC, cmd };
|
||||||
_send(telnet, bytes, 2);
|
_send(telnet, bytes, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send an iac command with telopt */
|
/* send an iac command with telopt */
|
||||||
void libtelnet_send_telopt(libtelnet_t *telnet, unsigned char cmd,
|
void telnet_send_telopt(telnet_t *telnet, unsigned char cmd,
|
||||||
unsigned char telopt) {
|
unsigned char telopt) {
|
||||||
char bytes[3] = { LIBTELNET_IAC, cmd, telopt };
|
char bytes[3] = { TELNET_IAC, cmd, telopt };
|
||||||
_send(telnet, bytes, 3);
|
_send(telnet, bytes, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send negotiation */
|
/* send negotiation */
|
||||||
void libtelnet_send_negotiate(libtelnet_t *telnet, unsigned char cmd,
|
void telnet_send_negotiate(telnet_t *telnet, unsigned char cmd,
|
||||||
unsigned char telopt) {
|
unsigned char telopt) {
|
||||||
libtelnet_rfc1143_t q;
|
telnet_rfc1143_t q;
|
||||||
|
|
||||||
/* if we're in proxy mode, just send it now */
|
/* if we're in proxy mode, just send it now */
|
||||||
if (telnet->flags & LIBTELNET_FLAG_PROXY) {
|
if (telnet->flags & TELNET_FLAG_PROXY) {
|
||||||
char bytes[3] = { LIBTELNET_IAC, cmd, telopt };
|
char bytes[3] = { TELNET_IAC, cmd, telopt };
|
||||||
_send(telnet, bytes, 3);
|
_send(telnet, bytes, 3);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -694,12 +694,12 @@ void libtelnet_send_negotiate(libtelnet_t *telnet, unsigned char cmd,
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
/* advertise willingess to support an option */
|
/* advertise willingess to support an option */
|
||||||
case LIBTELNET_WILL:
|
case TELNET_WILL:
|
||||||
switch (q.us) {
|
switch (q.us) {
|
||||||
case RFC1143_NO:
|
case RFC1143_NO:
|
||||||
q.us = RFC1143_WANTYES;
|
q.us = RFC1143_WANTYES;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_negotiate(telnet, LIBTELNET_WILL, telopt);
|
_negotiate(telnet, TELNET_WILL, telopt);
|
||||||
break;
|
break;
|
||||||
case RFC1143_YES:
|
case RFC1143_YES:
|
||||||
break;
|
break;
|
||||||
|
@ -719,14 +719,14 @@ void libtelnet_send_negotiate(libtelnet_t *telnet, unsigned char cmd,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* force turn-off of locally enabled option */
|
/* force turn-off of locally enabled option */
|
||||||
case LIBTELNET_WONT:
|
case TELNET_WONT:
|
||||||
switch (q.us) {
|
switch (q.us) {
|
||||||
case RFC1143_NO:
|
case RFC1143_NO:
|
||||||
break;
|
break;
|
||||||
case RFC1143_YES:
|
case RFC1143_YES:
|
||||||
q.us = RFC1143_WANTNO;
|
q.us = RFC1143_WANTNO;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_negotiate(telnet, LIBTELNET_WONT, telopt);
|
_negotiate(telnet, TELNET_WONT, telopt);
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTNO:
|
case RFC1143_WANTNO:
|
||||||
break;
|
break;
|
||||||
|
@ -744,12 +744,12 @@ void libtelnet_send_negotiate(libtelnet_t *telnet, unsigned char cmd,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* ask remote end to enable an option */
|
/* ask remote end to enable an option */
|
||||||
case LIBTELNET_DO:
|
case TELNET_DO:
|
||||||
switch (q.him) {
|
switch (q.him) {
|
||||||
case RFC1143_NO:
|
case RFC1143_NO:
|
||||||
q.him = RFC1143_WANTYES;
|
q.him = RFC1143_WANTYES;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_negotiate(telnet, LIBTELNET_DO, telopt);
|
_negotiate(telnet, TELNET_DO, telopt);
|
||||||
break;
|
break;
|
||||||
case RFC1143_YES:
|
case RFC1143_YES:
|
||||||
break;
|
break;
|
||||||
|
@ -769,14 +769,14 @@ void libtelnet_send_negotiate(libtelnet_t *telnet, unsigned char cmd,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* demand remote end disable an option */
|
/* demand remote end disable an option */
|
||||||
case LIBTELNET_DONT:
|
case TELNET_DONT:
|
||||||
switch (q.him) {
|
switch (q.him) {
|
||||||
case RFC1143_NO:
|
case RFC1143_NO:
|
||||||
break;
|
break;
|
||||||
case RFC1143_YES:
|
case RFC1143_YES:
|
||||||
q.him = RFC1143_WANTNO;
|
q.him = RFC1143_WANTNO;
|
||||||
_set_rfc1143(telnet, q);
|
_set_rfc1143(telnet, q);
|
||||||
_negotiate(telnet, LIBTELNET_DONT, telopt);
|
_negotiate(telnet, TELNET_DONT, telopt);
|
||||||
break;
|
break;
|
||||||
case RFC1143_WANTNO:
|
case RFC1143_WANTNO:
|
||||||
break;
|
break;
|
||||||
|
@ -796,20 +796,20 @@ void libtelnet_send_negotiate(libtelnet_t *telnet, unsigned char cmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send non-command data (escapes IAC bytes) */
|
/* send non-command data (escapes IAC bytes) */
|
||||||
void libtelnet_send_data(libtelnet_t *telnet, const char *buffer,
|
void telnet_send_data(telnet_t *telnet, const char *buffer,
|
||||||
size_t size) {
|
size_t size) {
|
||||||
size_t i, l;
|
size_t i, l;
|
||||||
|
|
||||||
for (l = i = 0; i != size; ++i) {
|
for (l = i = 0; i != size; ++i) {
|
||||||
/* dump prior portion of text, send escaped bytes */
|
/* dump prior portion of text, send escaped bytes */
|
||||||
if (buffer[i] == LIBTELNET_IAC) {
|
if (buffer[i] == TELNET_IAC) {
|
||||||
/* dump prior text if any */
|
/* dump prior text if any */
|
||||||
if (i != l)
|
if (i != l)
|
||||||
_send(telnet, buffer + l, i - l);
|
_send(telnet, buffer + l, i - l);
|
||||||
l = i + 1;
|
l = i + 1;
|
||||||
|
|
||||||
/* send escape */
|
/* send escape */
|
||||||
libtelnet_send_command(telnet, LIBTELNET_IAC);
|
telnet_send_command(telnet, TELNET_IAC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,67 +819,50 @@ void libtelnet_send_data(libtelnet_t *telnet, const char *buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send sub-request */
|
/* send sub-request */
|
||||||
void libtelnet_send_subnegotiation(libtelnet_t *telnet, unsigned char telopt,
|
void telnet_send_subnegotiation(telnet_t *telnet, unsigned char telopt,
|
||||||
const char *buffer, size_t size) {
|
const char *buffer, size_t size) {
|
||||||
libtelnet_send_telopt(telnet, LIBTELNET_SB, telopt);
|
telnet_send_telopt(telnet, TELNET_SB, telopt);
|
||||||
libtelnet_send_data(telnet, buffer, size);
|
telnet_send_data(telnet, buffer, size);
|
||||||
libtelnet_send_command(telnet, LIBTELNET_SE);
|
telnet_send_command(telnet, TELNET_SE);
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
/* if we're a proxy and we just sent the COMPRESS2 marker, we must
|
/* if we're a proxy and we just sent the COMPRESS2 marker, we must
|
||||||
* make sure all further data is compressed if not already.
|
* make sure all further data is compressed if not already.
|
||||||
*/
|
*/
|
||||||
if (telnet->flags & LIBTELNET_FLAG_PROXY &&
|
if (telnet->flags & TELNET_FLAG_PROXY &&
|
||||||
telopt == LIBTELNET_TELOPT_COMPRESS2) {
|
telopt == TELNET_TELOPT_COMPRESS2) {
|
||||||
|
|
||||||
if (_init_zlib(telnet, 1, 1) != LIBTELNET_EOK)
|
if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* notify app that compression was enabled */
|
/* notify app that compression was enabled */
|
||||||
_event(telnet, LIBTELNET_EV_COMPRESS, 1, 0, 0, 0);
|
_event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
|
||||||
}
|
}
|
||||||
#endif /* HAVE_ZLIB */
|
#endif /* HAVE_ZLIB */
|
||||||
}
|
}
|
||||||
|
|
||||||
void libtelnet_begin_compress2(libtelnet_t *telnet) {
|
void telnet_begin_compress2(telnet_t *telnet) {
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
static const char compress2[] = { LIBTELNET_IAC, LIBTELNET_SB,
|
static const char compress2[] = { TELNET_IAC, TELNET_SB,
|
||||||
LIBTELNET_TELOPT_COMPRESS2, LIBTELNET_IAC, LIBTELNET_SE };
|
TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
|
||||||
|
|
||||||
/* attempt to create output stream first, bail if we can't */
|
/* attempt to create output stream first, bail if we can't */
|
||||||
if (_init_zlib(telnet, 1, 0) != LIBTELNET_EOK)
|
if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* send compression marker. we send directly to the event handler
|
/* send compression marker. we send directly to the event handler
|
||||||
* instead of passing through _send because _send would result in
|
* instead of passing through _send because _send would result in
|
||||||
* the compress marker itself being compressed.
|
* the compress marker itself being compressed.
|
||||||
*/
|
*/
|
||||||
_event(telnet, LIBTELNET_EV_SEND, 0, 0, compress2, sizeof(compress2));
|
_event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2));
|
||||||
|
|
||||||
/* notify app that compression was successfully enabled */
|
/* notify app that compression was successfully enabled */
|
||||||
_event(telnet, LIBTELNET_EV_COMPRESS, 1, 0, 0, 0);
|
_event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
|
||||||
#endif /* HAVE_ZLIB */
|
#endif /* HAVE_ZLIB */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send formatted data through libtelnet_send_data */
|
|
||||||
int libtelnet_printf(libtelnet_t *telnet, const char *fmt, ...) {
|
|
||||||
char buffer[4096];
|
|
||||||
va_list va;
|
|
||||||
int rs;
|
|
||||||
|
|
||||||
/* format */
|
|
||||||
va_start(va, fmt);
|
|
||||||
rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
|
|
||||||
/* send */
|
|
||||||
libtelnet_send_data(telnet, (char *)buffer, rs);
|
|
||||||
|
|
||||||
return rs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send formatted data with \r and \n translation in addition to IAC IAC */
|
/* send formatted data with \r and \n translation in addition to IAC IAC */
|
||||||
int libtelnet_printf2(libtelnet_t *telnet, const char *fmt, ...) {
|
int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
|
||||||
static const char CRLF[] = { '\r', '\n' };
|
static const char CRLF[] = { '\r', '\n' };
|
||||||
static const char CRNUL[] = { '\r', '\0' };
|
static const char CRNUL[] = { '\r', '\0' };
|
||||||
char buffer[4096];
|
char buffer[4096];
|
||||||
|
@ -894,7 +877,7 @@ int libtelnet_printf2(libtelnet_t *telnet, const char *fmt, ...) {
|
||||||
/* send */
|
/* send */
|
||||||
for (l = i = 0; i != rs; ++i) {
|
for (l = i = 0; i != rs; ++i) {
|
||||||
/* special characters */
|
/* special characters */
|
||||||
if (buffer[i] == LIBTELNET_IAC || buffer[i] == '\r' ||
|
if (buffer[i] == TELNET_IAC || buffer[i] == '\r' ||
|
||||||
buffer[i] == '\n') {
|
buffer[i] == '\n') {
|
||||||
/* dump prior portion of text */
|
/* dump prior portion of text */
|
||||||
if (i != l)
|
if (i != l)
|
||||||
|
@ -902,8 +885,8 @@ int libtelnet_printf2(libtelnet_t *telnet, const char *fmt, ...) {
|
||||||
l = i + 1;
|
l = i + 1;
|
||||||
|
|
||||||
/* IAC -> IAC IAC */
|
/* IAC -> IAC IAC */
|
||||||
if (buffer[i] == LIBTELNET_IAC)
|
if (buffer[i] == TELNET_IAC)
|
||||||
libtelnet_send_command(telnet, LIBTELNET_IAC);
|
telnet_send_command(telnet, TELNET_IAC);
|
||||||
/* automatic translation of \r -> CRNUL */
|
/* automatic translation of \r -> CRNUL */
|
||||||
else if (buffer[i] == '\r')
|
else if (buffer[i] == '\r')
|
||||||
_send(telnet, CRNUL, 2);
|
_send(telnet, CRNUL, 2);
|
||||||
|
@ -919,3 +902,20 @@ int libtelnet_printf2(libtelnet_t *telnet, const char *fmt, ...) {
|
||||||
|
|
||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* send formatted data through telnet_send_data */
|
||||||
|
int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
|
||||||
|
char buffer[4096];
|
||||||
|
va_list va;
|
||||||
|
int rs;
|
||||||
|
|
||||||
|
/* format */
|
||||||
|
va_start(va, fmt);
|
||||||
|
rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
/* send */
|
||||||
|
telnet_send_data(telnet, (char *)buffer, rs);
|
||||||
|
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
259
libtelnet.h
259
libtelnet.h
|
@ -13,132 +13,132 @@
|
||||||
#define LIBTELNET_INCLUDE 1
|
#define LIBTELNET_INCLUDE 1
|
||||||
|
|
||||||
/* forward declarations */
|
/* forward declarations */
|
||||||
typedef struct libtelnet_t libtelnet_t;
|
typedef struct telnet_t telnet_t;
|
||||||
typedef struct libtelnet_event_t libtelnet_event_t;
|
typedef struct telnet_event_t telnet_event_t;
|
||||||
typedef struct libtelnet_rfc1143_t libtelnet_rfc1143_t;
|
typedef struct telnet_rfc1143_t telnet_rfc1143_t;
|
||||||
|
|
||||||
/* telnet special values */
|
/* telnet special values */
|
||||||
#define LIBTELNET_IAC 255
|
#define TELNET_IAC 255
|
||||||
#define LIBTELNET_DONT 254
|
#define TELNET_DONT 254
|
||||||
#define LIBTELNET_DO 253
|
#define TELNET_DO 253
|
||||||
#define LIBTELNET_WONT 252
|
#define TELNET_WONT 252
|
||||||
#define LIBTELNET_WILL 251
|
#define TELNET_WILL 251
|
||||||
#define LIBTELNET_SB 250
|
#define TELNET_SB 250
|
||||||
#define LIBTELNET_SB 250
|
#define TELNET_SB 250
|
||||||
#define LIBTELNET_GA 249
|
#define TELNET_GA 249
|
||||||
#define LIBTELNET_EL 248
|
#define TELNET_EL 248
|
||||||
#define LIBTELNET_EC 247
|
#define TELNET_EC 247
|
||||||
#define LIBTELNET_AYT 246
|
#define TELNET_AYT 246
|
||||||
#define LIBTELNET_AO 245
|
#define TELNET_AO 245
|
||||||
#define LIBTELNET_IP 244
|
#define TELNET_IP 244
|
||||||
#define LIBTELNET_BREAK 243
|
#define TELNET_BREAK 243
|
||||||
#define LIBTELNET_DM 242
|
#define TELNET_DM 242
|
||||||
#define LIBTELNET_NOP 241
|
#define TELNET_NOP 241
|
||||||
#define LIBTELNET_SE 240
|
#define TELNET_SE 240
|
||||||
#define LIBTELNET_EOR 239
|
#define TELNET_EOR 239
|
||||||
#define LIBTELNET_ABORT 238
|
#define TELNET_ABORT 238
|
||||||
#define LIBTELNET_SUSP 237
|
#define TELNET_SUSP 237
|
||||||
#define LIBTELNET_EOF 236
|
#define TELNET_EOF 236
|
||||||
|
|
||||||
/* telnet options */
|
/* telnet options */
|
||||||
#define LIBTELNET_TELOPT_BINARY 0
|
#define TELNET_TELOPT_BINARY 0
|
||||||
#define LIBTELNET_TELOPT_ECHO 1
|
#define TELNET_TELOPT_ECHO 1
|
||||||
#define LIBTELNET_TELOPT_RCP 2
|
#define TELNET_TELOPT_RCP 2
|
||||||
#define LIBTELNET_TELOPT_SGA 3
|
#define TELNET_TELOPT_SGA 3
|
||||||
#define LIBTELNET_TELOPT_NAMS 4
|
#define TELNET_TELOPT_NAMS 4
|
||||||
#define LIBTELNET_TELOPT_STATUS 5
|
#define TELNET_TELOPT_STATUS 5
|
||||||
#define LIBTELNET_TELOPT_TM 6
|
#define TELNET_TELOPT_TM 6
|
||||||
#define LIBTELNET_TELOPT_RCTE 7
|
#define TELNET_TELOPT_RCTE 7
|
||||||
#define LIBTELNET_TELOPT_NAOL 8
|
#define TELNET_TELOPT_NAOL 8
|
||||||
#define LIBTELNET_TELOPT_NAOP 9
|
#define TELNET_TELOPT_NAOP 9
|
||||||
#define LIBTELNET_TELOPT_NAOCRD 10
|
#define TELNET_TELOPT_NAOCRD 10
|
||||||
#define LIBTELNET_TELOPT_NAOHTS 11
|
#define TELNET_TELOPT_NAOHTS 11
|
||||||
#define LIBTELNET_TELOPT_NAOHTD 12
|
#define TELNET_TELOPT_NAOHTD 12
|
||||||
#define LIBTELNET_TELOPT_NAOFFD 13
|
#define TELNET_TELOPT_NAOFFD 13
|
||||||
#define LIBTELNET_TELOPT_NAOVTS 14
|
#define TELNET_TELOPT_NAOVTS 14
|
||||||
#define LIBTELNET_TELOPT_NAOVTD 15
|
#define TELNET_TELOPT_NAOVTD 15
|
||||||
#define LIBTELNET_TELOPT_NAOLFD 16
|
#define TELNET_TELOPT_NAOLFD 16
|
||||||
#define LIBTELNET_TELOPT_XASCII 17
|
#define TELNET_TELOPT_XASCII 17
|
||||||
#define LIBTELNET_TELOPT_LOGOUT 18
|
#define TELNET_TELOPT_LOGOUT 18
|
||||||
#define LIBTELNET_TELOPT_BM 19
|
#define TELNET_TELOPT_BM 19
|
||||||
#define LIBTELNET_TELOPT_DET 20
|
#define TELNET_TELOPT_DET 20
|
||||||
#define LIBTELNET_TELOPT_SUPDUP 21
|
#define TELNET_TELOPT_SUPDUP 21
|
||||||
#define LIBTELNET_TELOPT_SUPDUPOUTPUT 22
|
#define TELNET_TELOPT_SUPDUPOUTPUT 22
|
||||||
#define LIBTELNET_TELOPT_SNDLOC 23
|
#define TELNET_TELOPT_SNDLOC 23
|
||||||
#define LIBTELNET_TELOPT_TTYPE 24
|
#define TELNET_TELOPT_TTYPE 24
|
||||||
#define LIBTELNET_TELOPT_EOR 25
|
#define TELNET_TELOPT_EOR 25
|
||||||
#define LIBTELNET_TELOPT_TUID 26
|
#define TELNET_TELOPT_TUID 26
|
||||||
#define LIBTELNET_TELOPT_OUTMRK 27
|
#define TELNET_TELOPT_OUTMRK 27
|
||||||
#define LIBTELNET_TELOPT_TTYLOC 28
|
#define TELNET_TELOPT_TTYLOC 28
|
||||||
#define LIBTELNET_TELOPT_3270REGIME 29
|
#define TELNET_TELOPT_3270REGIME 29
|
||||||
#define LIBTELNET_TELOPT_X3PAD 30
|
#define TELNET_TELOPT_X3PAD 30
|
||||||
#define LIBTELNET_TELOPT_NAWS 31
|
#define TELNET_TELOPT_NAWS 31
|
||||||
#define LIBTELNET_TELOPT_TSPEED 32
|
#define TELNET_TELOPT_TSPEED 32
|
||||||
#define LIBTELNET_TELOPT_LFLOW 33
|
#define TELNET_TELOPT_LFLOW 33
|
||||||
#define LIBTELNET_TELOPT_LINEMODE 34
|
#define TELNET_TELOPT_LINEMODE 34
|
||||||
#define LIBTELNET_TELOPT_XDISPLOC 35
|
#define TELNET_TELOPT_XDISPLOC 35
|
||||||
#define LIBTELNET_TELOPT_ENVIRON 36
|
#define TELNET_TELOPT_ENVIRON 36
|
||||||
#define LIBTELNET_TELOPT_AUTHENTICATION 37
|
#define TELNET_TELOPT_AUTHENTICATION 37
|
||||||
#define LIBTELNET_TELOPT_ENCRYPT 38
|
#define TELNET_TELOPT_ENCRYPT 38
|
||||||
#define LIBTELNET_TELOPT_NEW_ENVIRON 39
|
#define TELNET_TELOPT_NEW_ENVIRON 39
|
||||||
#define LIBTELNET_TELOPT_COMPRESS 85
|
#define TELNET_TELOPT_COMPRESS 85
|
||||||
#define LIBTELNET_TELOPT_COMPRESS2 86
|
#define TELNET_TELOPT_COMPRESS2 86
|
||||||
#define LIBTELNET_TELOPT_ZMP 93
|
#define TELNET_TELOPT_ZMP 93
|
||||||
#define LIBTELNET_TELOPT_EXOPL 255
|
#define TELNET_TELOPT_EXOPL 255
|
||||||
|
|
||||||
/* libtelnet feature flags */
|
/* libtelnet feature flags */
|
||||||
#define LIBTELNET_FLAG_PROXY (1<<0)
|
#define TELNET_FLAG_PROXY (1<<0)
|
||||||
|
|
||||||
#define LIBTELNET_PFLAG_DEFLATE (1<<7)
|
#define TELNET_PFLAG_DEFLATE (1<<7)
|
||||||
|
|
||||||
/* telnet states */
|
/* telnet states */
|
||||||
enum libtelnet_state_t {
|
enum telnet_state_t {
|
||||||
LIBTELNET_STATE_DATA = 0,
|
TELNET_STATE_DATA = 0,
|
||||||
LIBTELNET_STATE_IAC,
|
TELNET_STATE_IAC,
|
||||||
LIBTELNET_STATE_DO,
|
TELNET_STATE_DO,
|
||||||
LIBTELNET_STATE_DONT,
|
TELNET_STATE_DONT,
|
||||||
LIBTELNET_STATE_WILL,
|
TELNET_STATE_WILL,
|
||||||
LIBTELNET_STATE_WONT,
|
TELNET_STATE_WONT,
|
||||||
LIBTELNET_STATE_SB,
|
TELNET_STATE_SB,
|
||||||
LIBTELNET_STATE_SB_DATA,
|
TELNET_STATE_SB_DATA,
|
||||||
LIBTELNET_STATE_SB_DATA_IAC
|
TELNET_STATE_SB_DATA_IAC
|
||||||
};
|
};
|
||||||
typedef enum libtelnet_state_t libtelnet_state_t;
|
typedef enum telnet_state_t telnet_state_t;
|
||||||
|
|
||||||
/* error codes */
|
/* error codes */
|
||||||
enum libtelnet_error_t {
|
enum telnet_error_t {
|
||||||
LIBTELNET_EOK = 0,
|
TELNET_EOK = 0,
|
||||||
LIBTELNET_EBADVAL, /* invalid parameter, or API misuse */
|
TELNET_EBADVAL, /* invalid parameter, or API misuse */
|
||||||
LIBTELNET_ENOMEM, /* memory allocation failure */
|
TELNET_ENOMEM, /* memory allocation failure */
|
||||||
LIBTELNET_EOVERFLOW, /* data exceeds buffer size */
|
TELNET_EOVERFLOW, /* data exceeds buffer size */
|
||||||
LIBTELNET_EPROTOCOL, /* invalid sequence of special bytes */
|
TELNET_EPROTOCOL, /* invalid sequence of special bytes */
|
||||||
LIBTELNET_ECOMPRESS /* error handling compressed streams */
|
TELNET_ECOMPRESS /* error handling compressed streams */
|
||||||
};
|
};
|
||||||
typedef enum libtelnet_error_t libtelnet_error_t;
|
typedef enum telnet_error_t telnet_error_t;
|
||||||
|
|
||||||
/* event codes */
|
/* event codes */
|
||||||
enum libtelnet_event_type_t {
|
enum telnet_event_type_t {
|
||||||
LIBTELNET_EV_DATA = 0,
|
TELNET_EV_DATA = 0,
|
||||||
LIBTELNET_EV_SEND,
|
TELNET_EV_SEND,
|
||||||
LIBTELNET_EV_IAC,
|
TELNET_EV_IAC,
|
||||||
LIBTELNET_EV_WILL,
|
TELNET_EV_WILL,
|
||||||
LIBTELNET_EV_WONT,
|
TELNET_EV_WONT,
|
||||||
LIBTELNET_EV_DO,
|
TELNET_EV_DO,
|
||||||
LIBTELNET_EV_DONT,
|
TELNET_EV_DONT,
|
||||||
LIBTELNET_EV_SUBNEGOTIATION,
|
TELNET_EV_SUBNEGOTIATION,
|
||||||
LIBTELNET_EV_COMPRESS,
|
TELNET_EV_COMPRESS,
|
||||||
LIBTELNET_EV_WARNING,
|
TELNET_EV_WARNING,
|
||||||
LIBTELNET_EV_ERROR
|
TELNET_EV_ERROR
|
||||||
};
|
};
|
||||||
typedef enum libtelnet_event_type_t libtelnet_event_type_t;
|
typedef enum telnet_event_type_t telnet_event_type_t;
|
||||||
|
|
||||||
/* event information */
|
/* event information */
|
||||||
struct libtelnet_event_t {
|
struct telnet_event_t {
|
||||||
/* data buffer: for DATA, SEND, SUBNEGOTIATION, and ERROR events */
|
/* data buffer: for DATA, SEND, SUBNEGOTIATION, and ERROR events */
|
||||||
const char *buffer;
|
const char *buffer;
|
||||||
size_t size;
|
size_t size;
|
||||||
/* type of event */
|
/* type of event */
|
||||||
enum libtelnet_event_type_t type;
|
enum telnet_event_type_t type;
|
||||||
/* IAC command */
|
/* IAC command */
|
||||||
unsigned char command;
|
unsigned char command;
|
||||||
/* telopt info: for negotiation events SUBNEGOTIATION */
|
/* telopt info: for negotiation events SUBNEGOTIATION */
|
||||||
|
@ -148,27 +148,27 @@ struct libtelnet_event_t {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* option negotiation state (RFC 1143) */
|
/* option negotiation state (RFC 1143) */
|
||||||
struct libtelnet_rfc1143_t {
|
struct telnet_rfc1143_t {
|
||||||
unsigned char telopt;
|
unsigned char telopt;
|
||||||
char us:4, him:4;
|
char us:4, him:4;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* event handler declaration */
|
/* event handler declaration */
|
||||||
typedef void (*libtelnet_event_handler_t)(libtelnet_t *telnet,
|
typedef void (*telnet_event_handler_t)(telnet_t *telnet,
|
||||||
libtelnet_event_t *event, void *user_data);
|
telnet_event_t *event, void *user_data);
|
||||||
|
|
||||||
/* state tracker */
|
/* state tracker */
|
||||||
struct libtelnet_t {
|
struct telnet_t {
|
||||||
/* user data */
|
/* user data */
|
||||||
void *ud;
|
void *ud;
|
||||||
/* event handler */
|
/* event handler */
|
||||||
libtelnet_event_handler_t eh;
|
telnet_event_handler_t eh;
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
/* zlib (mccp2) compression */
|
/* zlib (mccp2) compression */
|
||||||
z_stream *z;
|
z_stream *z;
|
||||||
#endif
|
#endif
|
||||||
/* RFC1143 option negotiation states */
|
/* RFC1143 option negotiation states */
|
||||||
struct libtelnet_rfc1143_t *q;
|
struct telnet_rfc1143_t *q;
|
||||||
/* sub-request buffer */
|
/* sub-request buffer */
|
||||||
char *buffer;
|
char *buffer;
|
||||||
/* current size of the buffer */
|
/* current size of the buffer */
|
||||||
|
@ -176,7 +176,7 @@ struct libtelnet_t {
|
||||||
/* current buffer write position (also length of buffer data) */
|
/* current buffer write position (also length of buffer data) */
|
||||||
size_t buffer_pos;
|
size_t buffer_pos;
|
||||||
/* current state */
|
/* current state */
|
||||||
enum libtelnet_state_t state;
|
enum telnet_state_t state;
|
||||||
/* option flags */
|
/* option flags */
|
||||||
unsigned char flags;
|
unsigned char flags;
|
||||||
/* current subnegotiation telopt */
|
/* current subnegotiation telopt */
|
||||||
|
@ -186,57 +186,58 @@ struct libtelnet_t {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* initialize a telnet state tracker */
|
/* initialize a telnet state tracker */
|
||||||
extern void libtelnet_init(libtelnet_t *telnet, libtelnet_event_handler_t eh,
|
extern void telnet_init(telnet_t *telnet, telnet_event_handler_t eh,
|
||||||
unsigned char flags, void *user_data);
|
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 libtelnet_free(libtelnet_t *telnet);
|
extern void telnet_free(telnet_t *telnet);
|
||||||
|
|
||||||
/* push a byte buffer into the state tracker */
|
/* push a byte buffer into the state tracker */
|
||||||
extern void libtelnet_push(libtelnet_t *telnet, const char *buffer,
|
extern void telnet_push(telnet_t *telnet, const char *buffer,
|
||||||
size_t size);
|
size_t size);
|
||||||
|
|
||||||
/* send an iac command */
|
/* send an iac command */
|
||||||
extern void libtelnet_send_command(libtelnet_t *telnet, unsigned char cmd);
|
extern void telnet_send_command(telnet_t *telnet, unsigned char cmd);
|
||||||
|
|
||||||
/* send an iac command with a telopt */
|
/* send an iac command with a telopt */
|
||||||
extern void libtelnet_send_telopt(libtelnet_t *telnet, unsigned char cmd,
|
extern void telnet_send_telopt(telnet_t *telnet, unsigned char cmd,
|
||||||
unsigned char telopt);
|
unsigned char telopt);
|
||||||
|
|
||||||
/* send negotiation, with RFC1143 checking.
|
/* send negotiation, with RFC1143 checking.
|
||||||
* will not actually send unless necessary, but will update internal
|
* will not actually send unless necessary, but will update internal
|
||||||
* negotiation queue.
|
* negotiation queue.
|
||||||
*/
|
*/
|
||||||
extern void libtelnet_send_negotiate(libtelnet_t *telnet, unsigned char cmd,
|
extern void telnet_send_negotiate(telnet_t *telnet, unsigned char cmd,
|
||||||
unsigned char opt);
|
unsigned char opt);
|
||||||
|
|
||||||
/* send non-command data (escapes IAC bytes) */
|
/* send non-command data (escapes IAC bytes) */
|
||||||
extern void libtelnet_send_data(libtelnet_t *telnet,
|
extern void telnet_send_data(telnet_t *telnet,
|
||||||
const char *buffer, size_t size);
|
const char *buffer, size_t size);
|
||||||
|
|
||||||
/* send sub-request, equivalent to:
|
/* send sub-request, equivalent to:
|
||||||
* libtelnet_send_telopt(telnet, LIBTELNET_SB, telopt)
|
* telnet_send_telopt(telnet, TELNET_SB, telopt)
|
||||||
* libtelnet_send_data(telnet, buffer, size);
|
* telnet_send_data(telnet, buffer, size);
|
||||||
* libtelnet_send_command(telnet, LIBTELNET_SE);
|
* telnet_send_command(telnet, TELNET_SE);
|
||||||
* manually generating sequence may be easier for complex subnegotiations
|
* manually generating sequence may be easier for complex subnegotiations
|
||||||
* thare are most easily implemented with a series of send_data calls.
|
* thare are most easily implemented with a series of send_data calls.
|
||||||
*/
|
*/
|
||||||
extern void libtelnet_send_subnegotiation(libtelnet_t *telnet,
|
extern void telnet_send_subnegotiation(telnet_t *telnet,
|
||||||
unsigned char telopt, const char *buffer, size_t size);
|
unsigned char telopt, const char *buffer, size_t size);
|
||||||
|
|
||||||
/* begin sending compressed data (server only) */
|
/* begin sending compressed data (server only) */
|
||||||
extern void libtelnet_begin_compress2(libtelnet_t *telnet);
|
extern void telnet_begin_compress2(telnet_t *telnet);
|
||||||
|
|
||||||
/* send formatted data (through libtelnet_send_data) */
|
/* printf type checking feature in GCC and some other compilers */
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
# define LIBTELNET_GNU_PRINTF(f,a) __attribute__((printf(f, a)))
|
# define TELNET_GNU_PRINTF(f,a) __attribute__((printf(f, a)))
|
||||||
#else
|
#else
|
||||||
# define LIBTELNET_GNU_PRINTF(f,a)
|
# define TELNET_GNU_PRINTF(f,a)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int libtelnet_printf(libtelnet_t *telnet, const char *fmt, ...);
|
/* send formatted data with \r and \n translated, and IAC escaped */
|
||||||
|
extern int telnet_printf(telnet_t *telnet, const char *fmt, ...);
|
||||||
|
|
||||||
/* send formatted data with \r and \n translated */
|
/* send formatted data with just IAC escaped */
|
||||||
extern int libtelnet_printf2(libtelnet_t *telnet, const char *fmt, ...);
|
extern int telnet_printf2(telnet_t *telnet, const char *fmt, ...);
|
||||||
|
|
||||||
#endif /* !defined(LIBTELNET_INCLUDE) */
|
#endif /* !defined(LIBTELNET_INCLUDE) */
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include "libtelnet.h"
|
#include "libtelnet.h"
|
||||||
|
|
||||||
static struct termios orig_tios;
|
static struct termios orig_tios;
|
||||||
static libtelnet_t telnet;
|
static telnet_t telnet;
|
||||||
static int do_echo;
|
static int do_echo;
|
||||||
|
|
||||||
static void _cleanup(void) {
|
static void _cleanup(void) {
|
||||||
|
@ -48,11 +48,11 @@ static void _input(char *buffer, int size) {
|
||||||
if (buffer[i] == '\r' || buffer[i] == '\n') {
|
if (buffer[i] == '\r' || buffer[i] == '\n') {
|
||||||
if (do_echo)
|
if (do_echo)
|
||||||
write(STDOUT_FILENO, crlf, 2);
|
write(STDOUT_FILENO, crlf, 2);
|
||||||
libtelnet_send_data(&telnet, crlf, 2);
|
telnet_send_data(&telnet, crlf, 2);
|
||||||
} else {
|
} else {
|
||||||
if (do_echo)
|
if (do_echo)
|
||||||
write(STDOUT_FILENO, buffer + i, 1);
|
write(STDOUT_FILENO, buffer + i, 1);
|
||||||
libtelnet_send_data(&telnet, buffer + i, 1);
|
telnet_send_data(&telnet, buffer + i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,49 +76,49 @@ static void _send(int sock, const char *buffer, size_t size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _event_handler(libtelnet_t *telnet, libtelnet_event_t *ev,
|
static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
|
||||||
void *user_data) {
|
void *user_data) {
|
||||||
int sock = *(int*)user_data;
|
int sock = *(int*)user_data;
|
||||||
|
|
||||||
switch (ev->type) {
|
switch (ev->type) {
|
||||||
/* data received */
|
/* data received */
|
||||||
case LIBTELNET_EV_DATA:
|
case TELNET_EV_DATA:
|
||||||
write(STDOUT_FILENO, ev->buffer, ev->size);
|
write(STDOUT_FILENO, ev->buffer, ev->size);
|
||||||
break;
|
break;
|
||||||
/* data must be sent */
|
/* data must be sent */
|
||||||
case LIBTELNET_EV_SEND:
|
case TELNET_EV_SEND:
|
||||||
_send(sock, ev->buffer, ev->size);
|
_send(sock, ev->buffer, ev->size);
|
||||||
break;
|
break;
|
||||||
/* request to enable remote feature (or receipt) */
|
/* request to enable remote feature (or receipt) */
|
||||||
case LIBTELNET_EV_WILL:
|
case TELNET_EV_WILL:
|
||||||
/* we accept COMPRESS2 (MCCP) */
|
/* we accept COMPRESS2 (MCCP) */
|
||||||
if (ev->telopt == LIBTELNET_TELOPT_COMPRESS2)
|
if (ev->telopt == TELNET_TELOPT_COMPRESS2)
|
||||||
ev->accept = 1;
|
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 == LIBTELNET_TELOPT_ECHO) {
|
else if (ev->telopt == TELNET_TELOPT_ECHO) {
|
||||||
do_echo = 0;
|
do_echo = 0;
|
||||||
ev->accept = 1;
|
ev->accept = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* notification of disabling remote feature (or receipt) */
|
/* notification of disabling remote feature (or receipt) */
|
||||||
case LIBTELNET_EV_WONT:
|
case TELNET_EV_WONT:
|
||||||
if (ev->telopt == LIBTELNET_TELOPT_ECHO)
|
if (ev->telopt == TELNET_TELOPT_ECHO)
|
||||||
do_echo = 1;
|
do_echo = 1;
|
||||||
break;
|
break;
|
||||||
/* request to enable local feature (or receipt) */
|
/* request to enable local feature (or receipt) */
|
||||||
case LIBTELNET_EV_DO:
|
case TELNET_EV_DO:
|
||||||
/* we support the TTYPE option */
|
/* we support the TTYPE option */
|
||||||
if (ev->telopt == LIBTELNET_TELOPT_TTYPE)
|
if (ev->telopt == TELNET_TELOPT_TTYPE)
|
||||||
ev->accept = 1;
|
ev->accept = 1;
|
||||||
break;
|
break;
|
||||||
/* demand to disable local feature (or receipt) */
|
/* demand to disable local feature (or receipt) */
|
||||||
case LIBTELNET_EV_DONT:
|
case TELNET_EV_DONT:
|
||||||
break;
|
break;
|
||||||
/* respond to particular subnegotiations */
|
/* respond to particular subnegotiations */
|
||||||
case LIBTELNET_EV_SUBNEGOTIATION:
|
case TELNET_EV_SUBNEGOTIATION:
|
||||||
/* respond with our terminal type */
|
/* respond with our terminal type */
|
||||||
if (ev->telopt == LIBTELNET_TELOPT_TTYPE) {
|
if (ev->telopt == TELNET_TELOPT_TTYPE) {
|
||||||
/* NOTE: we just assume the server sent a legitimate
|
/* NOTE: we just assume the server sent a legitimate
|
||||||
* sub-negotiation, as there really isn't anything else
|
* sub-negotiation, as there really isn't anything else
|
||||||
* it's allowed to send
|
* it's allowed to send
|
||||||
|
@ -126,12 +126,12 @@ static void _event_handler(libtelnet_t *telnet, libtelnet_event_t *ev,
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
buffer[0] = 0; /* IS code for RFC 1091 */
|
buffer[0] = 0; /* IS code for RFC 1091 */
|
||||||
snprintf(buffer + 1, sizeof(buffer) - 1, "%s", getenv("TERM"));
|
snprintf(buffer + 1, sizeof(buffer) - 1, "%s", getenv("TERM"));
|
||||||
libtelnet_send_subnegotiation(telnet, LIBTELNET_TELOPT_TTYPE,
|
telnet_send_subnegotiation(telnet, TELNET_TELOPT_TTYPE,
|
||||||
(char *)buffer, 1 + strlen(buffer + 1));
|
(char *)buffer, 1 + strlen(buffer + 1));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* error */
|
/* error */
|
||||||
case LIBTELNET_EV_ERROR:
|
case TELNET_EV_ERROR:
|
||||||
fprintf(stderr, "ERROR: %s\n", ev->buffer);
|
fprintf(stderr, "ERROR: %s\n", ev->buffer);
|
||||||
exit(1);
|
exit(1);
|
||||||
default:
|
default:
|
||||||
|
@ -202,7 +202,7 @@ int main(int argc, char **argv) {
|
||||||
do_echo = 1;
|
do_echo = 1;
|
||||||
|
|
||||||
/* initialize telnet box */
|
/* initialize telnet box */
|
||||||
libtelnet_init(&telnet, _event_handler, 0, &sock);
|
telnet_init(&telnet, _event_handler, 0, &sock);
|
||||||
|
|
||||||
/* initialize poll descriptors */
|
/* initialize poll descriptors */
|
||||||
memset(pfd, 0, sizeof(pfd));
|
memset(pfd, 0, sizeof(pfd));
|
||||||
|
@ -229,7 +229,7 @@ int main(int argc, char **argv) {
|
||||||
/* read from client */
|
/* read from client */
|
||||||
if (pfd[1].revents & POLLIN) {
|
if (pfd[1].revents & POLLIN) {
|
||||||
if ((rs = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
|
if ((rs = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
|
||||||
libtelnet_push(&telnet, buffer, rs);
|
telnet_push(&telnet, buffer, rs);
|
||||||
} else if (rs == 0) {
|
} else if (rs == 0) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -241,7 +241,7 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* clean up */
|
/* clean up */
|
||||||
libtelnet_free(&telnet);
|
telnet_free(&telnet);
|
||||||
close(sock);
|
close(sock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
struct conn_t {
|
struct conn_t {
|
||||||
const char *name;
|
const char *name;
|
||||||
int sock;
|
int sock;
|
||||||
libtelnet_t telnet;
|
telnet_t telnet;
|
||||||
struct conn_t *remote;
|
struct conn_t *remote;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -165,21 +165,21 @@ static void _send(int sock, const char *buffer, size_t size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _event_handler(libtelnet_t *telnet, libtelnet_event_t *ev,
|
static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
|
||||||
void *user_data) {
|
void *user_data) {
|
||||||
struct conn_t *conn = (struct conn_t*)user_data;
|
struct conn_t *conn = (struct conn_t*)user_data;
|
||||||
|
|
||||||
switch (ev->type) {
|
switch (ev->type) {
|
||||||
/* data received */
|
/* data received */
|
||||||
case LIBTELNET_EV_DATA:
|
case TELNET_EV_DATA:
|
||||||
printf("%s DATA: ", conn->name);
|
printf("%s DATA: ", conn->name);
|
||||||
print_buffer(ev->buffer, ev->size);
|
print_buffer(ev->buffer, ev->size);
|
||||||
printf(COLOR_NORMAL "\n");
|
printf(COLOR_NORMAL "\n");
|
||||||
|
|
||||||
libtelnet_send_data(&conn->remote->telnet, ev->buffer, ev->size);
|
telnet_send_data(&conn->remote->telnet, ev->buffer, ev->size);
|
||||||
break;
|
break;
|
||||||
/* data must be sent */
|
/* data must be sent */
|
||||||
case LIBTELNET_EV_SEND:
|
case TELNET_EV_SEND:
|
||||||
/* DONT SPAM
|
/* DONT SPAM
|
||||||
printf("%s SEND: ", conn->name);
|
printf("%s SEND: ", conn->name);
|
||||||
print_buffer(ev->buffer, ev->size);
|
print_buffer(ev->buffer, ev->size);
|
||||||
|
@ -189,41 +189,41 @@ static void _event_handler(libtelnet_t *telnet, libtelnet_event_t *ev,
|
||||||
_send(conn->sock, ev->buffer, ev->size);
|
_send(conn->sock, ev->buffer, ev->size);
|
||||||
break;
|
break;
|
||||||
/* IAC command */
|
/* IAC command */
|
||||||
case LIBTELNET_EV_IAC:
|
case TELNET_EV_IAC:
|
||||||
printf("%s IAC %s" COLOR_NORMAL "\n", conn->name,
|
printf("%s IAC %s" COLOR_NORMAL "\n", conn->name,
|
||||||
get_cmd(ev->command));
|
get_cmd(ev->command));
|
||||||
|
|
||||||
libtelnet_send_command(&conn->remote->telnet, ev->command);
|
telnet_send_command(&conn->remote->telnet, ev->command);
|
||||||
break;
|
break;
|
||||||
/* negotiation, WILL */
|
/* negotiation, WILL */
|
||||||
case LIBTELNET_EV_WILL:
|
case TELNET_EV_WILL:
|
||||||
printf("%s IAC WILL %d (%s)" COLOR_NORMAL "\n", conn->name,
|
printf("%s IAC WILL %d (%s)" COLOR_NORMAL "\n", conn->name,
|
||||||
(int)ev->telopt, get_opt(ev->telopt));
|
(int)ev->telopt, get_opt(ev->telopt));
|
||||||
libtelnet_send_negotiate(&conn->remote->telnet, LIBTELNET_WILL,
|
telnet_send_negotiate(&conn->remote->telnet, TELNET_WILL,
|
||||||
ev->telopt);
|
ev->telopt);
|
||||||
break;
|
break;
|
||||||
/* negotiation, WONT */
|
/* negotiation, WONT */
|
||||||
case LIBTELNET_EV_WONT:
|
case TELNET_EV_WONT:
|
||||||
printf("%s IAC WONT %d (%s)" COLOR_NORMAL "\n", conn->name,
|
printf("%s IAC WONT %d (%s)" COLOR_NORMAL "\n", conn->name,
|
||||||
(int)ev->telopt, get_opt(ev->telopt));
|
(int)ev->telopt, get_opt(ev->telopt));
|
||||||
libtelnet_send_negotiate(&conn->remote->telnet, LIBTELNET_WONT,
|
telnet_send_negotiate(&conn->remote->telnet, TELNET_WONT,
|
||||||
ev->telopt);
|
ev->telopt);
|
||||||
break;
|
break;
|
||||||
/* negotiation, DO */
|
/* negotiation, DO */
|
||||||
case LIBTELNET_EV_DO:
|
case TELNET_EV_DO:
|
||||||
printf("%s IAC DO %d (%s)" COLOR_NORMAL "\n", conn->name,
|
printf("%s IAC DO %d (%s)" COLOR_NORMAL "\n", conn->name,
|
||||||
(int)ev->telopt, get_opt(ev->telopt));
|
(int)ev->telopt, get_opt(ev->telopt));
|
||||||
libtelnet_send_negotiate(&conn->remote->telnet, LIBTELNET_DO,
|
telnet_send_negotiate(&conn->remote->telnet, TELNET_DO,
|
||||||
ev->telopt);
|
ev->telopt);
|
||||||
break;
|
break;
|
||||||
case LIBTELNET_EV_DONT:
|
case TELNET_EV_DONT:
|
||||||
printf("%s IAC DONT %d (%s)" COLOR_NORMAL "\n", conn->name,
|
printf("%s IAC DONT %d (%s)" COLOR_NORMAL "\n", conn->name,
|
||||||
(int)ev->telopt, get_opt(ev->telopt));
|
(int)ev->telopt, get_opt(ev->telopt));
|
||||||
libtelnet_send_negotiate(&conn->remote->telnet, LIBTELNET_DONT,
|
telnet_send_negotiate(&conn->remote->telnet, TELNET_DONT,
|
||||||
ev->telopt);
|
ev->telopt);
|
||||||
break;
|
break;
|
||||||
/* subnegotiation */
|
/* subnegotiation */
|
||||||
case LIBTELNET_EV_SUBNEGOTIATION:
|
case TELNET_EV_SUBNEGOTIATION:
|
||||||
printf("%s SUB %d (%s)", conn->name, (int)ev->telopt,
|
printf("%s SUB %d (%s)", conn->name, (int)ev->telopt,
|
||||||
get_opt(ev->telopt));
|
get_opt(ev->telopt));
|
||||||
if (ev->size > 0) {
|
if (ev->size > 0) {
|
||||||
|
@ -232,20 +232,20 @@ static void _event_handler(libtelnet_t *telnet, libtelnet_event_t *ev,
|
||||||
}
|
}
|
||||||
printf(COLOR_NORMAL "\n");
|
printf(COLOR_NORMAL "\n");
|
||||||
|
|
||||||
libtelnet_send_subnegotiation(&conn->remote->telnet, ev->telopt,
|
telnet_send_subnegotiation(&conn->remote->telnet, ev->telopt,
|
||||||
ev->buffer, ev->size);
|
ev->buffer, ev->size);
|
||||||
break;
|
break;
|
||||||
/* compression notification */
|
/* compression notification */
|
||||||
case LIBTELNET_EV_COMPRESS:
|
case TELNET_EV_COMPRESS:
|
||||||
printf("%s COMPRESSION %s" COLOR_NORMAL "\n", conn->name,
|
printf("%s COMPRESSION %s" COLOR_NORMAL "\n", conn->name,
|
||||||
ev->command ? "ON" : "OFF");
|
ev->command ? "ON" : "OFF");
|
||||||
break;
|
break;
|
||||||
/* warning */
|
/* warning */
|
||||||
case LIBTELNET_EV_WARNING:
|
case TELNET_EV_WARNING:
|
||||||
printf("%s WARNING: %s" COLOR_NORMAL "\n", conn->name, ev->buffer);
|
printf("%s WARNING: %s" COLOR_NORMAL "\n", conn->name, ev->buffer);
|
||||||
break;
|
break;
|
||||||
/* error */
|
/* error */
|
||||||
case LIBTELNET_EV_ERROR:
|
case TELNET_EV_ERROR:
|
||||||
printf("%s ERROR: %s" COLOR_NORMAL "\n", conn->name, ev->buffer);
|
printf("%s ERROR: %s" COLOR_NORMAL "\n", conn->name, ev->buffer);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -357,9 +357,9 @@ int main(int argc, char **argv) {
|
||||||
client.remote = &server;
|
client.remote = &server;
|
||||||
|
|
||||||
/* initialize telnet boxes */
|
/* initialize telnet boxes */
|
||||||
libtelnet_init(&server.telnet, _event_handler, LIBTELNET_FLAG_PROXY,
|
telnet_init(&server.telnet, _event_handler, TELNET_FLAG_PROXY,
|
||||||
&server);
|
&server);
|
||||||
libtelnet_init(&client.telnet, _event_handler, LIBTELNET_FLAG_PROXY,
|
telnet_init(&client.telnet, _event_handler, TELNET_FLAG_PROXY,
|
||||||
&client);
|
&client);
|
||||||
|
|
||||||
/* initialize poll descriptors */
|
/* initialize poll descriptors */
|
||||||
|
@ -374,7 +374,7 @@ int main(int argc, char **argv) {
|
||||||
/* read from server */
|
/* read from server */
|
||||||
if (pfd[0].revents & POLLIN) {
|
if (pfd[0].revents & POLLIN) {
|
||||||
if ((rs = recv(server.sock, buffer, sizeof(buffer), 0)) > 0) {
|
if ((rs = recv(server.sock, buffer, sizeof(buffer), 0)) > 0) {
|
||||||
libtelnet_push(&server.telnet, buffer, rs);
|
telnet_push(&server.telnet, buffer, rs);
|
||||||
} else if (rs == 0) {
|
} else if (rs == 0) {
|
||||||
printf("%s DISCONNECTED" COLOR_NORMAL "\n", server.name);
|
printf("%s DISCONNECTED" COLOR_NORMAL "\n", server.name);
|
||||||
break;
|
break;
|
||||||
|
@ -388,7 +388,7 @@ int main(int argc, char **argv) {
|
||||||
/* read from client */
|
/* read from client */
|
||||||
if (pfd[1].revents & POLLIN) {
|
if (pfd[1].revents & POLLIN) {
|
||||||
if ((rs = recv(client.sock, buffer, sizeof(buffer), 0)) > 0) {
|
if ((rs = recv(client.sock, buffer, sizeof(buffer), 0)) > 0) {
|
||||||
libtelnet_push(&client.telnet, buffer, rs);
|
telnet_push(&client.telnet, buffer, rs);
|
||||||
} else if (rs == 0) {
|
} else if (rs == 0) {
|
||||||
printf("%s DISCONNECTED" COLOR_NORMAL "\n", client.name);
|
printf("%s DISCONNECTED" COLOR_NORMAL "\n", client.name);
|
||||||
break;
|
break;
|
||||||
|
@ -401,8 +401,8 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* clean up */
|
/* clean up */
|
||||||
libtelnet_free(&server.telnet);
|
telnet_free(&server.telnet);
|
||||||
libtelnet_free(&client.telnet);
|
telnet_free(&client.telnet);
|
||||||
close(server.sock);
|
close(server.sock);
|
||||||
close(client.sock);
|
close(client.sock);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue