save a few bytes and use only a single z_stream, because mccp2 is unidirectional
diff --git a/libtelnet.c b/libtelnet.c
index 1f9e606..4f8461f 100644
--- a/libtelnet.c
+++ b/libtelnet.c
@@ -48,8 +48,9 @@
}
/* error generation function */
-static void _error(libtelnet_t *telnet, unsigned line, const char* func,
- libtelnet_error_t err, int fatal, const char *fmt, ...) {
+static libtelnet_error_t _error(libtelnet_t *telnet, unsigned line,
+ const char* func, libtelnet_error_t err, int fatal, const char *fmt,
+ ...) {
char buffer[512];
va_list va;
@@ -63,42 +64,49 @@
_event(telnet, fatal ? LIBTELNET_EV_ERROR : LIBTELNET_EV_WARNING, err,
0, (unsigned char *)buffer, strlen(buffer));
+
+ return err;
}
/* initialize the zlib box for a telnet box; if deflate is non-zero, it
* initializes zlib for delating (compression), otherwise for inflating
- * (decompression)
+ * (decompression). returns LIBTELNET_EOK on success, something else on
+ * failure.
*/
-z_stream *_init_zlib(libtelnet_t *telnet, int deflate, int err_fatal) {
- z_stream *zlib;
+libtelnet_error_t _init_zlib(libtelnet_t *telnet, int deflate, int err_fatal) {
+ z_stream *z;
int rs;
+ /* if compression is already enabled, fail loudly */
+ if (telnet->z != 0)
+ return _error(telnet, __LINE__, __func__, LIBTELNET_EBADVAL,
+ err_fatal, "cannot initialize compression twice");
+
/* allocate zstream box */
- if ((zlib = (z_stream *)calloc(1, sizeof(z_stream)))
- == 0) {
- _error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, err_fatal,
+ if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
+ return _error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, err_fatal,
"malloc() failed: %s", strerror(errno));
- return 0;
- }
/* initialize */
if (deflate) {
- if ((rs = deflateInit(zlib, Z_DEFAULT_COMPRESSION)) != Z_OK) {
- free(zlib);
- _error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, err_fatal,
- "deflateInit() failed: %s", zError(rs));
- return 0;
+ if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
+ free(z);
+ return _error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS,
+ err_fatal, "deflateInit() failed: %s", zError(rs));
}
+ telnet->flags |= LIBTELNET_PFLAG_DEFLATE;
} else {
- if ((rs = inflateInit(zlib)) != Z_OK) {
- free(zlib);
- _error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, err_fatal,
- "inflateInit() failed: %s", zError(rs));
- return 0;
+ if ((rs = inflateInit(z)) != Z_OK) {
+ free(z);
+ return _error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS,
+ err_fatal, "inflateInit() failed: %s", zError(rs));
}
+ telnet->flags &= ~LIBTELNET_PFLAG_DEFLATE;
}
- return zlib;
+ telnet->z = z;
+
+ return LIBTELNET_EOK;
}
/* initialize a telnet state tracker */
@@ -120,16 +128,14 @@
telnet->length = 0;
}
- /* free zlib box(es) */
- if (telnet->z_inflate != 0) {
- inflateEnd(telnet->z_inflate);
- free(telnet->z_inflate);
- telnet->z_inflate = 0;
- }
- if (telnet->z_deflate != 0) {
- deflateEnd(telnet->z_deflate);
- free(telnet->z_deflate);
- telnet->z_deflate = 0;
+ /* free zlib box */
+ if (telnet->z != 0) {
+ if (telnet->flags & LIBTELNET_PFLAG_DEFLATE)
+ deflateEnd(telnet->z);
+ else
+ inflateEnd(telnet->z);
+ free(telnet->z);
+ telnet->z = 0;
}
}
@@ -287,10 +293,10 @@
* handling the compressed stream if it's not already.
*/
if (telnet->sb_telopt == LIBTELNET_TELOPT_COMPRESS2 &&
- telnet->z_inflate == 0 &&
+ telnet->z == 0 &&
telnet->flags & LIBTELNET_FLAG_PROXY) {
- if ((telnet->z_inflate = _init_zlib(telnet, 0, 1)) == 0)
+ if (_init_zlib(telnet, 0, 1) != LIBTELNET_EOK)
break;
/* notify app that compression was enabled */
@@ -342,42 +348,42 @@
unsigned int size) {
#ifdef HAVE_ZLIB
/* if we have an inflate (decompression) zlib stream, use it */
- if (telnet->z_inflate != 0) {
+ if (telnet->z != 0) {
unsigned char inflate_buffer[4096];
int rs;
/* initialize zlib state */
- telnet->z_inflate->next_in = buffer;
- telnet->z_inflate->avail_in = size;
- telnet->z_inflate->next_out = inflate_buffer;
- telnet->z_inflate->avail_out = sizeof(inflate_buffer);
+ telnet->z->next_in = buffer;
+ telnet->z->avail_in = size;
+ telnet->z->next_out = inflate_buffer;
+ telnet->z->avail_out = sizeof(inflate_buffer);
/* inflate until buffer exhausted and all output is produced */
- while (telnet->z_inflate->avail_in > 0 || telnet->z_inflate->avail_out == 0) {
+ while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
/* reset output buffer */
/* decompress */
- rs = inflate(telnet->z_inflate, Z_SYNC_FLUSH);
+ rs = inflate(telnet->z, Z_SYNC_FLUSH);
/* process the decompressed bytes on success */
if (rs == Z_OK || rs == Z_STREAM_END)
_process(telnet, inflate_buffer, sizeof(inflate_buffer) -
- telnet->z_inflate->avail_out);
+ telnet->z->avail_out);
else
_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;
- telnet->z_inflate->avail_out = sizeof(inflate_buffer);
+ telnet->z->next_out = inflate_buffer;
+ telnet->z->avail_out = sizeof(inflate_buffer);
/* on error (or on end of stream) disable further inflation */
if (rs != Z_OK) {
_event(telnet, LIBTELNET_EV_COMPRESS, 0, 0, 0, 0);
- inflateEnd(telnet->z_inflate);
- free(telnet->z_inflate);
- telnet->z_inflate = 0;
+ inflateEnd(telnet->z);
+ free(telnet->z);
+ telnet->z = 0;
break;
}
}
@@ -392,34 +398,34 @@
unsigned int size) {
#ifdef HAVE_ZLIB
/* if we have a deflate (compression) zlib box, use it */
- if (telnet->z_deflate != 0) {
+ if (telnet->z != 0) {
unsigned char deflate_buffer[1024];
int rs;
- /* initialize z_deflate state */
- telnet->z_deflate->next_in = buffer;
- telnet->z_deflate->avail_in = size;
- telnet->z_deflate->next_out = deflate_buffer;
- telnet->z_deflate->avail_out = sizeof(deflate_buffer);
+ /* initialize z state */
+ telnet->z->next_in = buffer;
+ telnet->z->avail_in = size;
+ telnet->z->next_out = deflate_buffer;
+ telnet->z->avail_out = sizeof(deflate_buffer);
/* deflate until buffer exhausted and all output is produced */
- while (telnet->z_deflate->avail_in > 0 || telnet->z_deflate->avail_out == 0) {
+ while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
/* compress */
- if ((rs = deflate(telnet->z_deflate, Z_SYNC_FLUSH)) != Z_OK) {
+ if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
_error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, 1,
"deflate() failed: %s", zError(rs));
- deflateEnd(telnet->z_deflate);
- free(telnet->z_deflate);
- telnet->z_deflate = 0;
+ deflateEnd(telnet->z);
+ free(telnet->z);
+ telnet->z = 0;
break;
}
_event(telnet, LIBTELNET_EV_SEND, 0, 0, deflate_buffer,
- sizeof(deflate_buffer) - telnet->z_deflate->avail_out);
+ sizeof(deflate_buffer) - telnet->z->avail_out);
/* prepare output buffer for next run */
- telnet->z_deflate->next_out = deflate_buffer;
- telnet->z_deflate->avail_out = sizeof(deflate_buffer);
+ telnet->z->next_out = deflate_buffer;
+ telnet->z->avail_out = sizeof(deflate_buffer);
}
/* COMPRESS2 is not negotiated, just send */
@@ -476,10 +482,10 @@
* make sure all further data is compressed if not already.
*/
if (telnet->flags & LIBTELNET_FLAG_PROXY &&
- telnet->z_deflate == 0 &&
+ telnet->z == 0 &&
opt == LIBTELNET_TELOPT_COMPRESS2) {
- if ((telnet->z_deflate = _init_zlib(telnet, 1, 1)) == 0)
+ if (_init_zlib(telnet, 1, 1) != LIBTELNET_EOK)
return;
/* notify app that compression was enabled */
@@ -490,23 +496,24 @@
void libtelnet_begin_compress2(libtelnet_t *telnet) {
#ifdef HAVE_ZLIB
- z_stream *zlib;
+ unsigned char compress2[] = { LIBTELNET_IAC, LIBTELNET_SB,
+ LIBTELNET_TELOPT_COMPRESS2, LIBTELNET_IAC, LIBTELNET_SE };
/* don't do this if we've already got a compression stream */
- if (telnet->z_deflate != 0) {
+ if (telnet->z != 0) {
_error(telnet, __LINE__, __func__, LIBTELNET_EBADVAL, 0,
"compression already enabled");
return;
}
/* attempt to create output stream first, bail if we can't */
- if ((zlib = _init_zlib(telnet, 1, 0)) == 0)
+ if (_init_zlib(telnet, 1, 0) != LIBTELNET_EOK)
return;
- /* send compression marker */
- libtelnet_send_subnegotiation(telnet, LIBTELNET_TELOPT_COMPRESS2, 0, 0);
-
- /* set our deflate stream */
- telnet->z_deflate = zlib;
+ /* send compression marker. we send directly to the event handler
+ * instead of passing through _send because _send would result in
+ * the compress marker itself being compressed.
+ */
+ _event(telnet, LIBTELNET_EV_SEND, 0, 0, compress2, sizeof(compress2));
#endif /* HAVE_ZLIB */
}
diff --git a/libtelnet.h b/libtelnet.h
index 6a698c6..f926320 100644
--- a/libtelnet.h
+++ b/libtelnet.h
@@ -92,6 +92,8 @@
/* libtelnet feature flags */
#define LIBTELNET_FLAG_PROXY (1<<0)
+#define LIBTELNET_PFLAG_DEFLATE (1<<7)
+
/* telnet states */
enum libtelnet_state_t {
LIBTELNET_STATE_DATA = 0,
@@ -152,8 +154,7 @@
libtelnet_event_handler_t eh;
#ifdef HAVE_ZLIB
/* zlib (mccp2) compression */
- z_stream *z_deflate;
- z_stream *z_inflate;
+ z_stream *z;
#endif
/* sub-request buffer */
unsigned char *buffer;