You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by wr...@apache.org on 2014/08/22 13:37:20 UTC
svn commit: r1619753 - in /httpd/httpd/branches/2.2.x: ./ CHANGES
modules/filters/mod_deflate.c
Author: wrowe
Date: Fri Aug 22 11:37:19 2014
New Revision: 1619753
URL: http://svn.apache.org/r1619753
Log:
mod_deflate: Handle Zlib header and validation bytes received in multiple
chunks.
PR: 46146, 55666
Submitted by: Yann Ylavic
Reviewed by: ylavic, wrowe, rpluem
Backports: r1572655, r1572663, r1572668, r1572669, r1572670, r1572671, r1573224, r1586745, r1587594, r1587639, r1590509, r1603156, r1604353
Modified:
httpd/httpd/branches/2.2.x/ (props changed)
httpd/httpd/branches/2.2.x/CHANGES
httpd/httpd/branches/2.2.x/modules/filters/mod_deflate.c
Propchange: httpd/httpd/branches/2.2.x/
------------------------------------------------------------------------------
Merged /httpd/httpd/trunk:r1572655,1572663,1572668-1572671,1573224,1586745,1587594,1587639,1590509,1604353
Modified: httpd/httpd/branches/2.2.x/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/CHANGES?rev=1619753&r1=1619752&r2=1619753&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/CHANGES [utf-8] (original)
+++ httpd/httpd/branches/2.2.x/CHANGES [utf-8] Fri Aug 22 11:37:19 2014
@@ -20,6 +20,9 @@ Changes with Apache 2.2.28
*) SECURITY: CVE-2014-0226 (cve.mitre.org)
Fix a race condition in scoreboard handling, which could lead to
a heap buffer overflow. [Joe Orton, Eric Covener, Jeff Trawick]
+
+ *) mod_deflate: Handle Zlib header and validation bytes received in multiple
+ chunks. PR 46146. [Yann Ylavic]
*) SECURITY: CVE-2013-5704 (cve.mitre.org)
core: HTTP trailers could be used to replace HTTP headers
Modified: httpd/httpd/branches/2.2.x/modules/filters/mod_deflate.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/modules/filters/mod_deflate.c?rev=1619753&r1=1619752&r2=1619753&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/modules/filters/mod_deflate.c (original)
+++ httpd/httpd/branches/2.2.x/modules/filters/mod_deflate.c Fri Aug 22 11:37:19 2014
@@ -360,9 +360,15 @@ typedef struct deflate_ctx_t
int (*libz_end_func)(z_streamp);
unsigned char *validation_buffer;
apr_size_t validation_buffer_length;
- int inflate_init;
+ char header[10]; /* sizeof(gzip_header) */
+ apr_size_t header_len;
+ int zlib_flags;
int ratio_hits;
apr_off_t inflate_total;
+ unsigned int consume_pos,
+ consume_len;
+ unsigned int filter_init:1;
+ unsigned int done:1;
} deflate_ctx;
/* Check whether the (inflate) ratio exceeds the configured limit/burst. */
@@ -749,6 +755,9 @@ static apr_status_t deflate_out_filter(a
zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate,
Z_SYNC_FLUSH, NO_UPDATE_CRC);
if (zRC != Z_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Zlib error %d flushing zlib output buffer (%s)",
+ zRC, ctx->stream.msg);
return APR_EGENERAL;
}
@@ -774,6 +783,14 @@ static apr_status_t deflate_out_filter(a
/* read */
apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ if (!len) {
+ apr_bucket_delete(e);
+ continue;
+ }
+ if (len > APR_INT32_MAX) {
+ apr_bucket_split(e, APR_INT32_MAX);
+ apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ }
/* This crc32 function is from zlib. */
ctx->crc = crc32(ctx->crc, (const Bytef *)data, len);
@@ -805,6 +822,9 @@ static apr_status_t deflate_out_filter(a
zRC = deflate(&(ctx->stream), Z_NO_FLUSH);
if (zRC != Z_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Zlib error %d deflating data (%s)", zRC,
+ ctx->stream.msg);
return APR_EGENERAL;
}
}
@@ -816,6 +836,96 @@ static apr_status_t deflate_out_filter(a
return APR_SUCCESS;
}
+static apr_status_t consume_zlib_flags(deflate_ctx *ctx,
+ const char **data, apr_size_t *len)
+{
+ if ((ctx->zlib_flags & EXTRA_FIELD)) {
+ /* Consume 2 bytes length prefixed data. */
+ if (ctx->consume_pos == 0) {
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ ctx->consume_len = (unsigned int)**data;
+ ctx->consume_pos++;
+ ++*data;
+ --*len;
+ }
+ if (ctx->consume_pos == 1) {
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ ctx->consume_len += ((unsigned int)**data) << 8;
+ ctx->consume_pos++;
+ ++*data;
+ --*len;
+ }
+ if (*len < ctx->consume_len) {
+ ctx->consume_len -= *len;
+ *len = 0;
+ return APR_INCOMPLETE;
+ }
+ *data += ctx->consume_len;
+ *len -= ctx->consume_len;
+
+ ctx->consume_len = ctx->consume_pos = 0;
+ ctx->zlib_flags &= ~EXTRA_FIELD;
+ }
+
+ if ((ctx->zlib_flags & ORIG_NAME)) {
+ /* Consume nul terminated string. */
+ while (*len && **data) {
+ ++*data;
+ --*len;
+ }
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ /* .. and nul. */
+ ++*data;
+ --*len;
+
+ ctx->zlib_flags &= ~ORIG_NAME;
+ }
+
+ if ((ctx->zlib_flags & COMMENT)) {
+ /* Consume nul terminated string. */
+ while (*len && **data) {
+ ++*data;
+ --*len;
+ }
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ /* .. and nul. */
+ ++*data;
+ --*len;
+
+ ctx->zlib_flags &= ~COMMENT;
+ }
+
+ if ((ctx->zlib_flags & HEAD_CRC)) {
+ /* Consume CRC16 (2 octets). */
+ if (ctx->consume_pos == 0) {
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ ctx->consume_pos++;
+ ++*data;
+ --*len;
+ }
+ if (!*len) {
+ return APR_INCOMPLETE;
+ }
+ ++*data;
+ --*len;
+
+ ctx->consume_pos = 0;
+ ctx->zlib_flags &= ~HEAD_CRC;
+ }
+
+ return APR_SUCCESS;
+}
+
/* This is the deflate input filter (inflates). */
static apr_status_t deflate_in_filter(ap_filter_t *f,
apr_bucket_brigade *bb,
@@ -840,64 +950,98 @@ static apr_status_t deflate_in_filter(ap
c = ap_get_module_config(r->server->module_config, &deflate_module);
dc = ap_get_module_config(r->per_dir_config, &deflate_module);
- if (!ctx) {
- char deflate_hdr[10];
+ if (!ctx || ctx->header_len < sizeof(ctx->header)) {
apr_size_t len;
- /* only work on main request/no subrequests */
- if (!ap_is_initial_req(r)) {
- ap_remove_input_filter(f);
- return ap_get_brigade(f->next, bb, mode, block, readbytes);
- }
+ if (!ctx) {
+ /* only work on main request/no subrequests */
+ if (!ap_is_initial_req(r)) {
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ /* We can't operate on Content-Ranges */
+ if (apr_table_get(r->headers_in, "Content-Range") != NULL) {
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ /* Check whether request body is gzipped.
+ *
+ * If it is, we're transforming the contents, invalidating
+ * some request headers including Content-Encoding.
+ *
+ * If not, we just remove ourself.
+ */
+ if (check_gzip(r, r->headers_in, NULL) == 0) {
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
- /* We can't operate on Content-Ranges */
- if (apr_table_get(r->headers_in, "Content-Range") != NULL) {
- ap_remove_input_filter(f);
- return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+ ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+ ctx->buffer = apr_palloc(r->pool, c->bufferSize);
}
- /* Check whether request body is gzipped.
- *
- * If it is, we're transforming the contents, invalidating
- * some request headers including Content-Encoding.
- *
- * If not, we just remove ourself.
- */
- if (check_gzip(r, r->headers_in, NULL) == 0) {
- ap_remove_input_filter(f);
- return ap_get_brigade(f->next, bb, mode, block, readbytes);
- }
+ do {
+ apr_brigade_cleanup(ctx->bb);
- f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
- ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
- ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
- ctx->buffer = apr_palloc(r->pool, c->bufferSize);
+ len = sizeof(ctx->header) - ctx->header_len;
+ rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block,
+ len);
- rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10);
- if (rv != APR_SUCCESS) {
- return rv;
- }
+ /* ap_get_brigade may return success with an empty brigade for
+ * a non-blocking read which would block (an empty brigade for
+ * a blocking read is an issue which is simply forwarded here).
+ */
+ if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(ctx->bb)) {
+ return rv;
+ }
- apr_table_unset(r->headers_in, "Content-Length");
- apr_table_unset(r->headers_in, "Content-MD5");
+ /* zero length body? step aside */
+ bkt = APR_BRIGADE_FIRST(ctx->bb);
+ if (APR_BUCKET_IS_EOS(bkt)) {
+ if (ctx->header_len) {
+ /* If the header was (partially) read it's an error, this
+ * is not a gzip Content-Encoding, as claimed.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Encountered premature end-of-stream while "
+ "reading inflate header");
+ return APR_EGENERAL;
+ }
+ APR_BUCKET_REMOVE(bkt);
+ APR_BRIGADE_INSERT_TAIL(bb, bkt);
+ ap_remove_input_filter(f);
+ return APR_SUCCESS;
+ }
- len = 10;
- rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len);
- if (rv != APR_SUCCESS) {
- return rv;
- }
+ rv = apr_brigade_flatten(ctx->bb,
+ ctx->header + ctx->header_len, &len);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ if (len && !ctx->header_len) {
+ apr_table_unset(r->headers_in, "Content-Length");
+ apr_table_unset(r->headers_in, "Content-MD5");
+ }
+ ctx->header_len += len;
+
+ } while (ctx->header_len < sizeof(ctx->header));
/* We didn't get the magic bytes. */
- if (len != 10 ||
- deflate_hdr[0] != deflate_magic[0] ||
- deflate_hdr[1] != deflate_magic[1]) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Failed to inflate input: wrong/partial magic bytes");
+ if (ctx->header[0] != deflate_magic[0] ||
+ ctx->header[1] != deflate_magic[1]) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "Zlib: Invalid header");
return APR_EGENERAL;
}
- /* We can't handle flags for now. */
- if (deflate_hdr[3] != 0) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Failed to inflate input: cannot handle deflate flags");
+ ctx->zlib_flags = ctx->header[3];
+ if ((ctx->zlib_flags & RESERVED)) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "Zlib: Invalid flags %02x", ctx->zlib_flags);
return APR_EGENERAL;
}
@@ -930,8 +1074,15 @@ static apr_status_t deflate_in_filter(ap
if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
+ /* Don't terminate on EAGAIN (or success with an empty brigade in
+ * non-blocking mode), just return focus.
+ */
+ if (block == APR_NONBLOCK_READ
+ && (APR_STATUS_IS_EAGAIN(rv)
+ || (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(ctx->bb)))) {
+ return rv;
+ }
if (rv != APR_SUCCESS) {
- /* What about APR_EAGAIN errors? */
inflateEnd(&ctx->stream);
return rv;
}
@@ -945,14 +1096,26 @@ static apr_status_t deflate_in_filter(ap
/* If we actually see the EOS, that means we screwed up! */
if (APR_BUCKET_IS_EOS(bkt)) {
- inflateEnd(&ctx->stream);
- return APR_EGENERAL;
+ if (!ctx->done) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Encountered premature end-of-stream while inflating");
+ return APR_EGENERAL;
+ }
+
+ /* Move everything to the returning brigade. */
+ APR_BUCKET_REMOVE(bkt);
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, bkt);
+ break;
}
if (APR_BUCKET_IS_FLUSH(bkt)) {
- apr_bucket *tmp_heap;
+ apr_bucket *tmp_b;
zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
if (zRC != Z_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "Zlib error %d inflating data (%s)", zRC,
+ ctx->stream.msg);
inflateEnd(&ctx->stream);
return APR_EGENERAL;
}
@@ -972,72 +1135,130 @@ static apr_status_t deflate_in_filter(ap
}
ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
- tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
- NULL, f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
+ tmp_b = apr_bucket_heap_create((char *)ctx->buffer, len,
+ NULL, f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_b);
ctx->stream.avail_out = c->bufferSize;
- /* Move everything to the returning brigade. */
+ /* Flush everything so far in the returning brigade, but continue
+ * reading should EOS/more follow (don't lose them).
+ */
+ tmp_b = APR_BUCKET_PREV(bkt);
APR_BUCKET_REMOVE(bkt);
- APR_BRIGADE_CONCAT(bb, ctx->bb);
- break;
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, bkt);
+ bkt = tmp_b;
+ continue;
+ }
+
+ /* sanity check - data after completed compressed body and before eos? */
+ if (ctx->done) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Encountered extra data after compressed data");
+ return APR_EGENERAL;
}
/* read */
apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
+ if (!len) {
+ continue;
+ }
+ if (len > APR_INT32_MAX) {
+ apr_bucket_split(bkt, APR_INT32_MAX);
+ apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
+ }
+
+ if (ctx->zlib_flags) {
+ rv = consume_zlib_flags(ctx, &data, &len);
+ if (rv == APR_SUCCESS) {
+ ctx->zlib_flags = 0;
+ }
+ if (!len) {
+ continue;
+ }
+ }
/* pass through zlib inflate. */
ctx->stream.next_in = (unsigned char *)data;
- ctx->stream.avail_in = len;
+ ctx->stream.avail_in = (int)len;
zRC = Z_OK;
- while (ctx->stream.avail_in != 0) {
- if (ctx->stream.avail_out == 0) {
- apr_bucket *tmp_heap;
- ctx->stream.next_out = ctx->buffer;
- len = c->bufferSize - ctx->stream.avail_out;
-
- ctx->inflate_total += len;
- if (inflate_limit && ctx->inflate_total > inflate_limit) {
- inflateEnd(&ctx->stream);
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
- "Inflated content length of %" APR_OFF_T_FMT
- " is larger than the configured limit"
- " of %" APR_OFF_T_FMT,
- ctx->inflate_total, inflate_limit);
- return APR_ENOSPC;
- }
-
- if (!check_ratio(r, ctx, dc)) {
- inflateEnd(&ctx->stream);
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
- "Inflated content ratio is larger than the "
- "configured limit %i by %i time(s)",
- dc->ratio_limit, dc->ratio_burst);
- return APR_EINVAL;
- }
-
- ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
- tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
- NULL, f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
- ctx->stream.avail_out = c->bufferSize;
- }
+ if (!ctx->validation_buffer) {
+ while (ctx->stream.avail_in != 0) {
+ if (ctx->stream.avail_out == 0) {
+ apr_bucket *tmp_heap;
+ ctx->stream.next_out = ctx->buffer;
+ len = c->bufferSize - ctx->stream.avail_out;
+
+ ctx->inflate_total += len;
+ if (inflate_limit && ctx->inflate_total > inflate_limit) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "Inflated content length of %" APR_OFF_T_FMT
+ " is larger than the configured limit"
+ " of %" APR_OFF_T_FMT,
+ ctx->inflate_total, inflate_limit);
+ return APR_ENOSPC;
+ }
+
+ if (!check_ratio(r, ctx, dc)) {
+ inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "Inflated content ratio is larger than the "
+ "configured limit %i by %i time(s)",
+ dc->ratio_limit, dc->ratio_burst);
+ return APR_EINVAL;
+ }
+
+ ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
+ tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
+ NULL, f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
+ ctx->stream.avail_out = c->bufferSize;
+ }
- zRC = inflate(&ctx->stream, Z_NO_FLUSH);
+ zRC = inflate(&ctx->stream, Z_NO_FLUSH);
- if (zRC == Z_STREAM_END) {
- break;
- }
+ if (zRC == Z_STREAM_END) {
+ ctx->validation_buffer = apr_pcalloc(r->pool,
+ VALIDATION_SIZE);
+ ctx->validation_buffer_length = 0;
+ break;
+ }
- if (zRC != Z_OK) {
- inflateEnd(&ctx->stream);
- return APR_EGENERAL;
+ if (zRC != Z_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "Zlib error %d inflating data (%s)", zRC,
+ ctx->stream.msg);
+ inflateEnd(&ctx->stream);
+ return APR_EGENERAL;
+ }
}
}
- if (zRC == Z_STREAM_END) {
- apr_bucket *tmp_heap, *eos;
+
+ if (ctx->validation_buffer) {
+ apr_bucket *tmp_heap;
+ apr_size_t avail, valid;
+ unsigned char *buf = ctx->validation_buffer;
+
+ avail = ctx->stream.avail_in;
+ valid = (apr_size_t)VALIDATION_SIZE -
+ ctx->validation_buffer_length;
+
+ /*
+ * We have inflated all data. Now try to capture the
+ * validation bytes. We may not have them all available
+ * right now, but capture what is there.
+ */
+ if (avail < valid) {
+ memcpy(buf + ctx->validation_buffer_length,
+ ctx->stream.next_in, avail);
+ ctx->validation_buffer_length += avail;
+ continue;
+ }
+ memcpy(buf + ctx->validation_buffer_length,
+ ctx->stream.next_in, valid);
+ ctx->validation_buffer_length += valid;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"Zlib: Inflated %ld to %ld : URL %s",
@@ -1052,34 +1273,37 @@ static apr_status_t deflate_in_filter(ap
APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
ctx->stream.avail_out = c->bufferSize;
- /* Is the remaining 8 bytes already in the avail stream? */
- if (ctx->stream.avail_in >= 8) {
+ {
unsigned long compCRC, compLen;
- compCRC = getLong(ctx->stream.next_in);
+ compCRC = getLong(buf);
if (ctx->crc != compCRC) {
inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "Zlib: CRC error inflating data");
return APR_EGENERAL;
}
- ctx->stream.next_in += 4;
- compLen = getLong(ctx->stream.next_in);
+ compLen = getLong(buf + VALIDATION_SIZE / 2);
/* gzip stores original size only as 4 byte value */
if ((ctx->stream.total_out & 0xFFFFFFFF) != compLen) {
inflateEnd(&ctx->stream);
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "Zlib: Length %ld of inflated data does "
+ "not match expected value %ld",
+ ctx->stream.total_out, compLen);
return APR_EGENERAL;
}
}
- else {
- /* FIXME: We need to grab the 8 verification bytes
- * from the wire! */
- inflateEnd(&ctx->stream);
- return APR_EGENERAL;
- }
inflateEnd(&ctx->stream);
- eos = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos);
- break;
+ ctx->done = 1;
+
+ /* Did we have trailing data behind the closing 8 bytes? */
+ if (avail > valid) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Encountered extra data after compressed data");
+ return APR_EGENERAL;
+ }
}
}
@@ -1090,8 +1314,8 @@ static apr_status_t deflate_in_filter(ap
* some data in our zlib buffer, flush it out so we can return something.
*/
if (block == APR_BLOCK_READ &&
- APR_BRIGADE_EMPTY(ctx->proc_bb) &&
- ctx->stream.avail_out < c->bufferSize) {
+ APR_BRIGADE_EMPTY(ctx->proc_bb) &&
+ ctx->stream.avail_out < c->bufferSize) {
apr_bucket *tmp_heap;
apr_size_t len;
ctx->stream.next_out = ctx->buffer;
@@ -1113,6 +1337,9 @@ static apr_status_t deflate_in_filter(ap
newbb = apr_brigade_split(ctx->proc_bb, bkt);
APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
APR_BRIGADE_CONCAT(ctx->proc_bb, newbb);
+ if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
+ ap_remove_input_filter(f);
+ }
}
return APR_SUCCESS;
@@ -1123,8 +1350,6 @@ static apr_status_t deflate_in_filter(ap
static apr_status_t inflate_out_filter(ap_filter_t *f,
apr_bucket_brigade *bb)
{
- int zlib_method;
- int zlib_flags;
apr_bucket *e;
request_rec *r = f->r;
deflate_ctx *ctx = f->ctx;
@@ -1209,8 +1434,6 @@ static apr_status_t inflate_out_filter(a
/* initialize inflate output buffer */
ctx->stream.next_out = ctx->buffer;
ctx->stream.avail_out = c->bufferSize;
-
- ctx->inflate_init = 0;
}
while (!APR_BRIGADE_EMPTY(bb))
@@ -1320,56 +1543,68 @@ static apr_status_t inflate_out_filter(a
/* read */
apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ if (!len) {
+ apr_bucket_delete(e);
+ continue;
+ }
+ if (len > APR_INT32_MAX) {
+ apr_bucket_split(e, APR_INT32_MAX);
+ apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
+ }
/* first bucket contains zlib header */
- if (!ctx->inflate_init++) {
- if (len < 10) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Insufficient data for inflate");
- return APR_EGENERAL;
- }
- else {
- zlib_method = data[2];
- zlib_flags = data[3];
+ if (ctx->header_len < sizeof(ctx->header)) {
+ apr_size_t rem;
+
+ rem = sizeof(ctx->header) - ctx->header_len;
+ if (len < rem) {
+ memcpy(ctx->header + ctx->header_len, data, len);
+ ctx->header_len += len;
+ apr_bucket_delete(e);
+ continue;
+ }
+ memcpy(ctx->header + ctx->header_len, data, rem);
+ ctx->header_len += rem;
+ {
+ int zlib_method;
+ zlib_method = ctx->header[2];
if (zlib_method != Z_DEFLATED) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"inflate: data not deflated!");
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
- if (data[0] != deflate_magic[0] ||
- data[1] != deflate_magic[1] ||
- (zlib_flags & RESERVED) != 0) {
+ if (ctx->header[0] != deflate_magic[0] ||
+ ctx->header[1] != deflate_magic[1]) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"inflate: bad header");
return APR_EGENERAL ;
}
- data += 10 ;
- len -= 10 ;
- }
- if (zlib_flags & EXTRA_FIELD) {
- unsigned int bytes = (unsigned int)(data[0]);
- bytes += ((unsigned int)(data[1])) << 8;
- bytes += 2;
- if (len < bytes) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "inflate: extra field too big (not "
- "supported)");
- return APR_EGENERAL;
- }
- data += bytes;
- len -= bytes;
- }
- if (zlib_flags & ORIG_NAME) {
- while (len-- && *data++);
- }
- if (zlib_flags & COMMENT) {
- while (len-- && *data++);
- }
- if (zlib_flags & HEAD_CRC) {
- len -= 2;
- data += 2;
- }
+ ctx->zlib_flags = ctx->header[3];
+ if ((ctx->zlib_flags & RESERVED)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "inflate: bad flags %02x",
+ ctx->zlib_flags);
+ return APR_EGENERAL;
+ }
+ }
+ if (len == rem) {
+ apr_bucket_delete(e);
+ continue;
+ }
+ data += rem;
+ len -= rem;
+ }
+
+ if (ctx->zlib_flags) {
+ rv = consume_zlib_flags(ctx, &data, &len);
+ if (rv == APR_SUCCESS) {
+ ctx->zlib_flags = 0;
+ }
+ if (!len) {
+ apr_bucket_delete(e);
+ continue;
+ }
}
/* pass through zlib inflate. */
@@ -1458,6 +1693,9 @@ static apr_status_t inflate_out_filter(a
}
if (zRC != Z_OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "Zlib error %d inflating data (%s)", zRC,
+ ctx->stream.msg);
return APR_EGENERAL;
}
}