mirror of https://gerrit.osmocom.org/libtelnet
separate non-fatal errors into warnings, error events are always fatal
This commit is contained in:
parent
ae229d3dbd
commit
1699227c9c
33
README
33
README
|
@ -267,25 +267,30 @@ IId. Event Handling
|
|||
|
||||
The event->command value will be 1 if compression has started and
|
||||
will be 0 if compression has ended.
|
||||
|
||||
LIBTELNET_EV_ERROR
|
||||
This event is called whenever an error occurs while trying to
|
||||
process the TELNET protocol. This includes both invalid protocol
|
||||
sequences (which are rare) and out of memory conditions.
|
||||
|
||||
With few exceptions, an error is non-recoverable, and the only
|
||||
solid course of action is to close the connection. This is
|
||||
especially true for any errors involving the COMPRESS2 option.
|
||||
|
||||
LIBTELNET_EV_WARNING
|
||||
The WARNING event is sent whenever something has gone wrong
|
||||
inside of libtelnet (possibly due to malformed data sent by the
|
||||
other end) but which recovery is (likely) possible. It may be
|
||||
safe to continue using the connection, but some data may have
|
||||
been lost or incorrectly interpreted.
|
||||
|
||||
The event->buffer value will contain a NUL terminated string
|
||||
explaining the error, and the event->size value containers the
|
||||
length of the string.
|
||||
|
||||
FIXME: we should pass the error code in one of the fields, and
|
||||
better document which errors are definitely non-recoverable and
|
||||
which are maybe-recoverable (mostly those are just IAC-in-SB
|
||||
errors... every other error is related to MCCP2 and usually
|
||||
results in being unable to further read the stream).
|
||||
LIBTELNET_EV_ERROR
|
||||
Similar to the WARNING event, the ERROR event is sent whenever
|
||||
something has gone wrong. ERROR events are non-recoverable,
|
||||
however, and the application should immediately close the
|
||||
connection. Whatever has happened is likely going only to
|
||||
result in garbage from libtelnet. This is most likely to
|
||||
happen when a COMPRESS2 stream fails, but other problems can
|
||||
occur.
|
||||
|
||||
The event->buffer value will contain a NUL terminated string
|
||||
explaining the error, and the event->size value containers the
|
||||
length of the string.
|
||||
|
||||
III. INTEGRATING LIBTELNET WITH COMMON MUDS
|
||||
=====================================================================
|
||||
|
|
64
libtelnet.c
64
libtelnet.c
|
@ -22,19 +22,6 @@
|
|||
|
||||
#include "libtelnet.h"
|
||||
|
||||
/* error handler helpers */
|
||||
#ifdef ERROR
|
||||
# undef ERROR
|
||||
#endif
|
||||
#define ERROR(telnet, code, msg) \
|
||||
_error(telnet, __FILE__, __LINE__, code, "%s", msg)
|
||||
#define ERROR_NOMEM(telnet, msg) \
|
||||
_error(telnet, __FILE__, __LINE__, LIBTELNET_ENOMEM, \
|
||||
"%s: %s", msg, strerror(errno))
|
||||
#define ERROR_ZLIB(telnet, rs, msg) \
|
||||
_error(telnet, __FILE__, __LINE__, LIBTELNET_EUNKNOWN, \
|
||||
"%s: %s", msg, zError(rs))
|
||||
|
||||
/* buffer sizes */
|
||||
static const unsigned int _buffer_sizes[] = {
|
||||
0,
|
||||
|
@ -61,35 +48,37 @@ static void _event(struct libtelnet_t *telnet,
|
|||
}
|
||||
|
||||
/* error generation function */
|
||||
static void _error(struct libtelnet_t *telnet, const char *file, unsigned line,
|
||||
enum libtelnet_error_t err, const char *fmt, ...) {
|
||||
static void _error(struct libtelnet_t *telnet, unsigned line, const char* func,
|
||||
enum libtelnet_error_t err, int fatal, const char *fmt, ...) {
|
||||
char buffer[512];
|
||||
va_list va;
|
||||
|
||||
/* format error intro */
|
||||
snprintf(buffer, sizeof(buffer), "%s:%u: ",
|
||||
file, line);
|
||||
snprintf(buffer, sizeof(buffer), "%s:%u in %s: ",
|
||||
__FILE__, line, func);
|
||||
|
||||
va_start(va, fmt);
|
||||
vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
|
||||
fmt, va);
|
||||
va_end(va);
|
||||
|
||||
_event(telnet, LIBTELNET_EV_ERROR, err, 0, 0, 0);
|
||||
_event(telnet, fatal ? LIBTELNET_EV_ERROR : LIBTELNET_EV_WARNING, err,
|
||||
0, 0, 0);
|
||||
}
|
||||
|
||||
/* initialize the zlib box for a telnet box; if deflate is non-zero, it
|
||||
* initializes zlib for delating (compression), otherwise for inflating
|
||||
* (decompression)
|
||||
*/
|
||||
z_stream *_init_zlib(struct libtelnet_t *telnet, int deflate) {
|
||||
z_stream *_init_zlib(struct libtelnet_t *telnet, int deflate, int err_fatal) {
|
||||
z_stream *zlib;
|
||||
int rs;
|
||||
|
||||
/* allocate zstream box */
|
||||
if ((zlib = (z_stream *)calloc(1, sizeof(z_stream)))
|
||||
== 0) {
|
||||
ERROR_NOMEM(telnet, "malloc() failed");
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, err_fatal,
|
||||
"malloc() failed: %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -97,13 +86,15 @@ z_stream *_init_zlib(struct libtelnet_t *telnet, int deflate) {
|
|||
if (deflate) {
|
||||
if ((rs = deflateInit(zlib, Z_DEFAULT_COMPRESSION)) != Z_OK) {
|
||||
free(zlib);
|
||||
ERROR_ZLIB(telnet, rs, "deflateInit() failed");
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, err_fatal,
|
||||
"deflateInit() failed: %s", zError(rs));
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if ((rs = inflateInit(zlib)) != Z_OK) {
|
||||
free(zlib);
|
||||
ERROR_ZLIB(telnet, rs, "inflateInit() failed");
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, err_fatal,
|
||||
"inflateInit() failed: %s", zError(rs));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +150,7 @@ static enum libtelnet_error_t _buffer_byte(struct libtelnet_t *telnet,
|
|||
|
||||
/* overflow -- can't grow any more */
|
||||
if (i >= _buffer_sizes_count - 1) {
|
||||
_error(telnet, __FILE__, __LINE__, LIBTELNET_EOVERFLOW,
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_EOVERFLOW, 0,
|
||||
"subnegotiation buffer size limit reached");
|
||||
libtelnet_free(telnet);
|
||||
return LIBTELNET_EOVERFLOW;
|
||||
|
@ -169,7 +160,8 @@ static enum libtelnet_error_t _buffer_byte(struct libtelnet_t *telnet,
|
|||
new_buffer = (unsigned char *)realloc(telnet->buffer,
|
||||
_buffer_sizes[i + 1]);
|
||||
if (new_buffer == 0) {
|
||||
ERROR_NOMEM(telnet, "realloc() failed");
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, 0,
|
||||
"realloc() failed");
|
||||
libtelnet_free(telnet);
|
||||
return LIBTELNET_ENOMEM;
|
||||
}
|
||||
|
@ -300,7 +292,7 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
(telnet->mode == LIBTELNET_MODE_CLIENT ||
|
||||
telnet->mode == LIBTELNET_MODE_PROXY)) {
|
||||
|
||||
if ((telnet->z_inflate = _init_zlib(telnet, 0))
|
||||
if ((telnet->z_inflate = _init_zlib(telnet, 0, 1))
|
||||
== 0)
|
||||
break;
|
||||
|
||||
|
@ -332,7 +324,7 @@ static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
break;
|
||||
/* something else -- protocol error */
|
||||
default:
|
||||
_error(telnet, __FILE__, __LINE__, LIBTELNET_EPROTOCOL,
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
|
||||
"unexpected byte after IAC inside SB: %d",
|
||||
byte);
|
||||
start = i + 1;
|
||||
|
@ -375,7 +367,8 @@ void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
_process(telnet, inflate_buffer, sizeof(inflate_buffer) -
|
||||
telnet->z_inflate->avail_out);
|
||||
else
|
||||
ERROR_ZLIB(telnet, rs, "inflate() failed");
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, 1,
|
||||
"inflate() failed: %s", zError(rs));
|
||||
|
||||
/* prepare output buffer for next run */
|
||||
telnet->z_inflate->next_out = inflate_buffer;
|
||||
|
@ -416,7 +409,8 @@ static void _send(struct libtelnet_t *telnet, unsigned char *buffer,
|
|||
while (telnet->z_deflate->avail_in > 0 || telnet->z_deflate->avail_out == 0) {
|
||||
/* compress */
|
||||
if ((rs = deflate(telnet->z_deflate, Z_SYNC_FLUSH)) != Z_OK) {
|
||||
ERROR_ZLIB(telnet, rs, "deflate() failed");
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, 1,
|
||||
"deflate() failed: %s", zError(rs));
|
||||
deflateEnd(telnet->z_deflate);
|
||||
free(telnet->z_deflate);
|
||||
telnet->z_deflate = 0;
|
||||
|
@ -488,7 +482,7 @@ void libtelnet_send_subnegotiation(struct libtelnet_t *telnet,
|
|||
telnet->z_deflate == 0 &&
|
||||
opt == LIBTELNET_TELOPT_COMPRESS2) {
|
||||
|
||||
if ((telnet->z_deflate = _init_zlib(telnet, 1)) == 0)
|
||||
if ((telnet->z_deflate = _init_zlib(telnet, 1, 1)) == 0)
|
||||
return;
|
||||
|
||||
/* notify app that compression was enabled */
|
||||
|
@ -502,15 +496,21 @@ void libtelnet_begin_compress2(struct libtelnet_t *telnet) {
|
|||
z_stream *zlib;
|
||||
|
||||
/* don't do this if we've already got a compression stream */
|
||||
if (telnet->z_deflate != 0)
|
||||
if (telnet->z_deflate != 0) {
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_EBADVAL, 0,
|
||||
"compression already enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
/* only supported by servers */
|
||||
if (telnet->mode != LIBTELNET_MODE_SERVER)
|
||||
if (telnet->mode != LIBTELNET_MODE_SERVER) {
|
||||
_error(telnet, __LINE__, __func__, LIBTELNET_EBADVAL, 0,
|
||||
"only supported in SERVER mode");
|
||||
return;
|
||||
}
|
||||
|
||||
/* attempt to create output stream first, bail if we can't */
|
||||
if ((zlib = _init_zlib(telnet, 1)) == 0)
|
||||
if ((zlib = _init_zlib(telnet, 1, 0)) == 0)
|
||||
return;
|
||||
|
||||
/* send compression marker */
|
||||
|
|
|
@ -108,10 +108,11 @@ enum libtelnet_state_t {
|
|||
/* error codes */
|
||||
enum libtelnet_error_t {
|
||||
LIBTELNET_EOK = 0,
|
||||
LIBTELNET_EBADVAL, /* invalid parameter, or API misuse */
|
||||
LIBTELNET_ENOMEM, /* memory allocation failure */
|
||||
LIBTELNET_EOVERFLOW, /* data exceeds buffer size */
|
||||
LIBTELNET_EPROTOCOL, /* invalid sequence of special bytes */
|
||||
LIBTELNET_EUNKNOWN /* some crazy unexplainable unknown error */
|
||||
LIBTELNET_ECOMPRESS /* error handling compressed streams */
|
||||
};
|
||||
|
||||
/* event codes */
|
||||
|
@ -122,6 +123,7 @@ enum libtelnet_event_type_t {
|
|||
LIBTELNET_EV_NEGOTIATE,
|
||||
LIBTELNET_EV_SUBNEGOTIATION,
|
||||
LIBTELNET_EV_COMPRESS,
|
||||
LIBTELNET_EV_WARNING,
|
||||
LIBTELNET_EV_ERROR
|
||||
};
|
||||
|
||||
|
|
|
@ -217,6 +217,11 @@ static void _event_handler(struct libtelnet_t *telnet,
|
|||
printf("%s COMPRESSION %s" COLOR_NORMAL "\n", conn->name,
|
||||
ev->command ? "ON" : "OFF");
|
||||
break;
|
||||
/* warning */
|
||||
case LIBTELNET_EV_WARNING:
|
||||
printf("%s WARNING: %.*s" COLOR_NORMAL "\n", conn->name, ev->size,
|
||||
ev->buffer);
|
||||
break;
|
||||
/* error */
|
||||
case LIBTELNET_EV_ERROR:
|
||||
printf("%s ERROR: %.*s" COLOR_NORMAL "\n", conn->name, ev->size,
|
||||
|
|
Loading…
Reference in New Issue