summaryrefslogtreecommitdiff
path: root/www/apache22/files/patch-CVE-2015-3183
diff options
context:
space:
mode:
Diffstat (limited to 'www/apache22/files/patch-CVE-2015-3183')
-rw-r--r--www/apache22/files/patch-CVE-2015-3183777
1 files changed, 0 insertions, 777 deletions
diff --git a/www/apache22/files/patch-CVE-2015-3183 b/www/apache22/files/patch-CVE-2015-3183
deleted file mode 100644
index 899592db1643..000000000000
--- a/www/apache22/files/patch-CVE-2015-3183
+++ /dev/null
@@ -1,777 +0,0 @@
-diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
-index 347df85..5e190cb 100644
---- modules/http/http_filters.c
-+++ modules/http/http_filters.c
-@@ -56,27 +56,31 @@
- #include <unistd.h>
- #endif
-
--#define INVALID_CHAR -2
--
--static long get_chunk_size(char *);
--
--typedef struct http_filter_ctx {
-+typedef struct http_filter_ctx
-+{
- apr_off_t remaining;
- apr_off_t limit;
- apr_off_t limit_used;
-- enum {
-- BODY_NONE,
-- BODY_LENGTH,
-- BODY_CHUNK,
-- BODY_CHUNK_PART
-+ apr_int32_t chunk_used;
-+ apr_int32_t chunkbits;
-+ enum
-+ {
-+ BODY_NONE, /* streamed data */
-+ BODY_LENGTH, /* data constrained by content length */
-+ BODY_CHUNK, /* chunk expected */
-+ BODY_CHUNK_PART, /* chunk digits */
-+ BODY_CHUNK_EXT, /* chunk extension */
-+ BODY_CHUNK_LF, /* got CR, expect LF after digits/extension */
-+ BODY_CHUNK_DATA, /* data constrained by chunked encoding */
-+ BODY_CHUNK_END, /* chunked data terminating CRLF */
-+ BODY_CHUNK_END_LF, /* got CR, expect LF after data */
-+ BODY_CHUNK_TRAILER /* trailers */
- } state;
-- int eos_sent;
-- char chunk_ln[32];
-- char *pos;
-- apr_off_t linesize;
-+ unsigned int eos_sent :1;
- apr_bucket_brigade *bb;
- } http_ctx_t;
-
-+/* bail out if some error in the HTTP input filter happens */
- static apr_status_t bail_out_on_error(http_ctx_t *ctx,
- ap_filter_t *f,
- int http_error)
-@@ -109,119 +113,147 @@ static apr_status_t bail_out_on_error(http_ctx_t *ctx,
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
-+ /* If chunked encoding / content-length are corrupt, we may treat parts
-+ * of this request's body as the next one's headers.
-+ * To be safe, disable keep-alive.
-+ */
-+ f->r->connection->keepalive = AP_CONN_CLOSE;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
-
--static apr_status_t get_remaining_chunk_line(http_ctx_t *ctx,
-- apr_bucket_brigade *b,
-- int linelimit)
-+/**
-+ * Parse a chunk line with optional extension, detect overflow.
-+ * There are two error cases:
-+ * 1) If the conversion would require too many bits, APR_EGENERAL is returned.
-+ * 2) If the conversion used the correct number of bits, but an overflow
-+ * caused only the sign bit to flip, then APR_ENOSPC is returned.
-+ * In general, any negative number can be considered an overflow error.
-+ */
-+static apr_status_t parse_chunk_size(http_ctx_t *ctx, const char *buffer,
-+ apr_size_t len, int linelimit)
- {
-- apr_status_t rv;
-- apr_off_t brigade_length;
-- apr_bucket *e;
-- const char *lineend;
-- apr_size_t len;
-+ apr_size_t i = 0;
-
-- /*
-- * As the brigade b should have been requested in mode AP_MODE_GETLINE
-- * all buckets in this brigade are already some type of memory
-- * buckets (due to the needed scanning for LF in mode AP_MODE_GETLINE)
-- * or META buckets.
-- */
-- rv = apr_brigade_length(b, 0, &brigade_length);
-- if (rv != APR_SUCCESS) {
-- return rv;
-- }
-- /* Sanity check. Should never happen. See above. */
-- if (brigade_length == -1) {
-- return APR_EGENERAL;
-- }
-- if (!brigade_length) {
-- return APR_EAGAIN;
-- }
-- ctx->linesize += brigade_length;
-- if (ctx->linesize > linelimit) {
-- return APR_ENOSPC;
-- }
-- /*
-- * As all buckets are already some type of memory buckets or META buckets
-- * (see above), we only need to check the last byte in the last data bucket.
-- */
-- for (e = APR_BRIGADE_LAST(b);
-- e != APR_BRIGADE_SENTINEL(b);
-- e = APR_BUCKET_PREV(e)) {
-+ while (i < len) {
-+ char c = buffer[i];
-
-- if (APR_BUCKET_IS_METADATA(e)) {
-+ ap_xlate_proto_from_ascii(&c, 1);
-+
-+ /* handle CRLF after the chunk */
-+ if (ctx->state == BODY_CHUNK_END
-+ || ctx->state == BODY_CHUNK_END_LF) {
-+ if (c == LF) {
-+ ctx->state = BODY_CHUNK;
-+ }
-+ else if (c == CR && ctx->state == BODY_CHUNK_END) {
-+ ctx->state = BODY_CHUNK_END_LF;
-+ }
-+ else {
-+ /*
-+ * LF expected.
-+ */
-+ return APR_EINVAL;
-+ }
-+ i++;
- continue;
- }
-- rv = apr_bucket_read(e, &lineend, &len, APR_BLOCK_READ);
-- if (rv != APR_SUCCESS) {
-- return rv;
-+
-+ /* handle start of the chunk */
-+ if (ctx->state == BODY_CHUNK) {
-+ if (!apr_isxdigit(c)) {
-+ /*
-+ * Detect invalid character at beginning. This also works for
-+ * empty chunk size lines.
-+ */
-+ return APR_EINVAL;
-+ }
-+ else {
-+ ctx->state = BODY_CHUNK_PART;
-+ }
-+ ctx->remaining = 0;
-+ ctx->chunkbits = sizeof(apr_off_t) * 8;
-+ ctx->chunk_used = 0;
-+ }
-+
-+ if (c == LF) {
-+ if (ctx->remaining) {
-+ ctx->state = BODY_CHUNK_DATA;
-+ }
-+ else {
-+ ctx->state = BODY_CHUNK_TRAILER;
-+ }
- }
-- if (len > 0) {
-- break; /* we got the data we want */
-+ else if (ctx->state == BODY_CHUNK_LF) {
-+ /*
-+ * LF expected.
-+ */
-+ return APR_EINVAL;
- }
-- /* If we got a zero-length data bucket, we try the next one */
-- }
-- /* We had no data in this brigade */
-- if (!len || e == APR_BRIGADE_SENTINEL(b)) {
-- return APR_EAGAIN;
-- }
-- if (lineend[len - 1] != APR_ASCII_LF) {
-- return APR_EAGAIN;
-- }
-- /* Line is complete. So reset ctx->linesize for next round. */
-- ctx->linesize = 0;
-- return APR_SUCCESS;
--}
-+ else if (c == CR) {
-+ ctx->state = BODY_CHUNK_LF;
-+ }
-+ else if (c == ';') {
-+ ctx->state = BODY_CHUNK_EXT;
-+ }
-+ else if (ctx->state == BODY_CHUNK_EXT) {
-+ /*
-+ * Control chars (but tabs) are invalid.
-+ */
-+ if (c != '\t' && apr_iscntrl(c)) {
-+ return APR_EINVAL;
-+ }
-+ }
-+ else if (ctx->state == BODY_CHUNK_PART) {
-+ int xvalue;
-
--static apr_status_t get_chunk_line(http_ctx_t *ctx, apr_bucket_brigade *b,
-- int linelimit)
--{
-- apr_size_t len;
-- int tmp_len;
-- apr_status_t rv;
-+ /* ignore leading zeros */
-+ if (!ctx->remaining && c == '0') {
-+ i++;
-+ continue;
-+ }
-
-- tmp_len = sizeof(ctx->chunk_ln) - (ctx->pos - ctx->chunk_ln) - 1;
-- /* Saveguard ourselves against underflows */
-- if (tmp_len < 0) {
-- len = 0;
-- }
-- else {
-- len = (apr_size_t) tmp_len;
-- }
-- /*
-- * Check if there is space left in ctx->chunk_ln. If not, then either
-- * the chunk size is insane or we have chunk-extensions. Ignore both
-- * by discarding the remaining part of the line via
-- * get_remaining_chunk_line. Only bail out if the line is too long.
-- */
-- if (len > 0) {
-- rv = apr_brigade_flatten(b, ctx->pos, &len);
-- if (rv != APR_SUCCESS) {
-- return rv;
-+ ctx->chunkbits -= 4;
-+ if (ctx->chunkbits < 0) {
-+ /* overflow */
-+ return APR_ENOSPC;
-+ }
-+
-+ if (c >= '0' && c <= '9') {
-+ xvalue = c - '0';
-+ }
-+ else if (c >= 'A' && c <= 'F') {
-+ xvalue = c - 'A' + 0xa;
-+ }
-+ else if (c >= 'a' && c <= 'f') {
-+ xvalue = c - 'a' + 0xa;
-+ }
-+ else {
-+ /* bogus character */
-+ return APR_EINVAL;
-+ }
-+
-+ ctx->remaining = (ctx->remaining << 4) | xvalue;
-+ if (ctx->remaining < 0) {
-+ /* overflow */
-+ return APR_ENOSPC;
-+ }
- }
-- ctx->pos += len;
-- ctx->linesize += len;
-- *(ctx->pos) = '\0';
-- /*
-- * Check if we really got a full line. If yes the
-- * last char in the just read buffer must be LF.
-- * If not advance the buffer and return APR_EAGAIN.
-- * We do not start processing until we have the
-- * full line.
-- */
-- if (ctx->pos[-1] != APR_ASCII_LF) {
-- /* Check if the remaining data in the brigade has the LF */
-- return get_remaining_chunk_line(ctx, b, linelimit);
-+ else {
-+ /* Should not happen */
-+ return APR_EGENERAL;
- }
-- /* Line is complete. So reset ctx->pos for next round. */
-- ctx->pos = ctx->chunk_ln;
-- return APR_SUCCESS;
-+
-+ i++;
- }
-- return get_remaining_chunk_line(ctx, b, linelimit);
--}
-
-+ /* sanity check */
-+ ctx->chunk_used += len;
-+ if (ctx->chunk_used < 0 || ctx->chunk_used > linelimit) {
-+ return APR_ENOSPC;
-+ }
-+
-+ return APR_SUCCESS;
-+}
-
- static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f,
- apr_bucket_brigade *b, int merge)
-@@ -235,7 +267,6 @@ static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f,
- r->status = HTTP_OK;
- r->headers_in = r->trailers_in;
- apr_table_clear(r->headers_in);
-- ctx->state = BODY_NONE;
- ap_get_mime_headers(r);
-
- if(r->status == HTTP_OK) {
-@@ -282,6 +313,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
- apr_off_t totalread;
- int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE;
- apr_bucket_brigade *bb;
-+ int again;
-
- conf = (core_server_config *)
- ap_get_module_config(f->r->server->module_config, &core_module);
-@@ -295,7 +327,6 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
- const char *tenc, *lenp;
- f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
- ctx->state = BODY_NONE;
-- ctx->pos = ctx->chunk_ln;
- ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- bb = ctx->bb;
-
-@@ -337,7 +368,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
- */
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
- "Unknown Transfer-Encoding: %s", tenc);
-- return bail_out_on_error(ctx, f, HTTP_NOT_IMPLEMENTED);
-+ return bail_out_on_error(ctx, f, HTTP_BAD_REQUEST);
- }
- lenp = NULL;
- }
-@@ -357,7 +388,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
- "Invalid Content-Length");
-
-- return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
-+ return bail_out_on_error(ctx, f, HTTP_BAD_REQUEST);
- }
-
- /* If we have a limit in effect and we know the C-L ahead of
-@@ -399,7 +430,8 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
- if (!ap_is_HTTP_SUCCESS(f->r->status)) {
- ctx->state = BODY_NONE;
- ctx->eos_sent = 1;
-- } else {
-+ }
-+ else {
- char *tmp;
- int len;
-
-@@ -424,285 +456,194 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
- }
- }
- }
-+ }
-
-- /* We can't read the chunk until after sending 100 if required. */
-- if (ctx->state == BODY_CHUNK) {
-- apr_brigade_cleanup(bb);
-+ /* sanity check in case we're read twice */
-+ if (ctx->eos_sent) {
-+ e = apr_bucket_eos_create(f->c->bucket_alloc);
-+ APR_BRIGADE_INSERT_TAIL(b, e);
-+ return APR_SUCCESS;
-+ }
-+
-+ do {
-+ apr_brigade_cleanup(b);
-+ again = 0; /* until further notice */
-+
-+ /* read and handle the brigade */
-+ switch (ctx->state) {
-+ case BODY_CHUNK:
-+ case BODY_CHUNK_PART:
-+ case BODY_CHUNK_EXT:
-+ case BODY_CHUNK_LF:
-+ case BODY_CHUNK_END:
-+ case BODY_CHUNK_END_LF: {
-
-- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
-- block, 0);
-+ rv = ap_get_brigade(f->next, b, AP_MODE_GETLINE, block, 0);
-
- /* for timeout */
-- if (block == APR_NONBLOCK_READ &&
-- ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) ||
-- (APR_STATUS_IS_EAGAIN(rv)) )) {
-- ctx->state = BODY_CHUNK_PART;
-+ if (block == APR_NONBLOCK_READ
-+ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
-+ || (APR_STATUS_IS_EAGAIN(rv)))) {
- return APR_EAGAIN;
- }
-
-- if (rv == APR_SUCCESS) {
-- rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line);
-- if (APR_STATUS_IS_EAGAIN(rv)) {
-- apr_brigade_cleanup(bb);
-- ctx->state = BODY_CHUNK_PART;
-- return rv;
-- }
-- if (rv == APR_SUCCESS) {
-- ctx->remaining = get_chunk_size(ctx->chunk_ln);
-- if (ctx->remaining == INVALID_CHAR) {
-- rv = APR_EGENERAL;
-- http_error = HTTP_SERVICE_UNAVAILABLE;
-- }
-- }
-- }
-- apr_brigade_cleanup(bb);
--
-- /* Detect chunksize error (such as overflow) */
-- if (rv != APR_SUCCESS || ctx->remaining < 0) {
-- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "Error reading first chunk %s ",
-- (ctx->remaining < 0) ? "(overflow)" : "");
-- if (APR_STATUS_IS_TIMEUP(rv) || ctx->remaining > 0) {
-- http_error = HTTP_REQUEST_TIME_OUT;
-- }
-- ctx->remaining = 0; /* Reset it in case we have to
-- * come back here later */
-- return bail_out_on_error(ctx, f, http_error);
-+ if (rv == APR_EOF) {
-+ return APR_INCOMPLETE;
- }
-
-- if (!ctx->remaining) {
-- return read_chunked_trailers(ctx, f, b,
-- conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE);
-+ if (rv != APR_SUCCESS) {
-+ return rv;
- }
-- }
-- }
-- else {
-- bb = ctx->bb;
-- }
-
-- if (ctx->eos_sent) {
-- e = apr_bucket_eos_create(f->c->bucket_alloc);
-- APR_BRIGADE_INSERT_TAIL(b, e);
-- return APR_SUCCESS;
-- }
-+ e = APR_BRIGADE_FIRST(b);
-+ while (e != APR_BRIGADE_SENTINEL(b)) {
-+ const char *buffer;
-+ apr_size_t len;
-
-- if (!ctx->remaining) {
-- switch (ctx->state) {
-- case BODY_NONE:
-- break;
-- case BODY_LENGTH:
-- e = apr_bucket_eos_create(f->c->bucket_alloc);
-- APR_BRIGADE_INSERT_TAIL(b, e);
-- ctx->eos_sent = 1;
-- return APR_SUCCESS;
-- case BODY_CHUNK:
-- case BODY_CHUNK_PART:
-- {
-- apr_brigade_cleanup(bb);
-+ if (!APR_BUCKET_IS_METADATA(e)) {
-+ int parsing = 0;
-
-- /* We need to read the CRLF after the chunk. */
-- if (ctx->state == BODY_CHUNK) {
-- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
-- block, 0);
-- if (block == APR_NONBLOCK_READ &&
-- ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) ||
-- (APR_STATUS_IS_EAGAIN(rv)) )) {
-- return APR_EAGAIN;
-- }
-- /* If we get an error, then leave */
-- if (rv == APR_EOF) {
-- return APR_INCOMPLETE;
-- }
-- if (rv != APR_SUCCESS) {
-- return rv;
-- }
-- /*
-- * We really don't care whats on this line. If it is RFC
-- * compliant it should be only \r\n. If there is more
-- * before we just ignore it as long as we do not get over
-- * the limit for request lines.
-- */
-- rv = get_remaining_chunk_line(ctx, bb,
-- f->r->server->limit_req_line);
-- apr_brigade_cleanup(bb);
-- if (APR_STATUS_IS_EAGAIN(rv)) {
-- return rv;
-- }
-- } else {
-- rv = APR_SUCCESS;
-- }
-+ rv = apr_bucket_read(e, &buffer, &len, APR_BLOCK_READ);
-
-- if (rv == APR_SUCCESS) {
-- /* Read the real chunk line. */
-- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
-- block, 0);
-- /* Test timeout */
-- if (block == APR_NONBLOCK_READ &&
-- ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) ||
-- (APR_STATUS_IS_EAGAIN(rv)) )) {
-- ctx->state = BODY_CHUNK_PART;
-- return APR_EAGAIN;
-- }
-- ctx->state = BODY_CHUNK;
- if (rv == APR_SUCCESS) {
-- rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line);
-- if (APR_STATUS_IS_EAGAIN(rv)) {
-- ctx->state = BODY_CHUNK_PART;
-- apr_brigade_cleanup(bb);
-- return rv;
-- }
-- if (rv == APR_SUCCESS) {
-- ctx->remaining = get_chunk_size(ctx->chunk_ln);
-- if (ctx->remaining == INVALID_CHAR) {
-- rv = APR_EGENERAL;
-- http_error = HTTP_SERVICE_UNAVAILABLE;
-+ parsing = 1;
-+ rv = parse_chunk_size(ctx, buffer, len,
-+ f->r->server->limit_req_fieldsize);
-+ }
-+ if (rv != APR_SUCCESS) {
-+ ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r,
-+ "Error reading/parsing chunk %s ",
-+ (APR_ENOSPC == rv) ? "(overflow)" : "");
-+ if (parsing) {
-+ if (rv != APR_ENOSPC) {
-+ http_error = HTTP_BAD_REQUEST;
- }
-+ return bail_out_on_error(ctx, f, http_error);
- }
-+ return rv;
- }
-- apr_brigade_cleanup(bb);
- }
-
-- /* Detect chunksize error (such as overflow) */
-- if (rv != APR_SUCCESS || ctx->remaining < 0) {
-- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "Error reading chunk %s ",
-- (ctx->remaining < 0) ? "(overflow)" : "");
-- if (APR_STATUS_IS_TIMEUP(rv) || ctx->remaining > 0) {
-- http_error = HTTP_REQUEST_TIME_OUT;
-- }
-- ctx->remaining = 0; /* Reset it in case we have to
-- * come back here later */
-- return bail_out_on_error(ctx, f, http_error);
-- }
-+ apr_bucket_delete(e);
-+ e = APR_BRIGADE_FIRST(b);
-+ }
-+ again = 1; /* come around again */
-
-- if (!ctx->remaining) {
-- return read_chunked_trailers(ctx, f, b,
-+ if (ctx->state == BODY_CHUNK_TRAILER) {
-+ /* Treat UNSET as DISABLE - trailers aren't merged by default */
-+ return read_chunked_trailers(ctx, f, b,
- conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE);
-- }
- }
-+
- break;
- }
-- }
-+ case BODY_NONE:
-+ case BODY_LENGTH:
-+ case BODY_CHUNK_DATA: {
-
-- /* Ensure that the caller can not go over our boundary point. */
-- if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK) {
-- if (ctx->remaining < readbytes) {
-- readbytes = ctx->remaining;
-- }
-- AP_DEBUG_ASSERT(readbytes > 0);
-- }
-+ /* Ensure that the caller can not go over our boundary point. */
-+ if (ctx->state != BODY_NONE && ctx->remaining < readbytes) {
-+ readbytes = ctx->remaining;
-+ }
-+ if (readbytes > 0) {
-
-- rv = ap_get_brigade(f->next, b, mode, block, readbytes);
-+ rv = ap_get_brigade(f->next, b, mode, block, readbytes);
-
-- if (rv == APR_EOF && ctx->state != BODY_NONE &&
-- ctx->remaining > 0) {
-- return APR_INCOMPLETE;
-- }
-- if (rv != APR_SUCCESS) {
-- return rv;
-- }
-+ /* for timeout */
-+ if (block == APR_NONBLOCK_READ
-+ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
-+ || (APR_STATUS_IS_EAGAIN(rv)))) {
-+ return APR_EAGAIN;
-+ }
-
-- /* How many bytes did we just read? */
-- apr_brigade_length(b, 0, &totalread);
-+ if (rv == APR_EOF && ctx->state != BODY_NONE
-+ && ctx->remaining > 0) {
-+ return APR_INCOMPLETE;
-+ }
-
-- /* If this happens, we have a bucket of unknown length. Die because
-- * it means our assumptions have changed. */
-- AP_DEBUG_ASSERT(totalread >= 0);
-+ if (rv != APR_SUCCESS) {
-+ return rv;
-+ }
-
-- if (ctx->state != BODY_NONE) {
-- ctx->remaining -= totalread;
-- if (ctx->remaining > 0) {
-- e = APR_BRIGADE_LAST(b);
-- if (APR_BUCKET_IS_EOS(e)) {
-- apr_bucket_delete(e);
-- return APR_INCOMPLETE;
-- }
-- }
-- }
-+ /* How many bytes did we just read? */
-+ apr_brigade_length(b, 0, &totalread);
-
-- /* If we have no more bytes remaining on a C-L request,
-- * save the callter a roundtrip to discover EOS.
-- */
-- if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
-- e = apr_bucket_eos_create(f->c->bucket_alloc);
-- APR_BRIGADE_INSERT_TAIL(b, e);
-- }
-+ /* If this happens, we have a bucket of unknown length. Die because
-+ * it means our assumptions have changed. */
-+ AP_DEBUG_ASSERT(totalread >= 0);
-
-- /* We have a limit in effect. */
-- if (ctx->limit) {
-- /* FIXME: Note that we might get slightly confused on chunked inputs
-- * as we'd need to compensate for the chunk lengths which may not
-- * really count. This seems to be up for interpretation. */
-- ctx->limit_used += totalread;
-- if (ctx->limit < ctx->limit_used) {
-- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
-- "Read content-length of %" APR_OFF_T_FMT
-- " is larger than the configured limit"
-- " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
-- apr_brigade_cleanup(bb);
-- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
-- f->r->pool,
-- f->c->bucket_alloc);
-- APR_BRIGADE_INSERT_TAIL(bb, e);
-- e = apr_bucket_eos_create(f->c->bucket_alloc);
-- APR_BRIGADE_INSERT_TAIL(bb, e);
-- ctx->eos_sent = 1;
-- return ap_pass_brigade(f->r->output_filters, bb);
-- }
-- }
-+ if (ctx->state != BODY_NONE) {
-+ ctx->remaining -= totalread;
-+ if (ctx->remaining > 0) {
-+ e = APR_BRIGADE_LAST(b);
-+ if (APR_BUCKET_IS_EOS(e)) {
-+ apr_bucket_delete(e);
-+ return APR_INCOMPLETE;
-+ }
-+ }
-+ else if (ctx->state == BODY_CHUNK_DATA) {
-+ /* next chunk please */
-+ ctx->state = BODY_CHUNK_END;
-+ ctx->chunk_used = 0;
-+ }
-+ }
-
-- return APR_SUCCESS;
--}
-+ }
-
--/**
-- * Parse a chunk extension, detect overflow.
-- * There are two error cases:
-- * 1) If the conversion would require too many bits, a -1 is returned.
-- * 2) If the conversion used the correct number of bits, but an overflow
-- * caused only the sign bit to flip, then that negative number is
-- * returned.
-- * In general, any negative number can be considered an overflow error.
-- */
--static long get_chunk_size(char *b)
--{
-- long chunksize = 0;
-- size_t chunkbits = sizeof(long) * 8;
-+ /* If we have no more bytes remaining on a C-L request,
-+ * save the caller a round trip to discover EOS.
-+ */
-+ if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
-+ e = apr_bucket_eos_create(f->c->bucket_alloc);
-+ APR_BRIGADE_INSERT_TAIL(b, e);
-+ ctx->eos_sent = 1;
-+ }
-
-- ap_xlate_proto_from_ascii(b, strlen(b));
-+ /* We have a limit in effect. */
-+ if (ctx->limit) {
-+ /* FIXME: Note that we might get slightly confused on chunked inputs
-+ * as we'd need to compensate for the chunk lengths which may not
-+ * really count. This seems to be up for interpretation. */
-+ ctx->limit_used += totalread;
-+ if (ctx->limit < ctx->limit_used) {
-+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
-+ "Read content-length of %" APR_OFF_T_FMT
-+ " is larger than the configured limit"
-+ " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
-+ return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
-+ }
-+ }
-
-- if (!apr_isxdigit(*b)) {
-- /*
-- * Detect invalid character at beginning. This also works for empty
-- * chunk size lines.
-- */
-- return INVALID_CHAR;
-- }
-- /* Skip leading zeros */
-- while (*b == '0') {
-- ++b;
-- }
-+ break;
-+ }
-+ case BODY_CHUNK_TRAILER: {
-+
-+ rv = ap_get_brigade(f->next, b, mode, block, readbytes);
-
-- while (apr_isxdigit(*b) && (chunkbits > 0)) {
-- int xvalue = 0;
-+ /* for timeout */
-+ if (block == APR_NONBLOCK_READ
-+ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
-+ || (APR_STATUS_IS_EAGAIN(rv)))) {
-+ return APR_EAGAIN;
-+ }
-+
-+ if (rv != APR_SUCCESS) {
-+ return rv;
-+ }
-
-- if (*b >= '0' && *b <= '9') {
-- xvalue = *b - '0';
-+ break;
- }
-- else if (*b >= 'A' && *b <= 'F') {
-- xvalue = *b - 'A' + 0xa;
-+ default: {
-+ /* Should not happen */
-+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
-+ "Unexpected body state (%i)", (int)ctx->state);
-+ return APR_EGENERAL;
- }
-- else if (*b >= 'a' && *b <= 'f') {
-- xvalue = *b - 'a' + 0xa;
- }
-
-- chunksize = (chunksize << 4) | xvalue;
-- chunkbits -= 4;
-- ++b;
-- }
-- if (apr_isxdigit(*b) && (chunkbits <= 0)) {
-- /* overflow */
-- return -1;
-- }
-+ } while (again);
-
-- return chunksize;
-+ return APR_SUCCESS;
- }
-
- typedef struct header_struct {