Hi,
Yet another patch against 0.2.3, cleaning the previous patch up a bit. Now
failed requests are not COMMAND_RETURN()-ed as NULLs, but an exception
handler is installed, which sends failure messages on caught errors.
I can log in, and request for a pty, but no further testing has been done.
--
Bazsi
PGP info: KeyID 9AF8D0A9 Fingerprint CD27 CFB0 802C 0944 9CFD 804E C82C 8EB1
url: http://www.balabit.hu/pgpkey.txt
diff -urN --exclude-from=diff-exclude lsh-0.2.3-orig/src/channel.c
lsh-0.2.3-bazsi/src/channel.c
--- lsh-0.2.3-orig/src/channel.c Wed Jan 12 23:29:06 2000
+++ lsh-0.2.3-bazsi/src/channel.c Thu Jan 20 11:04:54 2000
@@ -80,32 +80,6 @@
; (start object connection_startup)))
*/
-/* ;; GABA:
- (class
- (name global_request_handler)
- (super packet_handler)
- (vars
- (global_requests object alist)))
-*/
-
-/* ;; GABA:
- (class
- (name channel_open_handler)
- (super packet_handler)
- (vars
- (channel_types object alist)))
-*/
-
-/* ;; GABA:
- (class
- (name channel_open_response)
- (super channel_open_callback)
- (vars
- (remote_channel_number simple UINT32)
- (window_size simple UINT32)
- (max_packet simple UINT32)))
-*/
-
struct lsh_string *format_global_failure(void)
{
return ssh_format("%c", SSH_MSG_REQUEST_FAILURE);
@@ -175,15 +149,6 @@
channel->channel_number, add);
}
-/* ;; GABA:
- (class
- (name channel_exception)
- (super exception)
- (vars
- (channel object ssh_channel)
- (pending_close . int)))
-*/
-
/* GABA:
(class
(name exc_finish_channel_handler)
@@ -421,7 +386,7 @@
/* GABA:
(class
- (name global_request_status)
+ (name request_status)
(vars
; -1 for still active requests,
; 0 for failure,
@@ -429,39 +394,30 @@
(status . int)))
*/
-static struct global_request_status *make_global_request_status(void)
+static struct request_status *make_request_status(void)
{
- NEW(global_request_status, self);
+ NEW(request_status, self);
self->status = -1;
return self;
}
-/* FIXME: Split into a continuation and an exception handler */
/* GABA:
(class
- (name global_request_response)
- (super global_request_callback)
+ (name global_request_continuation)
+ (super command_continuation)
(vars
- (active object global_request_status)))
+ (connection object ssh_connection)
+ (active object request_status)))
*/
-static void
-do_global_request_response(struct global_request_callback *c,
- int success)
+static void
+send_global_request_responses(struct ssh_connection *connection,
+ struct object_queue *q)
{
- CAST(global_request_response, self, c);
- struct object_queue *q = &self->super.connection->table->active_global_requests;
-
- assert( self->active->status == -1);
- assert( (success == 0) || (success == 1) );
- assert( !object_queue_is_empty(q));
-
- self->active->status = success;
-
for (;;)
{
- CAST(global_request_status, n, object_queue_peek_head(q));
+ CAST(request_status, n, object_queue_peek_head(q));
if (!n || (n->status < 0))
break;
@@ -469,27 +425,83 @@
/* FIXME: Perhaps install some exception handler that cancels
* the queue as soon as a write failes. */
- C_WRITE(self->super.connection,
+ C_WRITE(connection,
(n->status
? format_global_success()
: format_global_failure()));
}
}
-static struct global_request_callback *
-make_global_request_response(struct ssh_connection *connection,
- struct global_request_status *active)
+static void
+do_global_request_response(struct command_continuation *s,
+ struct lsh_object *x)
{
- NEW(global_request_response, self);
+ CAST(global_request_continuation, self, s);
+ struct object_queue *q = &self->connection->table->active_global_requests;
+
+ assert(self->active->status == -1);
+ assert(!object_queue_is_empty(q));
+ assert(!x);
+
+ self->active->status = 1;
- self->super.connection = connection;
- self->super.response = do_global_request_response;
+ send_global_request_responses(self->connection, q);
+}
+static struct command_continuation *
+make_global_request_response(struct ssh_connection *connection,
+ struct request_status *active)
+{
+ NEW(global_request_continuation, self);
+
+ self->super.c = do_global_request_response;
+ self->connection = connection;
self->active = active;
return &self->super;
}
-
+
+/* GABA:
+ (class
+ (name global_request_exception_handler)
+ (super exception_handler)
+ (vars
+ (connection object ssh_connection)
+ (active object request_status)))
+*/
+
+static void
+do_global_request_handler(struct exception_handler *c,
+ const struct exception *e)
+{
+ CAST(global_request_exception_handler, self, c);
+ struct object_queue *q = &self->connection->table->active_global_requests;
+
+ assert(self->active->status == -1);
+ assert(!object_queue_is_empty(q));
+
+ self->active->status = 0;
+
+ send_global_request_responses(self->connection, q);
+
+ if (e->type != EXC_GLOBAL_REQUEST)
+ EXCEPTION_RAISE(c->parent, e);
+}
+
+static struct exception_handler *
+make_global_request_exception_handler(struct ssh_connection *connection,
+ struct request_status *active,
+ struct exception_handler *h)
+{
+ NEW(global_request_exception_handler, self);
+
+ self->super.raise = do_global_request_handler;
+ self->super.parent = h;
+ self->active = active;
+ self->connection = connection;
+ return &self->super;
+}
+
static void do_global_request(struct packet_handler *s UNUSED,
struct ssh_connection *connection,
struct lsh_string *packet)
@@ -509,7 +521,8 @@
&& parse_boolean(&buffer, &want_reply))
{
struct global_request *req;
- struct global_request_callback *c = NULL;
+ struct command_continuation *c = &discard_continuation;
+ struct exception_handler *e = connection->e;
if (!name || !(req = ALIST_GET(connection->table->global_requests,
name)))
@@ -523,14 +536,15 @@
{
if (want_reply)
{
- struct global_request_status *a = make_global_request_status();
+ struct request_status *a = make_request_status();
object_queue_add_tail(&connection->table->active_global_requests,
&a->super);
c = make_global_request_response(connection, a);
+ e = make_global_request_exception_handler(connection, a, e);
}
- GLOBAL_REQUEST(req, connection, &buffer, c);
+ GLOBAL_REQUEST(req, connection, &buffer, c, e);
}
}
else
@@ -783,6 +797,112 @@
lsh_string_free(packet);
}
+/* GABA:
+ (class
+ (name channel_request_continuation)
+ (super command_continuation)
+ (vars
+ (connection object ssh_connection)
+ (channel object ssh_channel)
+ (active object request_status)))
+*/
+
+static void
+send_channel_request_responses(struct ssh_connection *connection,
+ struct ssh_channel *channel,
+ struct object_queue *q)
+{
+ for (;;)
+ {
+ CAST(request_status, n, object_queue_peek_head(q));
+ if (!n || (n->status < 0))
+ break;
+
+ object_queue_remove_head(q);
+
+ /* FIXME: Perhaps install some exception handler that cancels
+ * the queue as soon as a write failes. */
+ C_WRITE(connection,
+ (n->status
+ ? format_channel_success(channel->channel_number)
+ : format_channel_failure(channel->channel_number)));
+ }
+}
+
+static void
+do_channel_request_response(struct command_continuation *s,
+ struct lsh_object *x)
+{
+ CAST(channel_request_continuation, self, s);
+ struct object_queue *q = &self->channel->active_requests;
+
+ assert(self->active->status == -1);
+ assert(!object_queue_is_empty(q));
+ assert(!x);
+
+ self->active->status = 1;
+
+ send_channel_request_responses(self->connection, self->channel, q);
+}
+
+static struct command_continuation *
+make_channel_request_response(struct ssh_connection *connection,
+ struct ssh_channel *channel,
+ struct request_status *active)
+{
+ NEW(channel_request_continuation, self);
+
+ self->super.c = do_channel_request_response;
+ self->connection = connection;
+ self->channel = channel;
+ self->active = active;
+
+ return &self->super;
+}
+
+/* GABA:
+ (class
+ (name channel_request_exception_handler)
+ (super exception_handler)
+ (vars
+ (connection object ssh_connection)
+ (channel object ssh_channel)
+ (active object request_status)))
+*/
+
+static void
+do_channel_request_handler(struct exception_handler *c,
+ const struct exception *e)
+{
+ CAST(channel_request_exception_handler, self, c);
+ struct object_queue *q = &self->channel->active_requests;
+
+ assert(self->active->status == -1);
+ assert(!object_queue_is_empty(q));
+
+ self->active->status = 0;
+
+ send_channel_request_responses(self->connection, self->channel, q);
+ if (e->type != EXC_CHANNEL_REQUEST)
+ EXCEPTION_RAISE(c->parent, e);
+}
+
+static struct exception_handler *
+make_channel_request_exception_handler(struct ssh_connection *connection,
+ struct ssh_channel *channel,
+ struct request_status *active,
+ struct exception_handler *h)
+{
+ NEW(channel_request_exception_handler, self);
+
+ self->super.raise = do_channel_request_handler;
+ self->super.parent = h;
+ self->connection = connection;
+ self->channel = channel;
+ self->active = active;
+ return &self->super;
+}
+
static void
do_channel_request(struct packet_handler *closure UNUSED,
struct ssh_connection *connection,
@@ -812,10 +932,25 @@
if (channel)
{
struct channel_request *req;
+ struct command_continuation *c = &discard_continuation;
+ struct exception_handler *e = channel->e;
if (type && channel->request_types
&& ( (req = ALIST_GET(channel->request_types, type)) ))
- CHANNEL_REQUEST(req, channel, connection, want_reply, &buffer);
+ {
+ if (want_reply)
+ {
+ struct request_status *a = make_request_status();
+
+ object_queue_add_tail(&channel->active_requests,
+ &a->super);
+
+ c = make_channel_request_response(connection, channel, a);
+ e = make_channel_request_exception_handler(connection, channel, a,
+e);
+
+ }
+ CHANNEL_REQUEST(req, channel, connection, type, want_reply, &buffer, c,
+e);
+ }
else
{
if (want_reply)
@@ -1498,6 +1633,7 @@
channel->resources = empty_resource_list();
object_queue_init(&channel->pending_requests);
+ object_queue_init(&channel->active_requests);
}
struct lsh_string *channel_transmit_data(struct ssh_channel *channel,
diff -urN --exclude-from=diff-exclude lsh-0.2.3-orig/src/channel.h
lsh-0.2.3-bazsi/src/channel.h
--- lsh-0.2.3-orig/src/channel.h Wed Jan 12 23:31:03 2000
+++ lsh-0.2.3-bazsi/src/channel.h Thu Jan 20 10:59:27 2000
@@ -124,7 +124,11 @@
(open_continuation object command_continuation)
; Queue of channel requests that we expect replies on
- (pending_requests struct object_queue)))
+ (pending_requests struct object_queue)
+
+ ; Channel requests that we have received, and should reply to
+ ; in the right order
+ (active_requests struct object_queue)))
; Reply from SSH_MSG_CHANNEL_REQUEST
;; (channel_success method int)
@@ -201,30 +205,19 @@
))
*/
-
/* SSH_MSG_GLOBAL_REQUEST */
/* GABA:
(class
- (name global_request_callback)
- (vars
- (response method void "int success")
- (connection object ssh_connection)))
-*/
-
-#define GLOBAL_REQUEST_CALLBACK(c, s) \
-((c)->response((c), (s)))
-
-/* GABA:
- (class
(name global_request)
(vars
(handler method void "struct ssh_connection *connection"
"struct simple_buffer *args"
- "struct global_request_callback *response")))
+ "struct command_continuation *c"
+ "struct exception_handler *e")))
*/
-#define GLOBAL_REQUEST(r, c, a, n) ((r)->handler((r), (c), (a), (n)))
+#define GLOBAL_REQUEST(r, c, a, n, e) ((r)->handler((r), (c), (a), (n), (e)))
/* SSH_MSG_CHANNEL_OPEN */
@@ -264,20 +257,15 @@
(handler method void
"struct ssh_channel *channel"
"struct ssh_connection *connection"
+ "UINT32 type"
"int want_reply"
- "struct simple_buffer *args")))
+ "struct simple_buffer *args"
+ "struct command_continuation *c"
+ "struct exception_handler *e")))
*/
-#define CHANNEL_REQUEST(s, c, conn, w, a) \
-((s)->handler((s), (c), (conn), (w), (a)))
-
-/* ;;GABA:
- (class
- (name connection_startup)
- (vars
- (start method int
- "struct ssh_connection *connection")))
-*/
+#define CHANNEL_REQUEST(s, c, conn, t, w, a, n, e) \
+((s)->handler((s), (c), (conn), (t), (w), (a), (n), (e)))
/* #define CONNECTION_START(c, s) ((c)->start((c), (s))) */
diff -urN --exclude-from=diff-exclude lsh-0.2.3-orig/src/client.c
lsh-0.2.3-bazsi/src/client.c
--- lsh-0.2.3-orig/src/client.c Sun Dec 12 19:47:53 1999
+++ lsh-0.2.3-bazsi/src/client.c Thu Jan 20 10:11:02 2000
@@ -228,8 +228,11 @@
do_exit_status(struct channel_request *c,
struct ssh_channel *channel,
struct ssh_connection *connection UNUSED,
+ UINT32 type UNUSED,
int want_reply,
- struct simple_buffer *args)
+ struct simple_buffer *args,
+ struct command_continuation *s,
+ struct exception_handler *e)
{
CAST(exit_handler, closure, c);
int status;
@@ -248,18 +251,24 @@
* child process alive that we could talk to. */
channel_eof(channel);
+ COMMAND_RETURN(s, NULL);
}
else
- /* Invalid request */
- PROTOCOL_ERROR(channel->e, "Invalid exit-status message");
+ {
+ /* Invalid request */
+ PROTOCOL_ERROR(e, "Invalid exit-status message");
+ }
}
static void
do_exit_signal(struct channel_request *c,
struct ssh_channel *channel,
struct ssh_connection *connection UNUSED,
+ UINT32 type UNUSED,
int want_reply,
- struct simple_buffer *args)
+ struct simple_buffer *args,
+ struct command_continuation *s,
+ struct exception_handler *e)
{
CAST(exit_handler, closure, c);
@@ -299,10 +308,13 @@
* child process alive that we could talk to. */
channel_eof(channel);
+ COMMAND_RETURN(s, NULL);
}
else
- /* Invalid request */
- PROTOCOL_ERROR(channel->e, "Invalid exit-signal message");
+ {
+ /* Invalid request */
+ PROTOCOL_ERROR(e, "Invalid exit-signal message");
+ }
}
struct channel_request *make_handle_exit_status(int *exit_status)
diff -urN --exclude-from=diff-exclude lsh-0.2.3-orig/src/server_session.c
lsh-0.2.3-bazsi/src/server_session.c
--- lsh-0.2.3-orig/src/server_session.c Wed Jan 12 23:50:00 2000
+++ lsh-0.2.3-bazsi/src/server_session.c Thu Jan 20 11:05:41 2000
@@ -617,12 +617,18 @@
#define USE_LOGIN_DASH_CONVENTION 1
+static struct exception shell_request_failed =
+STATIC_EXCEPTION(EXC_CHANNEL_REQUEST, "Shell request failed");
+
static void
do_spawn_shell(struct channel_request *c,
struct ssh_channel *channel,
- struct ssh_connection *connection,
- int want_reply,
- struct simple_buffer *args)
+ struct ssh_connection *connection UNUSED,
+ UINT32 type UNUSED,
+ int want_reply UNUSED,
+ struct simple_buffer *args,
+ struct command_continuation *s,
+ struct exception_handler *e)
{
CAST(shell_request, closure, c);
struct server_session *session = (struct server_session *) channel;
@@ -637,7 +643,7 @@
if (!parse_eod(args))
{
- PROTOCOL_ERROR(connection->e, "Invalid shell CHANNEL_REQUEST message.");
+ PROTOCOL_ERROR(e, "Invalid shell CHANNEL_REQUEST message.");
return;
}
@@ -899,9 +905,12 @@
REMEMBER_RESOURCE
(channel->resources, &session->err->super.super);
- if (want_reply)
- A_WRITE(channel->write,
- format_channel_success(channel->channel_number) );
+ COMMAND_RETURN(s, NULL);
+ /*
+ if (want_reply)
+ A_WRITE(channel->write,
+ format_channel_success(channel->channel_number) );
+ */
channel_start_receive(channel);
return;
@@ -914,9 +923,13 @@
close(in[1]);
}
fail:
- if (want_reply)
+ EXCEPTION_RAISE(e, &shell_request_failed);
+ /* COMMAND_RETURN(s, NULL); */
+
+ /*
A_WRITE(channel->write,
- format_channel_failure(channel->channel_number));
+ format_channel_failure(channel->channel_number));
+ */
}
struct channel_request *make_shell_handler(struct io_backend *backend,
@@ -932,13 +945,20 @@
}
#if WITH_PTY_SUPPORT
+
+static struct exception pty_request_failed =
+STATIC_EXCEPTION(EXC_CHANNEL_REQUEST, "pty request failed");
+
/* pty_handler */
static void
do_alloc_pty(struct channel_request *c UNUSED,
struct ssh_channel *channel,
struct ssh_connection *connection UNUSED,
- int want_reply,
- struct simple_buffer *args)
+ UINT32 type UNUSED,
+ int want_reply UNUSED,
+ struct simple_buffer *args,
+ struct command_continuation *s,
+ struct exception_handler *e)
{
UINT32 width, height, width_p, height_p;
UINT8 *mode;
@@ -988,9 +1008,12 @@
REMEMBER_RESOURCE(channel->resources, &pty->super);
verbose(" granted.\n");
- if (want_reply)
- A_WRITE(channel->write,
- format_channel_success(channel->channel_number) );
+ COMMAND_RETURN(s, NULL);
+ /*
+ if (want_reply)
+ A_WRITE(channel->write,
+ format_channel_success(channel->channel_number) );
+ */
return;
}
else
@@ -1003,10 +1026,12 @@
verbose("Pty allocation failed.\n");
lsh_string_free(term);
-
- if (want_reply)
- A_WRITE(channel->write,
- format_channel_failure(channel->channel_number) );
+ /*
+ if (want_reply)
+ A_WRITE(channel->write,
+ format_channel_failure(channel->channel_number) );
+ */
+ EXCEPTION_RAISE(e, &pty_request_failed);
}
struct channel_request *make_pty_handler(void)
diff -urN --exclude-from=diff-exclude lsh-0.2.3-orig/src/tcpforward.c
lsh-0.2.3-bazsi/src/tcpforward.c
--- lsh-0.2.3-orig/src/tcpforward.c Wed Jan 12 23:52:09 2000
+++ lsh-0.2.3-bazsi/src/tcpforward.c Thu Jan 20 11:07:27 2000
@@ -316,7 +316,7 @@
(vars
(connection object ssh_connection)
(forward object local_port)
- (c object global_request_callback)))
+ (c object command_continuation)))
*/
/* FIXME: Split off an exception handler */
@@ -340,20 +340,20 @@
assert(port);
assert(port == self->forward);
- GLOBAL_REQUEST_CALLBACK(self->c, 0);
+ COMMAND_RETURN(self->c, NULL);
}
REMEMBER_RESOURCE(self->connection->resources, &fd->super);
self->forward->socket = fd;
- GLOBAL_REQUEST_CALLBACK(self->c, 1);
+ COMMAND_RETURN(self->c, &self->forward->super.super);
}
static struct command_continuation *
make_tcpip_forward_request_continuation(struct ssh_connection *connection,
struct local_port *forward,
- struct global_request_callback *c)
+ struct command_continuation *c)
{
NEW(tcpip_forward_request_continuation, self);
@@ -380,7 +380,8 @@
do_tcpip_forward_request(struct global_request *s,
struct ssh_connection *connection,
struct simple_buffer *args,
- struct global_request_callback *c)
+ struct command_continuation *c,
+ struct exception_handler *e)
{
CAST(tcpip_forward_request, self, s);
struct lsh_string *bind_host;
@@ -396,15 +397,18 @@
if (bind_port < 1024)
{
werror("Denying forwarding of privileged port %i.\n", bind_port);
- GLOBAL_REQUEST_CALLBACK(c, 0);
+ COMMAND_RETURN(c, NULL);
return;
}
if (lookup_forward(&connection->table->local_ports,
bind_host->length, bind_host->data, bind_port))
{
+ static const struct exception again =
+ STATIC_EXCEPTION(EXC_GLOBAL_REQUEST, "An already requested tcp-forward
+requested again");
+
verbose("An already requested tcp-forward requested again\n");
- GLOBAL_REQUEST_CALLBACK(c, 0);
+ EXCEPTION_RAISE(e, &again);
return;
}
@@ -428,7 +432,7 @@
else
{
werror("Incorrectly formatted tcpip-forward request\n");
- PROTOCOL_ERROR(connection->e, "Invalid tcpip-forward message.");
+ PROTOCOL_ERROR(e, "Invalid tcpip-forward message.");
}
}
@@ -446,7 +450,8 @@
do_tcpip_cancel_forward(struct global_request *s UNUSED,
struct ssh_connection *connection,
struct simple_buffer *args,
- struct global_request_callback *c)
+ struct command_continuation *c,
+ struct exception_handler *e)
{
UINT32 bind_host_length;
UINT8 *bind_host;
@@ -472,14 +477,16 @@
close_fd(port->socket, 0);
port->socket = NULL;
- GLOBAL_REQUEST_CALLBACK(c, 1);
+ COMMAND_RETURN(c, NULL);
return;
}
else
{
+ static const struct exception notfound =
+ STATIC_EXCEPTION(EXC_GLOBAL_REQUEST, "Could not find tcpip-forward to
+cancel");
verbose("Could not find tcpip-forward to cancel\n");
- GLOBAL_REQUEST_CALLBACK(c, 0);
+ EXCEPTION_RAISE(e, ¬found);
return;
}
}
diff -urN --exclude-from=diff-exclude lsh-0.2.3-orig/src/zlib.c
lsh-0.2.3-bazsi/src/zlib.c
--- lsh-0.2.3-orig/src/zlib.c Sun Jan 9 19:02:27 2000
+++ lsh-0.2.3-bazsi/src/zlib.c Wed Jan 19 14:41:04 2000
@@ -91,13 +91,15 @@
}
/* Estimates of the resulting packet sizes. We use fixnum arithmetic,
- * with one represented as 1<<10=1024. Only rates between 1/8 and 8
- * are used. */
+ * with one represented as 1<<10=1024. Only rates between 1/16 and 16
+ * are used. This may be a little too conservative; I have observed
+ * compression ratios of about 50. */
#define RATE_UNIT 1024
-#define RATE_MAX (RATE_UNIT * 8)
-#define RATE_MIN (RATE_UNIT / 8)
+#define RATE_MAX (RATE_UNIT * 16)
+#define RATE_MIN (RATE_UNIT / 16)
#define MARGIN 200
+#define INSIGNIFICANT 100
static UINT32 estimate_size(UINT32 rate, UINT32 input, UINT32 max)
{
@@ -108,17 +110,28 @@
/* Assumes that input is nonzero */
static UINT32 estimate_update(UINT32 rate, UINT32 input, UINT32 output)
{
- UINT32 estimate = output * rate / input;
-
- if (estimate > RATE_MAX)
- return RATE_MAX;
-
/* Decay old estimate */
rate = rate * 15 / 16;
- /* Follow the "envelope" */
- rate = MAX(estimate, rate);
+ /* FIXME: Following the envelope is suboptimal for small inputs. We
+ * do it only for input packets of reasonable size. This method
+ * could be improved.
+ *
+ * Perhaps a linear combination k * rate + (1-k) estimate, where k
+ * depends on the size of the sample (i.e. input) would make sense?
+ * Or use different rate estimates for different lengths? */
+
+ if (input > INSIGNIFICANT)
+ {
+ UINT32 estimate = output * RATE_UNIT / input;
+
+ if (estimate > RATE_MAX)
+ return RATE_MAX;
+ /* Follow the "envelope" */
+ rate = MAX(estimate, rate);
+ }
+
return MAX(rate, RATE_MIN);
}
@@ -129,19 +142,30 @@
{
CAST(zlib_instance, self, c);
struct string_buffer buffer;
- UINT32 limit = self->max;
+
+ /* LIMIT keeps track of the amount of storage we may still need to
+ * allocate. To detect that a packet grows unexpectedly large, we
+ * need a little extra buffer space beyond the maximum size. */
+ UINT32 limit = self->max + 1;
+
+ UINT32 estimate;
+
+ debug("do_zlib: length in: %i\n", packet->length);
if (!packet->length)
{
werror("do_zlib_deflate: Compressing empty packet.\n");
return free ? packet : lsh_string_dup(packet);
}
-
+
+ estimate = estimate_size(self->rate, packet->length, self->max);
+ debug("do_zlib: estimate: %i\n", estimate);
+
string_buffer_init(&buffer,
estimate_size(self->rate, packet->length, self->max));
limit -= buffer.partial->length;
-
+
self->z.next_in = packet->data;
self->z.avail_in = packet->length;
@@ -149,8 +173,6 @@
{
int rc;
- assert(self->z.avail_in);
-
self->z.next_out = buffer.current;
self->z.avail_out = buffer.left;
@@ -166,7 +188,32 @@
return NULL;
}
- if (!self->z.avail_in)
+ /* NOTE: It's not enough to check that avail_in is zero to
+ * determine that all data have been flushed. avail_in == 0 and
+ * avail_out > 0 implies that all data has been flushed, but if
+ * avail_in == avail_out == 0, we have to allocate more output
+ * space. */
+
+ if (!self->z.avail_in && !self->z.avail_out)
+ verbose("do_zlib: Both avail_in and avail_out are zero.\n");
+
+ if (!self->z.avail_out)
+ { /* All output space consumed */
+ if (!limit)
+ {
+ werror("do_zlib_deflate: Packet grew too large!\n");
+ if (free)
+ lsh_string_free(packet);
+
+ string_buffer_clear(&buffer);
+ return NULL;
+ }
+
+ /* Grow to about double size. */
+ string_buffer_grow(&buffer, MIN(limit, buffer.partial->length + buffer.total
++ 100));
+ limit -= buffer.partial->length;
+ }
+ else if (!self->z.avail_in)
{ /* Compressed entire packet */
UINT32 input = packet->length;
@@ -176,26 +223,17 @@
packet =
string_buffer_final(&buffer, self->z.avail_out);
- self->rate = estimate_update(self->rate, input, packet->length);
+ assert(packet->length <= self->max);
- return packet;
- }
- else
- { /* All output space consumed */
- assert(!self->z.avail_out);
-
- if (!limit)
- {
- werror("do_zlib_deflate: Packet grew too large!\n");
- if (free)
- lsh_string_free(packet);
+ debug("do_zlib: length out: %i\n", packet->length);
- string_buffer_clear(&buffer);
- return NULL;
- }
+ if (packet->length > estimate)
+ verbose("do_zlib: Estimated size exceeded: input = %i, estimate = %i,
+output = %i\n",
+ input, estimate, packet->length);
+
+ self->rate = estimate_update(self->rate, input, packet->length);
- string_buffer_grow(&buffer, MIN(limit, packet->length + 100));
- limit -= buffer.partial->length;
+ return packet;
}
}
}