Skip to content
This repository has been archived by the owner on Nov 27, 2024. It is now read-only.

Commit

Permalink
Merge pull request #34 from slcasner/eol-in-process
Browse files Browse the repository at this point in the history
Alternative pull request with EOL translation in process()
  • Loading branch information
Sean Middleditch authored Sep 14, 2017
2 parents 2b645e9 + 5382149 commit 278f59b
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 12 deletions.
29 changes: 20 additions & 9 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ IIa. Initialization
Operate in proxy mode. This disables the RFC1143 support and
enables automatic detection of COMPRESS2 streams.

TELNET_FLAG_NVT_EOL
Receive data with translation of the TELNET NVT CR NUL and CR LF
sequences specified in RFC854 to C carriage return (\r) and C
newline (\n), respectively.

If telnet_init() fails to allocate the required memory, the
returned pointer will be zero.

Expand Down Expand Up @@ -174,7 +179,13 @@ IIc. Sending Data
Sends raw data, which would be either the process output from a
server or the user input from a client.

For sending regular text is may be more convenient to use
void telnet_send_text(telnet_t *telnet, const char *buffer,
size_t size);
Sends text characters with translation of C newlines (\n) into
CR LF and C carriage returns (\r) into CR NUL, as required by
RFC854, unless transmission in BINARY mode has been negotiated.

For sending regular text it may be more convenient to use
telnet_printf().

void telnet_begin_sb(telnet_t *telnet, unsigned char telopt);
Expand Down Expand Up @@ -213,14 +224,14 @@ IIc. Sending Data
detect the COMPRESS2 marker and enable zlib compression.

int telnet_printf(telnet_t *telnet, const char *fmt, ...);
This functions very similarly to fprintf, except that output is
sent through libtelnet for processing. IAC bytes are properly
escaped, C newlines (\n) are translated into CR LF, and C carriage
returns (\r) are translated into CR NUL, all as required by
RFC854. The return code is the length of the formatted text.

NOTE: due to an internal implementation detail, the maximum
lenth of the formatted text is 4096 characters.
This functions very similarly to fprintf, except that output is
sent through libtelnet for processing. IAC bytes are properly
escaped, C newlines (\n) are translated into CR LF, and C carriage
returns (\r) are translated into CR NUL, all as required by
RFC854. The return code is the length of the formatted text.

NOTE: due to an internal implementation detail, the maximum
length of the formatted text is 4096 characters.

IId. Event Handling

Expand Down
87 changes: 85 additions & 2 deletions libtelnet.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
/* telnet state codes */
enum telnet_state_t {
TELNET_STATE_DATA = 0,
TELNET_STATE_EOL,
TELNET_STATE_IAC,
TELNET_STATE_WILL,
TELNET_STATE_WONT,
Expand Down Expand Up @@ -119,6 +120,10 @@ typedef struct telnet_rfc1143_t {
#define Q_WANTNO_OP 4
#define Q_WANTYES_OP 5

/* telnet NVT EOL sequences */
static const char CRLF[] = { '\r', '\n' };
static const char CRNUL[] = { '\r', '\0' };

/* buffer sizes */
static const size_t _buffer_sizes[] = { 0, 512, 2048, 8192, 16384, };
static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
Expand Down Expand Up @@ -302,6 +307,14 @@ static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
for (i = 0; i != telnet->q_size; ++i) {
if (telnet->q[i].telopt == telopt) {
telnet->q[i].state = Q_MAKE(us,him);
if (telopt != TELNET_TELOPT_BINARY)
return;
telnet->flags &= ~(TELNET_FLAG_TRANSMIT_BINARY |
TELNET_FLAG_RECEIVE_BINARY);
if (us == Q_YES)
telnet->flags |= TELNET_FLAG_TRANSMIT_BINARY;
if (him == Q_YES)
telnet->flags |= TELNET_FLAG_RECEIVE_BINARY;
return;
}
}
Expand Down Expand Up @@ -962,7 +975,36 @@ static void _process(telnet_t *telnet, const char *buffer, size_t size) {
telnet->eh(telnet, &ev, telnet->ud);
}
telnet->state = TELNET_STATE_IAC;
} else if (byte == '\r' &&
(telnet->flags & TELNET_FLAG_NVT_EOL) &&
!(telnet->flags & TELNET_FLAG_RECEIVE_BINARY)) {
if (i != start) {
ev.type = TELNET_EV_DATA;
ev.data.buffer = buffer + start;
ev.data.size = i - start;
telnet->eh(telnet, &ev, telnet->ud);
}
telnet->state = TELNET_STATE_EOL;
}
break;

/* NVT EOL to be translated */
case TELNET_STATE_EOL:
if (byte != '\n') {
byte = '\r';
ev.type = TELNET_EV_DATA;
ev.data.buffer = (char*)&byte;
ev.data.size = 1;
telnet->eh(telnet, &ev, telnet->ud);
byte = buffer[i];
}
// any byte following '\r' other than '\n' or '\0' is invalid,
// so pass both \r and the byte
start = i;
if (byte == '\0')
++start;
/* state update */
telnet->state = TELNET_STATE_DATA;
break;

/* IAC command */
Expand Down Expand Up @@ -1297,6 +1339,49 @@ void telnet_send(telnet_t *telnet, const char *buffer,
}
}

