separate non-fatal errors into warnings, error events are always fatal
diff --git a/README b/README
index c7860b2..de08386 100644
--- a/README
+++ b/README
@@ -267,25 +267,30 @@
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
=====================================================================
diff --git a/libtelnet.c b/libtelnet.c
index ea0e661..e4f14c1 100644
--- a/libtelnet.c
+++ b/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 @@
}
/* 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 @@
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 @@
/* 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 @@
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 @@
(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 @@
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 @@
_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 @@
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 @@
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 @@
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 */
diff --git a/libtelnet.h b/libtelnet.h
index 11e01b9..972f601 100644
--- a/libtelnet.h
+++ b/libtelnet.h
@@ -108,10 +108,11 @@
/* 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 @@
LIBTELNET_EV_NEGOTIATE,
LIBTELNET_EV_SUBNEGOTIATION,
LIBTELNET_EV_COMPRESS,
+ LIBTELNET_EV_WARNING,
LIBTELNET_EV_ERROR
};
diff --git a/telnet-proxy.c b/telnet-proxy.c
index dbbbf05..d678d6c 100644
--- a/telnet-proxy.c
+++ b/telnet-proxy.c
@@ -217,6 +217,11 @@
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,