/* send non-command text (escapes IAC bytes and does NVT translation) */
void telnet_send_text(telnet_t *telnet, const char *buffer,
size_t size) {
size_t i, l;

for (l = i = 0; i != size; ++i) {
/* dump prior portion of text, send escaped bytes */
if (buffer[i] == (char)TELNET_IAC) {
/* dump prior text if any */
if (i != l) {
_send(telnet, buffer + l, i - l);
}
l = i + 1;

/* send escape */
telnet_iac(telnet, TELNET_IAC);
}
/* special characters if not in BINARY mode */
else if (!(telnet->flags & TELNET_FLAG_TRANSMIT_BINARY) &&
(buffer[i] == '\r' || buffer[i] == '\n')) {
/* dump prior portion of text */
if (i != l) {
_send(telnet, buffer + l, i - l);
}
l = i + 1;

/* automatic translation of \r -> CRNUL */
if (buffer[i] == '\r') {
_send(telnet, CRNUL, 2);
}
/* automatic translation of \n -> CRLF */
else {
_send(telnet, CRLF, 2);
}
}
}

/* send whatever portion of buffer is left */
if (i != l) {
_send(telnet, buffer + l, i - l);
}
}

/* send subnegotiation header */
void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
unsigned char sb[3];
Expand Down Expand Up @@ -1369,8 +1454,6 @@ void telnet_begin_compress2(telnet_t *telnet) {

/* send formatted data with \r and \n translation in addition to IAC IAC */
int telnet_vprintf(telnet_t *telnet, const char *fmt, va_list va) {
static const char CRLF[] = { '\r', '\n' };
static const char CRNUL[] = { '\r', '\0' };
char buffer[1024];
char *output = buffer;
int rs, i, l;
Expand Down
17 changes: 16 additions & 1 deletion libtelnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,11 @@ typedef struct telnet_telopt_t telnet_telopt_t;
/*@{*/
/*! Control behavior of telnet state tracker. */
#define TELNET_FLAG_PROXY (1<<0)
#define TELNET_FLAG_NVT_EOL (1<<1)

/* Internal-only bits in option flags */
#define TELNET_FLAG_TRANSMIT_BINARY (1<<5)
#define TELNET_FLAG_RECEIVE_BINARY (1<<6)
#define TELNET_PFLAG_DEFLATE (1<<7)
/*@}*/

Expand Down Expand Up @@ -375,7 +379,7 @@ struct telnet_t;
* \param eh Event handler function called for every event.
* \param flags 0 or TELNET_FLAG_PROXY.
* \param user_data Optional data pointer that will be passsed to eh.
* \return Telent state tracker object.
* \return Telnet state tracker object.
*/
extern telnet_t* telnet_init(const telnet_telopt_t *telopts,
telnet_event_handler_t eh, unsigned char flags, void *user_data);
Expand Down Expand Up @@ -438,6 +442,17 @@ extern void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
extern void telnet_send(telnet_t *telnet,
const char *buffer, size_t size);

/*!
* Send non-command text (escapes IAC bytes and translates
* \\r -> CR-NUL and \\n -> CR-LF unless in BINARY mode.
*
* \param telnet Telnet state tracker object.
* \param buffer Buffer of bytes to send.
* \param size Number of bytes to send.
*/
extern void telnet_send_text(telnet_t *telnet,
const char *buffer, size_t size);

/*!
* \brief Begin a sub-negotiation command.
*
Expand Down

0 comments on commit 278f59b

Please sign in to comment.