diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-25 04:41:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-25 04:41:28 +0000 |
commit | b1a1c1d95059e2fefd7b5671eb110ab690409a84 (patch) | |
tree | 97ecfcc9425e2d09d2cd669594d626a616f324a3 /modules/filters | |
parent | Releasing progress-linux version 2.4.38-3+deb10u10progress5u1. (diff) | |
download | apache2-b1a1c1d95059e2fefd7b5671eb110ab690409a84.tar.xz apache2-b1a1c1d95059e2fefd7b5671eb110ab690409a84.zip |
Merging upstream version 2.4.59.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/filters')
-rw-r--r-- | modules/filters/libsed.h | 12 | ||||
-rw-r--r-- | modules/filters/mod_brotli.c | 15 | ||||
-rw-r--r-- | modules/filters/mod_charset_lite.c | 4 | ||||
-rw-r--r-- | modules/filters/mod_data.c | 4 | ||||
-rw-r--r-- | modules/filters/mod_deflate.c | 349 | ||||
-rw-r--r-- | modules/filters/mod_ext_filter.c | 6 | ||||
-rw-r--r-- | modules/filters/mod_include.c | 41 | ||||
-rw-r--r-- | modules/filters/mod_proxy_html.c | 31 | ||||
-rw-r--r-- | modules/filters/mod_reflector.c | 11 | ||||
-rw-r--r-- | modules/filters/mod_reqtimeout.c | 237 | ||||
-rw-r--r-- | modules/filters/mod_request.c | 28 | ||||
-rw-r--r-- | modules/filters/mod_sed.c | 116 | ||||
-rw-r--r-- | modules/filters/mod_substitute.c | 9 | ||||
-rw-r--r-- | modules/filters/mod_xml2enc.c | 47 | ||||
-rw-r--r-- | modules/filters/regexp.h | 4 | ||||
-rw-r--r-- | modules/filters/sed1.c | 207 |
16 files changed, 656 insertions, 465 deletions
diff --git a/modules/filters/libsed.h b/modules/filters/libsed.h index 76cbc0c..0256b1e 100644 --- a/modules/filters/libsed.h +++ b/modules/filters/libsed.h @@ -60,7 +60,7 @@ struct sed_label_s { }; typedef apr_status_t (sed_err_fn_t)(void *data, const char *error); -typedef apr_status_t (sed_write_fn_t)(void *ctx, char *buf, int sz); +typedef apr_status_t (sed_write_fn_t)(void *ctx, char *buf, apr_size_t sz); typedef struct sed_commands_s sed_commands_t; #define NWFILES 11 /* 10 plus one for standard output */ @@ -69,7 +69,7 @@ struct sed_commands_s { sed_err_fn_t *errfn; void *data; - unsigned lsize; + apr_size_t lsize; char *linebuf; char *lbend; const char *saveq; @@ -116,15 +116,15 @@ struct sed_eval_s { apr_int64_t lnum; void *fout; - unsigned lsize; + apr_size_t lsize; char *linebuf; char *lspend; - unsigned hsize; + apr_size_t hsize; char *holdbuf; char *hspend; - unsigned gsize; + apr_size_t gsize; char *genbuf; char *lcomend; @@ -160,7 +160,7 @@ apr_status_t sed_init_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data, sed_write_fn_t *writefn, apr_pool_t *p); apr_status_t sed_reset_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data); -apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void *fout); +apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz, void *fout); apr_status_t sed_eval_file(sed_eval_t *eval, apr_file_t *fin, void *fout); apr_status_t sed_finalize_eval(sed_eval_t *eval, void *f); void sed_destroy_eval(sed_eval_t *eval); diff --git a/modules/filters/mod_brotli.c b/modules/filters/mod_brotli.c index 56717e7..0f7d770 100644 --- a/modules/filters/mod_brotli.c +++ b/modules/filters/mod_brotli.c @@ -344,6 +344,7 @@ static apr_status_t compress_filter(ap_filter_t *f, apr_bucket_brigade *bb) const char *encoding; const char *token; const char *accepts; + const char *q = NULL; /* Only work on main request, not subrequests, that are not * a 204 response with no content, and are not tagged with the @@ -411,7 +412,19 @@ static apr_status_t compress_filter(ap_filter_t *f, apr_bucket_brigade *bb) token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL; } - if (!token || token[0] == '\0') { + /* Find the qvalue, if provided */ + if (*accepts) { + while (*accepts == ';') { + ++accepts; + } + q = ap_get_token(r->pool, &accepts, 1); + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "token: '%s' - q: '%s'", token ? token : "NULL", q); + } + + /* No acceptable token found or q=0 */ + if (!token || token[0] == '\0' || + (q && strlen(q) >= 3 && strncmp("q=0.000", q, strlen(q)) == 0)) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } diff --git a/modules/filters/mod_charset_lite.c b/modules/filters/mod_charset_lite.c index ed76f61..e3d1ce9 100644 --- a/modules/filters/mod_charset_lite.c +++ b/modules/filters/mod_charset_lite.c @@ -790,7 +790,7 @@ static apr_status_t xlate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) if (!ctx->noop && ctx->xlate == NULL) { const char *mime_type = f->r->content_type; - if (mime_type && (strncasecmp(mime_type, "text/", 5) == 0 || + if (mime_type && (ap_cstr_casecmpn(mime_type, "text/", 5) == 0 || #if APR_CHARSET_EBCDIC /* On an EBCDIC machine, be willing to translate mod_autoindex- * generated output. Otherwise, it doesn't look too cool. @@ -806,7 +806,7 @@ static apr_status_t xlate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) */ strcmp(mime_type, DIR_MAGIC_TYPE) == 0 || #endif - strncasecmp(mime_type, "message/", 8) == 0 || + ap_cstr_casecmpn(mime_type, "message/", 8) == 0 || dc->force_xlate == FX_FORCE)) { rv = apr_xlate_open(&ctx->xlate, diff --git a/modules/filters/mod_data.c b/modules/filters/mod_data.c index d083d32..ddadd1b 100644 --- a/modules/filters/mod_data.c +++ b/modules/filters/mod_data.c @@ -107,8 +107,8 @@ static apr_status_t data_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) if (content_length) { apr_off_t len, clen; apr_brigade_length(ctx->bb, 1, &len); - clen = apr_atoi64(content_length); - if (clen >= 0 && clen < APR_INT32_MAX) { + if (ap_parse_strict_length(&clen, content_length) + && clen < APR_INT32_MAX) { ap_set_content_length(r, len + apr_base64_encode_len((int)clen) - 1); } diff --git a/modules/filters/mod_deflate.c b/modules/filters/mod_deflate.c index d60b2de..5a541e7 100644 --- a/modules/filters/mod_deflate.c +++ b/modules/filters/mod_deflate.c @@ -43,10 +43,11 @@ #include "apr_general.h" #include "util_filter.h" #include "apr_buckets.h" +#include "http_protocol.h" #include "http_request.h" +#include "http_ssl.h" #define APR_WANT_STRFUNC #include "apr_want.h" -#include "mod_ssl.h" #include "zlib.h" @@ -56,15 +57,20 @@ module AP_MODULE_DECLARE_DATA deflate_module; #define AP_INFLATE_RATIO_LIMIT 200 #define AP_INFLATE_RATIO_BURST 3 +#define AP_DEFLATE_ETAG_ADDSUFFIX 0 +#define AP_DEFLATE_ETAG_NOCHANGE 1 +#define AP_DEFLATE_ETAG_REMOVE 2 + typedef struct deflate_filter_config_t { int windowSize; int memlevel; int compressionlevel; - apr_size_t bufferSize; + int bufferSize; const char *note_ratio_name; const char *note_input_name; const char *note_output_name; + int etag_opt; } deflate_filter_config; typedef struct deflate_dirconf_t { @@ -94,8 +100,6 @@ static const char deflate_magic[2] = { '\037', '\213' }; #define DEFAULT_MEMLEVEL 9 #define DEFAULT_BUFFERSIZE 8096 -static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *mod_deflate_ssl_var = NULL; - /* Check whether a request is gzipped, so we can un-gzip it. * If a request has multiple encodings, we need the gzip * to be the outermost non-identity encoding. @@ -118,8 +122,8 @@ static int check_gzip(request_rec *r, apr_table_t *hdrs1, apr_table_t *hdrs2) if (encoding && *encoding) { /* check the usual/simple case first */ - if (!strcasecmp(encoding, "gzip") - || !strcasecmp(encoding, "x-gzip")) { + if (!ap_cstr_casecmp(encoding, "gzip") + || !ap_cstr_casecmp(encoding, "x-gzip")) { found = 1; if (hdrs) { apr_table_unset(hdrs, "Content-Encoding"); @@ -137,8 +141,8 @@ static int check_gzip(request_rec *r, apr_table_t *hdrs1, apr_table_t *hdrs2) for(;;) { char *token = ap_strrchr(new_encoding, ','); if (!token) { /* gzip:identity or other:identity */ - if (!strcasecmp(new_encoding, "gzip") - || !strcasecmp(new_encoding, "x-gzip")) { + if (!ap_cstr_casecmp(new_encoding, "gzip") + || !ap_cstr_casecmp(new_encoding, "x-gzip")) { found = 1; if (hdrs) { apr_table_unset(hdrs, "Content-Encoding"); @@ -150,8 +154,8 @@ static int check_gzip(request_rec *r, apr_table_t *hdrs1, apr_table_t *hdrs2) break; /* seen all tokens */ } for (ptr=token+1; apr_isspace(*ptr); ++ptr); - if (!strcasecmp(ptr, "gzip") - || !strcasecmp(ptr, "x-gzip")) { + if (!ap_cstr_casecmp(ptr, "gzip") + || !ap_cstr_casecmp(ptr, "x-gzip")) { *token = '\0'; if (hdrs) { apr_table_setn(hdrs, "Content-Encoding", new_encoding); @@ -161,7 +165,7 @@ static int check_gzip(request_rec *r, apr_table_t *hdrs1, apr_table_t *hdrs2) } found = 1; } - else if (!ptr[0] || !strcasecmp(ptr, "identity")) { + else if (!ptr[0] || !ap_cstr_casecmp(ptr, "identity")) { *token = '\0'; continue; /* strip the token and find the next one */ } @@ -250,7 +254,7 @@ static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy, return "DeflateBufferSize should be positive"; } - c->bufferSize = (apr_size_t)n; + c->bufferSize = n; return NULL; } @@ -296,6 +300,29 @@ static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy, return NULL; } +static const char *deflate_set_etag(cmd_parms *cmd, void *dummy, + const char *arg) +{ + deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, + &deflate_module); + + if (!strcasecmp(arg, "NoChange")) { + c->etag_opt = AP_DEFLATE_ETAG_NOCHANGE; + } + else if (!strcasecmp(arg, "AddSuffix")) { + c->etag_opt = AP_DEFLATE_ETAG_ADDSUFFIX; + } + else if (!strcasecmp(arg, "Remove")) { + c->etag_opt = AP_DEFLATE_ETAG_REMOVE; + } + else { + return "DeflateAlterETAG accepts only 'NoChange', 'AddSuffix', and 'Remove'"; + } + + return NULL; +} + + static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy, const char *arg) { @@ -389,35 +416,40 @@ typedef struct deflate_ctx_t /* Do update ctx->crc, see comment in flush_libz_buffer */ #define UPDATE_CRC 1 +static void consume_buffer(deflate_ctx *ctx, deflate_filter_config *c, + int len, int crc, apr_bucket_brigade *bb) +{ + apr_bucket *b; + + /* + * Do we need to update ctx->crc? Usually this is the case for + * inflate action where we need to do a crc on the output, whereas + * in the deflate case we need to do a crc on the input + */ + if (crc) { + ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); + } + + b = apr_bucket_heap_create((char *)ctx->buffer, len, NULL, + bb->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + + ctx->stream.next_out = ctx->buffer; + ctx->stream.avail_out = c->bufferSize; +} + static int flush_libz_buffer(deflate_ctx *ctx, deflate_filter_config *c, - struct apr_bucket_alloc_t *bucket_alloc, int (*libz_func)(z_streamp, int), int flush, int crc) { int zRC = Z_OK; int done = 0; - unsigned int deflate_len; - apr_bucket *b; + int deflate_len; for (;;) { deflate_len = c->bufferSize - ctx->stream.avail_out; - - if (deflate_len != 0) { - /* - * Do we need to update ctx->crc? Usually this is the case for - * inflate action where we need to do a crc on the output, whereas - * in the deflate case we need to do a crc on the input - */ - if (crc) { - ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, - deflate_len); - } - b = apr_bucket_heap_create((char *)ctx->buffer, - deflate_len, NULL, - bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->bb, b); - ctx->stream.next_out = ctx->buffer; - ctx->stream.avail_out = c->bufferSize; + if (deflate_len > 0) { + consume_buffer(ctx, c, deflate_len, crc, ctx->bb); } if (done) @@ -465,11 +497,16 @@ static apr_status_t deflate_ctx_cleanup(void *data) * value inside the double-quotes if an ETag has already been set * and its value already contains double-quotes. PR 39727 */ -static void deflate_check_etag(request_rec *r, const char *transform) +static void deflate_check_etag(request_rec *r, const char *transform, int etag_opt) { const char *etag = apr_table_get(r->headers_out, "ETag"); apr_size_t etaglen; + if (etag_opt == AP_DEFLATE_ETAG_REMOVE) { + apr_table_unset(r->headers_out, "ETag"); + return; + } + if ((etag && ((etaglen = strlen(etag)) > 2))) { if (etag[etaglen - 1] == '"') { apr_size_t transformlen = strlen(transform); @@ -514,10 +551,8 @@ static int check_ratio(request_rec *r, deflate_ctx *ctx, static int have_ssl_compression(request_rec *r) { const char *comp; - if (mod_deflate_ssl_var == NULL) - return 0; - comp = mod_deflate_ssl_var(r->pool, r->server, r->connection, r, - "SSL_COMPRESS_METHOD"); + comp = ap_ssl_var_lookup(r->pool, r->server, r->connection, r, + "SSL_COMPRESS_METHOD"); if (comp == NULL || *comp == '\0' || strcmp(comp, "NULL") == 0) return 0; return 1; @@ -530,6 +565,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, request_rec *r = f->r; deflate_ctx *ctx = f->ctx; int zRC; + apr_status_t rv; apr_size_t len = 0, blen; const char *data; deflate_filter_config *c; @@ -586,9 +622,14 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, continue; } - rc = apr_bucket_read(e, &data, &blen, APR_BLOCK_READ); - if (rc != APR_SUCCESS) - return rc; + if (e->length == (apr_size_t)-1) { + rc = apr_bucket_read(e, &data, &blen, APR_BLOCK_READ); + if (rc != APR_SUCCESS) + return rc; + } + else { + blen = e->length; + } len += blen; /* 50 is for Content-Encoding and Vary headers and ETag suffix */ if (len > sizeof(gzip_header) + VALIDATION_SIZE + 50) @@ -699,6 +740,8 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, */ if (!apr_table_get(r->subprocess_env, "force-gzip")) { const char *accepts; + const char *q = NULL; + /* if they don't have the line, then they can't play */ accepts = apr_table_get(r->headers_in, "Accept-Encoding"); if (accepts == NULL) { @@ -707,7 +750,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, } token = ap_get_token(r->pool, &accepts, 0); - while (token && token[0] && strcasecmp(token, "gzip")) { + while (token && token[0] && ap_cstr_casecmp(token, "gzip")) { /* skip parameters, XXX: ;q=foo evaluation? */ while (*accepts == ';') { ++accepts; @@ -721,10 +764,21 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL; } - /* No acceptable token found. */ - if (token == NULL || token[0] == '\0') { + /* Find the qvalue, if provided */ + if (*accepts) { + while (*accepts == ';') { + ++accepts; + } + q = ap_get_token(r->pool, &accepts, 1); + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "token: '%s' - q: '%s'", token ? token : "NULL", q); + } + + /* No acceptable token found or q=0 */ + if (!token || token[0] == '\0' || + (q && strlen(q) >= 3 && strncmp("q=0.000", q, strlen(q)) == 0)) { ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, - "Not compressing (no Accept-Encoding: gzip)"); + "Not compressing (no Accept-Encoding: gzip or q=0)"); ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } @@ -781,7 +835,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, */ /* If the entire Content-Encoding is "identity", we can replace it. */ - if (!encoding || !strcasecmp(encoding, "identity")) { + if (!encoding || !ap_cstr_casecmp(encoding, "identity")) { apr_table_setn(r->headers_out, "Content-Encoding", "gzip"); } else { @@ -794,7 +848,9 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, } apr_table_unset(r->headers_out, "Content-Length"); apr_table_unset(r->headers_out, "Content-MD5"); - deflate_check_etag(r, "gzip"); + if (c->etag_opt != AP_DEFLATE_ETAG_NOCHANGE) { + deflate_check_etag(r, "gzip", c->etag_opt); + } /* For a 304 response, only change the headers */ if (r->status == HTTP_NOT_MODIFIED) { @@ -841,8 +897,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, ctx->stream.avail_in = 0; /* should be zero already anyway */ /* flush the remaining data from the zlib buffers */ - flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, Z_FINISH, - NO_UPDATE_CRC); + flush_libz_buffer(ctx, c, deflate, Z_FINISH, NO_UPDATE_CRC); buf = apr_palloc(r->pool, VALIDATION_SIZE); putLong((unsigned char *)&buf[0], ctx->crc); @@ -852,8 +907,10 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(ctx->bb, b); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01384) - "Zlib: Compressed %ld to %ld : URL %s", - ctx->stream.total_in, ctx->stream.total_out, r->uri); + "Zlib: Compressed %" APR_UINT64_T_FMT + " to %" APR_UINT64_T_FMT " : URL %s", + (apr_uint64_t)ctx->stream.total_in, + (apr_uint64_t)ctx->stream.total_out, r->uri); /* leave notes for logging */ if (c->note_input_name) { @@ -866,7 +923,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, if (c->note_output_name) { apr_table_setn(r->notes, c->note_output_name, - (ctx->stream.total_in > 0) + (ctx->stream.total_out > 0) ? apr_off_t_toa(r->pool, ctx->stream.total_out) : "-"); @@ -883,6 +940,10 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, } deflateEnd(&ctx->stream); + + /* We've ended the libz stream, so remove ourselves. */ + ap_remove_output_filter(f); + /* No need for cleanup any longer */ apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup); @@ -893,15 +954,15 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, /* Okay, we've seen the EOS. * Time to pass it along down the chain. */ - return ap_pass_brigade(f->next, ctx->bb); + rv = ap_pass_brigade(f->next, ctx->bb); + apr_brigade_cleanup(ctx->bb); + return rv; } if (APR_BUCKET_IS_FLUSH(e)) { - apr_status_t rv; - /* flush the remaining data from the zlib buffers */ - zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, - Z_SYNC_FLUSH, NO_UPDATE_CRC); + zRC = flush_libz_buffer(ctx, c, deflate, Z_SYNC_FLUSH, + NO_UPDATE_CRC); if (zRC != Z_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01385) "Zlib error %d flushing zlib output buffer (%s)", @@ -913,6 +974,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, APR_BUCKET_REMOVE(e); APR_BRIGADE_INSERT_TAIL(ctx->bb, e); rv = ap_pass_brigade(f->next, ctx->bb); + apr_brigade_cleanup(ctx->bb); if (rv != APR_SUCCESS) { return rv; } @@ -930,7 +992,12 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, } /* read */ - apr_bucket_read(e, &data, &len, APR_BLOCK_READ); + rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ); + if (rv) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10298) + "failed reading from %s bucket", e->type->name); + return rv; + } if (!len) { apr_bucket_delete(e); continue; @@ -947,21 +1014,15 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness, * but we'll just have to * trust zlib */ - ctx->stream.avail_in = len; + ctx->stream.avail_in = (int)len; while (ctx->stream.avail_in != 0) { if (ctx->stream.avail_out == 0) { - apr_status_t rv; - - ctx->stream.next_out = ctx->buffer; - len = c->bufferSize - ctx->stream.avail_out; + consume_buffer(ctx, c, c->bufferSize, NO_UPDATE_CRC, ctx->bb); - b = apr_bucket_heap_create((char *)ctx->buffer, len, - NULL, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->bb, b); - ctx->stream.avail_out = c->bufferSize; /* Send what we have right now to the next filter. */ rv = ap_pass_brigade(f->next, ctx->bb); + apr_brigade_cleanup(ctx->bb); if (rv != APR_SUCCESS) { return rv; } @@ -1258,44 +1319,40 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, if (APR_BUCKET_IS_FLUSH(bkt)) { apr_bucket *tmp_b; - ctx->inflate_total += ctx->stream.avail_out; - zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH); - ctx->inflate_total -= ctx->stream.avail_out; - if (zRC != Z_OK) { - inflateEnd(&ctx->stream); - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01391) - "Zlib error %d inflating data (%s)", zRC, - ctx->stream.msg); - return APR_EGENERAL; - } + if (!ctx->done) { + ctx->inflate_total += ctx->stream.avail_out; + zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH); + ctx->inflate_total -= ctx->stream.avail_out; + if (zRC != Z_OK) { + inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01391) + "Zlib error %d inflating data (%s)", zRC, + ctx->stream.msg); + return APR_EGENERAL; + } - if (inflate_limit && ctx->inflate_total > inflate_limit) { - inflateEnd(&ctx->stream); - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02647) - "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, APLOGNO(02805) - "Inflated content ratio is larger than the " - "configured limit %i by %i time(s)", - dc->ratio_limit, dc->ratio_burst); - return APR_EINVAL; - } + if (inflate_limit && ctx->inflate_total > inflate_limit) { + inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02647) + "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; + } - len = c->bufferSize - ctx->stream.avail_out; - ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); - tmp_b = apr_bucket_heap_create((char *)ctx->buffer, len, - NULL, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_b); + if (!check_ratio(r, ctx, dc)) { + inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02805) + "Inflated content ratio is larger than the " + "configured limit %i by %i time(s)", + dc->ratio_limit, dc->ratio_burst); + return APR_EINVAL; + } - ctx->stream.next_out = ctx->buffer; - ctx->stream.avail_out = c->bufferSize; + consume_buffer(ctx, c, c->bufferSize - ctx->stream.avail_out, + UPDATE_CRC, ctx->proc_bb); + } /* Flush everything so far in the returning brigade, but continue * reading should EOS/more follow (don't lose them). @@ -1338,21 +1395,11 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, ctx->stream.next_in = (unsigned char *)data; ctx->stream.avail_in = (int)len; - zRC = Z_OK; - 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->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; + consume_buffer(ctx, c, c->bufferSize, UPDATE_CRC, + ctx->proc_bb); } ctx->inflate_total += ctx->stream.avail_out; @@ -1395,7 +1442,6 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, } if (ctx->validation_buffer) { - apr_bucket *tmp_heap; apr_size_t avail, valid; unsigned char *buf = ctx->validation_buffer; @@ -1419,17 +1465,13 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, ctx->validation_buffer_length += valid; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01393) - "Zlib: Inflated %ld to %ld : URL %s", - ctx->stream.total_in, ctx->stream.total_out, - r->uri); + "Zlib: Inflated %" APR_UINT64_T_FMT + " to %" APR_UINT64_T_FMT " : URL %s", + (apr_uint64_t)ctx->stream.total_in, + (apr_uint64_t)ctx->stream.total_out, r->uri); - len = c->bufferSize - ctx->stream.avail_out; - - 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; + consume_buffer(ctx, c, c->bufferSize - ctx->stream.avail_out, + UPDATE_CRC, ctx->proc_bb); { unsigned long compCRC, compLen; @@ -1445,9 +1487,10 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, if ((ctx->stream.total_out & 0xFFFFFFFF) != compLen) { inflateEnd(&ctx->stream); ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01395) - "Zlib: Length %ld of inflated data does " - "not match expected value %ld", - ctx->stream.total_out, compLen); + "Zlib: Length %" APR_UINT64_T_FMT + " of inflated data does not match" + " expected value %ld", + (apr_uint64_t)ctx->stream.total_out, compLen); return APR_EGENERAL; } } @@ -1474,16 +1517,8 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, if (block == APR_BLOCK_READ && 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; - len = c->bufferSize - ctx->stream.avail_out; - - 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; + consume_buffer(ctx, c, c->bufferSize - ctx->stream.avail_out, + UPDATE_CRC, ctx->proc_bb); } if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) { @@ -1549,7 +1584,9 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, */ apr_table_unset(r->headers_out, "Content-Length"); apr_table_unset(r->headers_out, "Content-MD5"); - deflate_check_etag(r, "gunzip"); + if (c->etag_opt != AP_DEFLATE_ETAG_NOCHANGE) { + deflate_check_etag(r, "gunzip", c->etag_opt); + } /* For a 304 response, only change the headers */ if (r->status == HTTP_NOT_MODIFIED) { @@ -1597,7 +1634,6 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, while (!APR_BRIGADE_EMPTY(bb)) { const char *data; - apr_bucket *b; apr_size_t len; e = APR_BRIGADE_FIRST(bb); @@ -1619,11 +1655,12 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, * fails, whereas in the deflate case you can empty a filled output * buffer and call it again until no more output can be created. */ - flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, Z_SYNC_FLUSH, - UPDATE_CRC); + flush_libz_buffer(ctx, c, inflate, Z_SYNC_FLUSH, UPDATE_CRC); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01398) - "Zlib: Inflated %ld to %ld : URL %s", - ctx->stream.total_in, ctx->stream.total_out, r->uri); + "Zlib: Inflated %" APR_UINT64_T_FMT + " to %" APR_UINT64_T_FMT " : URL %s", + (apr_uint64_t)ctx->stream.total_in, + (apr_uint64_t)ctx->stream.total_out, r->uri); if (ctx->validation_buffer_length == VALIDATION_SIZE) { unsigned long compCRC, compLen; @@ -1660,15 +1697,14 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, * Okay, we've seen the EOS. * Time to pass it along down the chain. */ - return ap_pass_brigade(f->next, ctx->bb); + rv = ap_pass_brigade(f->next, ctx->bb); + apr_brigade_cleanup(ctx->bb); + return rv; } if (APR_BUCKET_IS_FLUSH(e)) { - apr_status_t rv; - /* flush the remaining data from the zlib buffers */ - zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, - Z_SYNC_FLUSH, UPDATE_CRC); + zRC = flush_libz_buffer(ctx, c, inflate, Z_SYNC_FLUSH, UPDATE_CRC); if (zRC == Z_STREAM_END) { if (ctx->validation_buffer == NULL) { ctx->validation_buffer = apr_pcalloc(f->r->pool, @@ -1686,6 +1722,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, APR_BUCKET_REMOVE(e); APR_BRIGADE_INSERT_TAIL(ctx->bb, e); rv = ap_pass_brigade(f->next, ctx->bb); + apr_brigade_cleanup(ctx->bb); if (rv != APR_SUCCESS) { return rv; } @@ -1802,16 +1839,11 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, while (ctx->stream.avail_in != 0) { if (ctx->stream.avail_out == 0) { - ctx->stream.next_out = ctx->buffer; - len = c->bufferSize - ctx->stream.avail_out; - - ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); - b = apr_bucket_heap_create((char *)ctx->buffer, len, - NULL, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->bb, b); - ctx->stream.avail_out = c->bufferSize; + consume_buffer(ctx, c, c->bufferSize, UPDATE_CRC, ctx->bb); + /* Send what we have right now to the next filter. */ rv = ap_pass_brigade(f->next, ctx->bb); + apr_brigade_cleanup(ctx->bb); if (rv != APR_SUCCESS) { return rv; } @@ -1826,6 +1858,7 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, return APR_EGENERAL; } + /* Don't check length limits on inflate_out */ if (!check_ratio(r, ctx, dc)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02650) "Inflated content ratio is larger than the " @@ -1868,7 +1901,6 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, static int mod_deflate_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { - mod_deflate_ssl_var = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); return OK; } @@ -1904,6 +1936,9 @@ static const command_rec deflate_filter_cmds[] = { AP_INIT_TAKE1("DeflateInflateRatioBurst", deflate_set_inflate_ratio_burst, NULL, OR_ALL, "Set the maximum number of following inflate ratios above limit " "(default: " APR_STRINGIFY(AP_INFLATE_RATIO_BURST) ")"), + AP_INIT_TAKE1("DeflateAlterEtag", deflate_set_etag, NULL, RSRC_CONF, + "Set how mod_deflate should modify ETAG response headers: 'AddSuffix' (default), 'NoChange' (2.2.x behavior), 'Remove'"), + {NULL} }; diff --git a/modules/filters/mod_ext_filter.c b/modules/filters/mod_ext_filter.c index 7aac19d..7afd8dd 100644 --- a/modules/filters/mod_ext_filter.c +++ b/modules/filters/mod_ext_filter.c @@ -655,8 +655,7 @@ static apr_status_t drain_available_output(ap_filter_t *f, if (rv && !APR_STATUS_IS_EAGAIN(rv)) lvl = APLOG_DEBUG; ap_log_rerror(APLOG_MARK, lvl, rv, r, APLOGNO(01460) - "apr_file_read(child output), len %" APR_SIZE_T_FMT, - !rv ? len : -1); + "apr_file_read(child output), len %" APR_SIZE_T_FMT, len); if (rv != APR_SUCCESS) { return rv; } @@ -810,8 +809,7 @@ static int ef_unified_filter(ap_filter_t *f, apr_bucket_brigade *bb) if (rv && !APR_STATUS_IS_EOF(rv) && !APR_STATUS_IS_EAGAIN(rv)) lvl = APLOG_ERR; ap_log_rerror(APLOG_MARK, lvl, rv, r, APLOGNO(01466) - "apr_file_read(child output), len %" APR_SIZE_T_FMT, - !rv ? len : -1); + "apr_file_read(child output), len %" APR_SIZE_T_FMT, len); if (APR_STATUS_IS_EAGAIN(rv)) { if (eos) { /* should not occur, because we have an APR timeout in place */ diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c index a46a944..584d8fb 100644 --- a/modules/filters/mod_include.c +++ b/modules/filters/mod_include.c @@ -1967,25 +1967,25 @@ static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f, token = apr_strtok(d, ", \t", &last); while (token) { - if (!strcasecmp(token, "none")) { + if (!ap_cstr_casecmp(token, "none")) { /* do nothing */ } - else if (!strcasecmp(token, "url")) { + else if (!ap_cstr_casecmp(token, "url")) { char *buf = apr_pstrdup(ctx->pool, echo_text); ap_unescape_url(buf); echo_text = buf; } - else if (!strcasecmp(token, "urlencoded")) { + else if (!ap_cstr_casecmp(token, "urlencoded")) { char *buf = apr_pstrdup(ctx->pool, echo_text); ap_unescape_urlencoded(buf); echo_text = buf; } - else if (!strcasecmp(token, "entity")) { + else if (!ap_cstr_casecmp(token, "entity")) { char *buf = apr_pstrdup(ctx->pool, echo_text); decodehtml(buf); echo_text = buf; } - else if (!strcasecmp(token, "base64")) { + else if (!ap_cstr_casecmp(token, "base64")) { echo_text = ap_pbase64decode(ctx->dpool, echo_text); } else { @@ -2003,19 +2003,19 @@ static apr_status_t handle_echo(include_ctx_t *ctx, ap_filter_t *f, token = apr_strtok(e, ", \t", &last); while (token) { - if (!strcasecmp(token, "none")) { + if (!ap_cstr_casecmp(token, "none")) { /* do nothing */ } - else if (!strcasecmp(token, "url")) { + else if (!ap_cstr_casecmp(token, "url")) { echo_text = ap_escape_uri(ctx->dpool, echo_text); } - else if (!strcasecmp(token, "urlencoded")) { + else if (!ap_cstr_casecmp(token, "urlencoded")) { echo_text = ap_escape_urlencoded(ctx->dpool, echo_text); } - else if (!strcasecmp(token, "entity")) { + else if (!ap_cstr_casecmp(token, "entity")) { echo_text = ap_escape_html2(ctx->dpool, echo_text, 0); } - else if (!strcasecmp(token, "base64")) { + else if (!ap_cstr_casecmp(token, "base64")) { char *buf; buf = ap_pbase64encode(ctx->dpool, (char *)echo_text); echo_text = buf; @@ -2605,25 +2605,25 @@ static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f, token = apr_strtok(d, ", \t", &last); while (token) { - if (!strcasecmp(token, "none")) { + if (!ap_cstr_casecmp(token, "none")) { /* do nothing */ } - else if (!strcasecmp(token, "url")) { + else if (!ap_cstr_casecmp(token, "url")) { char *buf = apr_pstrdup(ctx->pool, parsed_string); ap_unescape_url(buf); parsed_string = buf; } - else if (!strcasecmp(token, "urlencoded")) { + else if (!ap_cstr_casecmp(token, "urlencoded")) { char *buf = apr_pstrdup(ctx->pool, parsed_string); ap_unescape_urlencoded(buf); parsed_string = buf; } - else if (!strcasecmp(token, "entity")) { + else if (!ap_cstr_casecmp(token, "entity")) { char *buf = apr_pstrdup(ctx->pool, parsed_string); decodehtml(buf); parsed_string = buf; } - else if (!strcasecmp(token, "base64")) { + else if (!ap_cstr_casecmp(token, "base64")) { parsed_string = ap_pbase64decode(ctx->dpool, parsed_string); } else { @@ -2641,19 +2641,19 @@ static apr_status_t handle_set(include_ctx_t *ctx, ap_filter_t *f, token = apr_strtok(e, ", \t", &last); while (token) { - if (!strcasecmp(token, "none")) { + if (!ap_cstr_casecmp(token, "none")) { /* do nothing */ } - else if (!strcasecmp(token, "url")) { + else if (!ap_cstr_casecmp(token, "url")) { parsed_string = ap_escape_uri(ctx->dpool, parsed_string); } - else if (!strcasecmp(token, "urlencoded")) { + else if (!ap_cstr_casecmp(token, "urlencoded")) { parsed_string = ap_escape_urlencoded(ctx->dpool, parsed_string); } - else if (!strcasecmp(token, "entity")) { + else if (!ap_cstr_casecmp(token, "entity")) { parsed_string = ap_escape_html2(ctx->dpool, parsed_string, 0); } - else if (!strcasecmp(token, "base64")) { + else if (!ap_cstr_casecmp(token, "base64")) { char *buf; buf = ap_pbase64encode(ctx->dpool, (char *)parsed_string); parsed_string = buf; @@ -3855,6 +3855,7 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) ctx->intern = intern = apr_palloc(r->pool, sizeof(*ctx->intern)); ctx->pool = r->pool; apr_pool_create(&ctx->dpool, ctx->pool); + apr_pool_tag(ctx->dpool, "includes_dpool"); /* runtime data */ intern->tmp_bb = apr_brigade_create(ctx->pool, f->c->bucket_alloc); diff --git a/modules/filters/mod_proxy_html.c b/modules/filters/mod_proxy_html.c index ea6bf03..7783da1 100644 --- a/modules/filters/mod_proxy_html.c +++ b/modules/filters/mod_proxy_html.c @@ -29,9 +29,28 @@ #define VERBOSEB(x) if (verbose) {x} #endif +/* libxml2 includes unicode/[...].h files which uses C++ comments */ +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wcomment" +#elif defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic push +#pragma GCC diagnostic warning "-Wcomment" +#endif +#endif + /* libxml2 */ #include <libxml/HTMLparser.h> +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic pop +#endif +#endif + #include "http_protocol.h" #include "http_config.h" #include "http_log.h" @@ -88,7 +107,7 @@ typedef struct { const char *doctype; const char *etag; unsigned int flags; - size_t bufsz; + int bufsz; apr_hash_t *links; apr_array_header_t *events; const char *charset_out; @@ -660,7 +679,7 @@ static meta *metafix(request_rec *r, const char *buf, apr_size_t len) while (!apr_isalpha(*++p)); for (q = p; apr_isalnum(*q) || (*q == '-'); ++q); header = apr_pstrmemdup(r->pool, p, q-p); - if (strncasecmp(header, "Content-", 8)) { + if (ap_cstr_casecmpn(header, "Content-", 8)) { /* find content=... string */ p = apr_strmatch(seek_content, buf+offs+pmatch[0].rm_so, pmatch[0].rm_eo - pmatch[0].rm_so); @@ -677,7 +696,7 @@ static meta *metafix(request_rec *r, const char *buf, apr_size_t len) if ((*p == '\'') || (*p == '"')) { delim = *p++; for (q = p; *q && *q != delim; ++q); - /* No terminating delimiter found? Skip the boggus directive */ + /* No terminating delimiter found? Skip the bogus directive */ if (*q != delim) break; } else { @@ -688,7 +707,7 @@ static meta *metafix(request_rec *r, const char *buf, apr_size_t len) } } } - else if (!strncasecmp(header, "Content-Type", 12)) { + else if (!ap_cstr_casecmpn(header, "Content-Type", 12)) { ret = apr_palloc(r->pool, sizeof(meta)); ret->start = offs+pmatch[0].rm_so; ret->end = offs+pmatch[0].rm_eo; @@ -817,8 +836,8 @@ static saxctxt *check_filter_init (ap_filter_t *f) else if (!f->r->content_type) { errmsg = "No content-type; bailing out of proxy-html filter"; } - else if (strncasecmp(f->r->content_type, "text/html", 9) && - strncasecmp(f->r->content_type, + else if (ap_cstr_casecmpn(f->r->content_type, "text/html", 9) && + ap_cstr_casecmpn(f->r->content_type, "application/xhtml+xml", 21)) { errmsg = "Non-HTML content; not inserting proxy-html filter"; } diff --git a/modules/filters/mod_reflector.c b/modules/filters/mod_reflector.c index 961092d..5979cb8 100644 --- a/modules/filters/mod_reflector.c +++ b/modules/filters/mod_reflector.c @@ -91,11 +91,16 @@ static int reflector_handler(request_rec * r) /* reflect the content length, if present */ if ((content_length = apr_table_get(r->headers_in, "Content-Length"))) { - apr_off_t offset; + apr_off_t clen; - apr_strtoff(&offset, content_length, NULL, 10); - ap_set_content_length(r, offset); + if (!ap_parse_strict_length(&clen, content_length)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10243) + "reflector_handler: invalid content-length '%s'", + content_length); + return HTTP_BAD_REQUEST; + } + ap_set_content_length(r, clen); } /* reflect the content type, if present */ diff --git a/modules/filters/mod_reqtimeout.c b/modules/filters/mod_reqtimeout.c index 538e9b1..0ebd78a 100644 --- a/modules/filters/mod_reqtimeout.c +++ b/modules/filters/mod_reqtimeout.c @@ -29,23 +29,29 @@ module AP_MODULE_DECLARE_DATA reqtimeout_module; #define UNSET -1 -#define MRT_DEFAULT_HEADER_TIMEOUT 20 -#define MRT_DEFAULT_HEADER_MAX_TIMEOUT 40 -#define MRT_DEFAULT_HEADER_MIN_RATE 500 -#define MRT_DEFAULT_BODY_TIMEOUT 20 -#define MRT_DEFAULT_BODY_MAX_TIMEOUT 0 -#define MRT_DEFAULT_BODY_MIN_RATE 500 +#define MRT_DEFAULT_handshake_TIMEOUT 0 /* disabled */ +#define MRT_DEFAULT_handshake_MAX_TIMEOUT 0 +#define MRT_DEFAULT_handshake_MIN_RATE 0 +#define MRT_DEFAULT_header_TIMEOUT 20 +#define MRT_DEFAULT_header_MAX_TIMEOUT 40 +#define MRT_DEFAULT_header_MIN_RATE 500 +#define MRT_DEFAULT_body_TIMEOUT 20 +#define MRT_DEFAULT_body_MAX_TIMEOUT 0 +#define MRT_DEFAULT_body_MIN_RATE 500 typedef struct { - int header_timeout; /* timeout for reading the req hdrs in secs */ - int header_max_timeout; /* max timeout for req hdrs in secs */ - int header_min_rate; /* min rate for reading req hdrs in bytes/s */ - apr_time_t header_rate_factor; - int body_timeout; /* timeout for reading the req body in secs */ - int body_max_timeout; /* max timeout for req body in secs */ - int body_min_rate; /* min rate for reading req body in bytes/s */ - apr_time_t body_rate_factor; + int timeout; /* timeout in secs */ + int max_timeout; /* max timeout in secs */ + int min_rate; /* min rate in bytes/s */ + apr_time_t rate_factor; /* scale factor (#usecs per min_rate) */ +} reqtimeout_stage_t; + +typedef struct +{ + reqtimeout_stage_t handshake; /* Handshaking (TLS) */ + reqtimeout_stage_t header; /* Reading the HTTP header */ + reqtimeout_stage_t body; /* Reading the HTTP body */ } reqtimeout_srv_cfg; /* this struct is used both as conn_config and as filter context */ @@ -53,17 +59,15 @@ typedef struct { apr_time_t timeout_at; apr_time_t max_timeout_at; - int min_rate; - int new_timeout; - int new_max_timeout; + reqtimeout_stage_t cur_stage; int in_keep_alive; char *type; apr_socket_t *socket; - apr_time_t rate_factor; apr_bucket_brigade *tmpbb; } reqtimeout_con_cfg; static const char *const reqtimeout_filter_name = "reqtimeout"; +static int default_handshake_rate_factor; static int default_header_rate_factor; static int default_body_rate_factor; @@ -75,7 +79,7 @@ static void extend_timeout(reqtimeout_con_cfg *ccfg, apr_bucket_brigade *bb) if (apr_brigade_length(bb, 0, &len) != APR_SUCCESS || len <= 0) return; - new_timeout_at = ccfg->timeout_at + len * ccfg->rate_factor; + new_timeout_at = ccfg->timeout_at + len * ccfg->cur_stage.rate_factor; if (ccfg->max_timeout_at > 0 && new_timeout_at > ccfg->max_timeout_at) { ccfg->timeout_at = ccfg->max_timeout_at; } @@ -190,14 +194,14 @@ static apr_status_t reqtimeout_filter(ap_filter_t *f, apr_brigade_cleanup(bb); } - if (ccfg->new_timeout > 0) { + if (ccfg->cur_stage.timeout > 0) { /* set new timeout */ now = apr_time_now(); - ccfg->timeout_at = now + apr_time_from_sec(ccfg->new_timeout); - ccfg->new_timeout = 0; - if (ccfg->new_max_timeout > 0) { - ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->new_max_timeout); - ccfg->new_max_timeout = 0; + ccfg->timeout_at = now + apr_time_from_sec(ccfg->cur_stage.timeout); + ccfg->cur_stage.timeout = 0; + if (ccfg->cur_stage.max_timeout > 0) { + ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->cur_stage.max_timeout); + ccfg->cur_stage.max_timeout = 0; } } else if (ccfg->timeout_at == 0) { @@ -216,7 +220,7 @@ static apr_status_t reqtimeout_filter(ap_filter_t *f, if (block == APR_NONBLOCK_READ || mode == AP_MODE_INIT || mode == AP_MODE_EATCRLF) { rv = ap_get_brigade(f->next, bb, mode, block, readbytes); - if (ccfg->min_rate > 0 && rv == APR_SUCCESS) { + if (ccfg->cur_stage.rate_factor && rv == APR_SUCCESS) { extend_timeout(ccfg, bb); } return rv; @@ -250,7 +254,7 @@ static apr_status_t reqtimeout_filter(ap_filter_t *f, } if (!APR_BRIGADE_EMPTY(bb)) { - if (ccfg->min_rate > 0) { + if (ccfg->cur_stage.rate_factor) { extend_timeout(ccfg, bb); } @@ -309,9 +313,9 @@ static apr_status_t reqtimeout_filter(ap_filter_t *f, rv = ap_get_brigade(f->next, bb, mode, block, readbytes); /* Don't extend the timeout in speculative mode, wait for * the real (relevant) bytes to be asked later, within the - * currently alloted time. + * currently allotted time. */ - if (ccfg->min_rate > 0 && rv == APR_SUCCESS + if (ccfg->cur_stage.rate_factor && rv == APR_SUCCESS && mode != AP_MODE_SPECULATIVE) { extend_timeout(ccfg, bb); } @@ -350,6 +354,19 @@ static apr_status_t reqtimeout_eor(ap_filter_t *f, apr_bucket_brigade *bb) return ap_pass_brigade(f->next, bb); } +#define INIT_STAGE(cfg, ccfg, stage) do { \ + if (cfg->stage.timeout != UNSET) { \ + ccfg->cur_stage.timeout = cfg->stage.timeout; \ + ccfg->cur_stage.max_timeout = cfg->stage.max_timeout; \ + ccfg->cur_stage.rate_factor = cfg->stage.rate_factor; \ + } \ + else { \ + ccfg->cur_stage.timeout = MRT_DEFAULT_##stage##_TIMEOUT; \ + ccfg->cur_stage.max_timeout = MRT_DEFAULT_##stage##_MAX_TIMEOUT; \ + ccfg->cur_stage.rate_factor = default_##stage##_rate_factor; \ + } \ +} while (0) + static int reqtimeout_init(conn_rec *c) { reqtimeout_con_cfg *ccfg; @@ -358,7 +375,11 @@ static int reqtimeout_init(conn_rec *c) cfg = ap_get_module_config(c->base_server->module_config, &reqtimeout_module); AP_DEBUG_ASSERT(cfg != NULL); - if (cfg->header_timeout == 0 && cfg->body_timeout == 0) { + + /* For compatibility, handshake timeout is disabled when UNSET (< 0) */ + if (cfg->handshake.timeout <= 0 + && cfg->header.timeout == 0 + && cfg->body.timeout == 0) { /* disabled for this vhost */ return DECLINED; } @@ -369,6 +390,11 @@ static int reqtimeout_init(conn_rec *c) ap_set_module_config(c->conn_config, &reqtimeout_module, ccfg); ap_add_output_filter(reqtimeout_filter_name, ccfg, NULL, c); ap_add_input_filter(reqtimeout_filter_name, ccfg, NULL, c); + + ccfg->type = "handshake"; + if (cfg->handshake.timeout > 0) { + INIT_STAGE(cfg, ccfg, handshake); + } } /* we are not handling the connection, we just do initialization */ @@ -393,22 +419,11 @@ static void reqtimeout_before_header(request_rec *r, conn_rec *c) /* (Re)set the state for this new request, but ccfg->socket and * ccfg->tmpbb which have the lifetime of the connection. */ + ccfg->type = "header"; ccfg->timeout_at = 0; ccfg->max_timeout_at = 0; ccfg->in_keep_alive = (c->keepalives > 0); - ccfg->type = "header"; - if (cfg->header_timeout != UNSET) { - ccfg->new_timeout = cfg->header_timeout; - ccfg->new_max_timeout = cfg->header_max_timeout; - ccfg->min_rate = cfg->header_min_rate; - ccfg->rate_factor = cfg->header_rate_factor; - } - else { - ccfg->new_timeout = MRT_DEFAULT_HEADER_TIMEOUT; - ccfg->new_max_timeout = MRT_DEFAULT_HEADER_MAX_TIMEOUT; - ccfg->min_rate = MRT_DEFAULT_HEADER_MIN_RATE; - ccfg->rate_factor = default_header_rate_factor; - } + INIT_STAGE(cfg, ccfg, header); } static int reqtimeout_before_body(request_rec *r) @@ -421,64 +436,61 @@ static int reqtimeout_before_body(request_rec *r) /* not configured for this connection */ return OK; } - cfg = ap_get_module_config(r->connection->base_server->module_config, - &reqtimeout_module); + cfg = ap_get_module_config(r->server->module_config, + &reqtimeout_module); AP_DEBUG_ASSERT(cfg != NULL); + ccfg->type = "body"; ccfg->timeout_at = 0; ccfg->max_timeout_at = 0; - ccfg->type = "body"; if (r->method_number == M_CONNECT) { /* disabled for a CONNECT request */ - ccfg->new_timeout = 0; - } - else if (cfg->body_timeout != UNSET) { - ccfg->new_timeout = cfg->body_timeout; - ccfg->new_max_timeout = cfg->body_max_timeout; - ccfg->min_rate = cfg->body_min_rate; - ccfg->rate_factor = cfg->body_rate_factor; + ccfg->cur_stage.timeout = 0; } else { - ccfg->new_timeout = MRT_DEFAULT_BODY_TIMEOUT; - ccfg->new_max_timeout = MRT_DEFAULT_BODY_MAX_TIMEOUT; - ccfg->min_rate = MRT_DEFAULT_BODY_MIN_RATE; - ccfg->rate_factor = default_body_rate_factor; + INIT_STAGE(cfg, ccfg, body); } return OK; } +#define UNSET_STAGE(cfg, stage) do { \ + cfg->stage.timeout = UNSET; \ + cfg->stage.max_timeout = UNSET; \ + cfg->stage.min_rate = UNSET; \ +} while (0) + static void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s) { reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg)); - cfg->header_timeout = UNSET; - cfg->header_max_timeout = UNSET; - cfg->header_min_rate = UNSET; - cfg->body_timeout = UNSET; - cfg->body_max_timeout = UNSET; - cfg->body_min_rate = UNSET; + UNSET_STAGE(cfg, handshake); + UNSET_STAGE(cfg, header); + UNSET_STAGE(cfg, body); return cfg; } -#define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == UNSET) ? b->val : a->val; +#define MERGE_INT(cfg, base, add, val) \ + cfg->val = (add->val == UNSET) ? base->val : add->val +#define MERGE_STAGE(cfg, base, add, stage) do { \ + MERGE_INT(cfg, base, add, stage.timeout); \ + MERGE_INT(cfg, base, add, stage.max_timeout); \ + MERGE_INT(cfg, base, add, stage.min_rate); \ + cfg->stage.rate_factor = (cfg->stage.min_rate == UNSET) \ + ? base->stage.rate_factor \ + : add->stage.rate_factor; \ +} while (0) + static void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_) { reqtimeout_srv_cfg *base = base_; reqtimeout_srv_cfg *add = add_; reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg)); - MERGE_INT(cfg, base, add, header_timeout); - MERGE_INT(cfg, base, add, header_max_timeout); - MERGE_INT(cfg, base, add, header_min_rate); - MERGE_INT(cfg, base, add, body_timeout); - MERGE_INT(cfg, base, add, body_max_timeout); - MERGE_INT(cfg, base, add, body_min_rate); - - cfg->header_rate_factor = (cfg->header_min_rate == UNSET) ? - base->header_rate_factor : add->header_rate_factor; - cfg->body_rate_factor = (cfg->body_min_rate == UNSET) ? - base->body_rate_factor : add->body_rate_factor; + MERGE_STAGE(cfg, base, add, handshake); + MERGE_STAGE(cfg, base, add, header); + MERGE_STAGE(cfg, base, add, body); + return cfg; } @@ -506,66 +518,59 @@ static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf, { const char *ret = NULL; char *rate_str = NULL, *initial_str, *max_str = NULL; - int rate = 0, initial = 0, max = 0; - enum { PARAM_HEADER, PARAM_BODY } type; + reqtimeout_stage_t *stage; - if (!strcasecmp(key, "header")) { - type = PARAM_HEADER; + if (!strcasecmp(key, "handshake")) { + stage = &conf->handshake; + } + else if (!strcasecmp(key, "header")) { + stage = &conf->header; } else if (!strcasecmp(key, "body")) { - type = PARAM_BODY; + stage = &conf->body; } else { return "Unknown RequestReadTimeout parameter"; } + memset(stage, 0, sizeof(*stage)); + if ((rate_str = ap_strcasestr(val, ",minrate="))) { initial_str = apr_pstrndup(p, val, rate_str - val); rate_str += strlen(",minrate="); - ret = parse_int(p, rate_str, &rate); + ret = parse_int(p, rate_str, &stage->min_rate); if (ret) return ret; - if (rate == 0) + if (stage->min_rate == 0) return "Minimum data rate must be larger than 0"; if ((max_str = strchr(initial_str, '-'))) { *max_str++ = '\0'; - ret = parse_int(p, max_str, &max); + ret = parse_int(p, max_str, &stage->max_timeout); if (ret) return ret; } - ret = parse_int(p, initial_str, &initial); + ret = parse_int(p, initial_str, &stage->timeout); } else { if (ap_strchr_c(val, '-')) return "Must set MinRate option if using timeout range"; - ret = parse_int(p, val, &initial); + ret = parse_int(p, val, &stage->timeout); } - if (ret) return ret; - if (max && initial >= max) { + if (stage->max_timeout && stage->timeout >= stage->max_timeout) { return "Maximum timeout must be larger than initial timeout"; } - if (type == PARAM_HEADER) { - conf->header_timeout = initial; - conf->header_max_timeout = max; - conf->header_min_rate = rate; - if (rate) - conf->header_rate_factor = apr_time_from_sec(1) / rate; - } - else { - conf->body_timeout = initial; - conf->body_max_timeout = max; - conf->body_min_rate = rate; - if (rate) - conf->body_rate_factor = apr_time_from_sec(1) / rate; + if (stage->min_rate) { + stage->rate_factor = apr_time_from_sec(1) / stage->min_rate; } - return ret; + + return NULL; } static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig, @@ -603,8 +608,7 @@ static void reqtimeout_hooks(apr_pool_t *pool) { /* * mod_ssl is AP_FTYPE_CONNECTION + 5 and mod_reqtimeout needs to - * be called before mod_ssl. Otherwise repeated reads during the ssl - * handshake can prevent the timeout from triggering. + * be called before mod_ssl for the handshake stage to catch SSL traffic. */ ap_register_input_filter(reqtimeout_filter_name, reqtimeout_filter, NULL, AP_FTYPE_CONNECTION + 8); @@ -621,28 +625,37 @@ static void reqtimeout_hooks(apr_pool_t *pool) * mod_reqtimeout needs to be called before ap_process_http_request (which * is run at APR_HOOK_REALLY_LAST) but after all other protocol modules. * This ensures that it only influences normal http connections and not - * e.g. mod_ftp. Also, if mod_reqtimeout used the pre_connection hook, it - * would be inserted on mod_proxy's backend connections. + * e.g. mod_ftp. We still process it first though, for the handshake stage + * to work with/before mod_ssl, but since it's disabled by default it won't + * influence non-HTTP modules unless configured explicitly. Also, if + * mod_reqtimeout used the pre_connection hook, it would be inserted on + * mod_proxy's backend connections, and we don't want this. */ - ap_hook_process_connection(reqtimeout_init, NULL, NULL, APR_HOOK_LAST); + ap_hook_process_connection(reqtimeout_init, NULL, NULL, APR_HOOK_FIRST); ap_hook_pre_read_request(reqtimeout_before_header, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_read_request(reqtimeout_before_body, NULL, NULL, APR_HOOK_MIDDLE); -#if MRT_DEFAULT_HEADER_MIN_RATE > 0 - default_header_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_HEADER_MIN_RATE; +#if MRT_DEFAULT_handshake_MIN_RATE + default_handshake_rate_factor = apr_time_from_sec(1) / + MRT_DEFAULT_handshake_MIN_RATE; +#endif +#if MRT_DEFAULT_header_MIN_RATE + default_header_rate_factor = apr_time_from_sec(1) / + MRT_DEFAULT_header_MIN_RATE; #endif -#if MRT_DEFAULT_BODY_MIN_RATE > 0 - default_body_rate_factor = apr_time_from_sec(1) / MRT_DEFAULT_BODY_MIN_RATE; +#if MRT_DEFAULT_body_MIN_RATE + default_body_rate_factor = apr_time_from_sec(1) / + MRT_DEFAULT_body_MIN_RATE; #endif } static const command_rec reqtimeout_cmds[] = { AP_INIT_RAW_ARGS("RequestReadTimeout", set_reqtimeouts, NULL, RSRC_CONF, - "Set various timeout parameters for reading request " - "headers and body"), + "Set various timeout parameters for TLS handshake and/or " + "reading request headers and body"), {NULL} }; diff --git a/modules/filters/mod_request.c b/modules/filters/mod_request.c index 21db7de..1768edc 100644 --- a/modules/filters/mod_request.c +++ b/modules/filters/mod_request.c @@ -73,10 +73,8 @@ static apr_status_t keep_body_filter(ap_filter_t *f, apr_bucket_brigade *b, apr_bucket *bucket; apr_off_t len = 0; - if (!ctx) { const char *lenp; - char *endstr = NULL; request_dir_conf *dconf = ap_get_module_config(f->r->per_dir_config, &request_module); @@ -93,13 +91,12 @@ static apr_status_t keep_body_filter(ap_filter_t *f, apr_bucket_brigade *b, if (lenp) { /* Protects against over/underflow, non-digit chars in the - * string (excluding leading space) (the endstr checks) - * and a negative number. */ - if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10) - || endstr == lenp || *endstr || ctx->remaining < 0) { - + * string, leading plus/minus signs, trailing characters and + * a negative number. + */ + if (!ap_parse_strict_length(&ctx->remaining, lenp)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01411) - "Invalid Content-Length"); + "Invalid Content-Length '%s'", lenp); ap_remove_input_filter(f); return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE); @@ -121,7 +118,6 @@ static apr_status_t keep_body_filter(ap_filter_t *f, apr_bucket_brigade *b, f->r->kept_body = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc); ctx->remaining = dconf->keep_body; - } /* get the brigade from upstream, and read it in to get its length */ @@ -264,8 +260,8 @@ static apr_status_t kept_body_filter(ap_filter_t *f, apr_bucket_brigade *b, ctx->remaining -= readbytes; ctx->offset += readbytes; - return APR_SUCCESS; + return APR_SUCCESS; } /** @@ -311,18 +307,18 @@ static void ap_request_insert_filter(request_rec * r) NULL, r, r->connection); } } - } -/** - * Remove the kept_body and keep body filters from this specific request. +/* + * Remove the kept_body and keep_body filters from this specific request. */ -static void ap_request_remove_filter(request_rec * r) +static void ap_request_remove_filter(request_rec *r) { - ap_filter_t * f = r->input_filters; + ap_filter_t *f = r->input_filters; + while (f) { if (f->frec->filter_func.in_func == kept_body_filter || - f->frec->filter_func.in_func == keep_body_filter) { + f->frec->filter_func.in_func == keep_body_filter) { ap_remove_input_filter(f); } f = f->next; diff --git a/modules/filters/mod_sed.c b/modules/filters/mod_sed.c index 346c210..12cb04a 100644 --- a/modules/filters/mod_sed.c +++ b/modules/filters/mod_sed.c @@ -51,7 +51,7 @@ typedef struct sed_filter_ctxt apr_bucket_brigade *bbinp; char *outbuf; char *curoutbuf; - int bufsize; + apr_size_t bufsize; apr_pool_t *tpool; int numbuckets; } sed_filter_ctxt; @@ -59,7 +59,7 @@ typedef struct sed_filter_ctxt module AP_MODULE_DECLARE_DATA sed_module; /* This function will be call back from libsed functions if there is any error - * happend during execution of sed scripts + * happened during execution of sed scripts */ static apr_status_t log_sed_errf(void *data, const char *error) { @@ -100,7 +100,7 @@ static void alloc_outbuf(sed_filter_ctxt* ctx) /* append_bucket * Allocate a new bucket from buf and sz and append to ctx->bb */ -static apr_status_t append_bucket(sed_filter_ctxt* ctx, char* buf, int sz) +static apr_status_t append_bucket(sed_filter_ctxt* ctx, char* buf, apr_size_t sz) { apr_status_t status = APR_SUCCESS; apr_bucket *b; @@ -133,7 +133,7 @@ static apr_status_t append_bucket(sed_filter_ctxt* ctx, char* buf, int sz) */ static apr_status_t flush_output_buffer(sed_filter_ctxt *ctx) { - int size = ctx->curoutbuf - ctx->outbuf; + apr_size_t size = ctx->curoutbuf - ctx->outbuf; char *out; apr_status_t status = APR_SUCCESS; if ((ctx->outbuf == NULL) || (size <=0)) @@ -147,12 +147,12 @@ static apr_status_t flush_output_buffer(sed_filter_ctxt *ctx) /* This is a call back function. When libsed wants to generate the output, * this function will be invoked. */ -static apr_status_t sed_write_output(void *dummy, char *buf, int sz) +static apr_status_t sed_write_output(void *dummy, char *buf, apr_size_t sz) { /* dummy is basically filter context. Context is passed during invocation * of sed_eval_buffer */ - int remainbytes = 0; + apr_size_t remainbytes = 0; apr_status_t status = APR_SUCCESS; sed_filter_ctxt *ctx = (sed_filter_ctxt *) dummy; if (ctx->outbuf == NULL) { @@ -168,21 +168,29 @@ static apr_status_t sed_write_output(void *dummy, char *buf, int sz) } /* buffer is now full */ status = append_bucket(ctx, ctx->outbuf, ctx->bufsize); - /* old buffer is now used so allocate new buffer */ - alloc_outbuf(ctx); - /* if size is bigger than the allocated buffer directly add to output - * brigade */ - if ((status == APR_SUCCESS) && (sz >= ctx->bufsize)) { - char* newbuf = apr_pmemdup(ctx->tpool, buf, sz); - status = append_bucket(ctx, newbuf, sz); - /* pool might get clear after append_bucket */ - if (ctx->outbuf == NULL) { + if (status == APR_SUCCESS) { + /* if size is bigger than the allocated buffer directly add to output + * brigade */ + if (sz >= ctx->bufsize) { + char* newbuf = apr_pmemdup(ctx->tpool, buf, sz); + status = append_bucket(ctx, newbuf, sz); + if (status == APR_SUCCESS) { + /* old buffer is now used so allocate new buffer */ + alloc_outbuf(ctx); + } + else { + clear_ctxpool(ctx); + } + } + else { + /* old buffer is now used so allocate new buffer */ alloc_outbuf(ctx); + memcpy(ctx->curoutbuf, buf, sz); + ctx->curoutbuf += sz; } } else { - memcpy(ctx->curoutbuf, buf, sz); - ctx->curoutbuf += sz; + clear_ctxpool(ctx); } } else { @@ -254,6 +262,7 @@ static apr_status_t init_context(ap_filter_t *f, sed_expr_config *sed_cfg, int u ctx->bufsize = MODSED_OUTBUF_SIZE; if (usetpool) { apr_pool_create(&(ctx->tpool), r->pool); + apr_pool_tag(ctx->tpool, "sed_tpool"); } else { ctx->tpool = r->pool; @@ -268,7 +277,7 @@ static apr_status_t sed_response_filter(ap_filter_t *f, apr_bucket_brigade *bb) { apr_bucket *b; - apr_status_t status; + apr_status_t status = APR_SUCCESS; sed_config *cfg = ap_get_module_config(f->r->per_dir_config, &sed_module); sed_filter_ctxt *ctx = f->ctx; @@ -293,9 +302,9 @@ static apr_status_t sed_response_filter(ap_filter_t *f, return status; ctx = f->ctx; apr_table_unset(f->r->headers_out, "Content-Length"); - } - ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); + } /* Here is the main logic. Iterate through all the buckets, read the * content of the bucket, call sed_eval_buffer on the data. @@ -317,63 +326,52 @@ static apr_status_t sed_response_filter(ap_filter_t *f, * in sed's internal buffer which can't be flushed until new line * character is arrived. */ - for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb);) { - const char *buf = NULL; - apr_size_t bytes = 0; + while (!APR_BRIGADE_EMPTY(bb)) { + b = APR_BRIGADE_FIRST(bb); if (APR_BUCKET_IS_EOS(b)) { - apr_bucket *b1 = APR_BUCKET_NEXT(b); /* Now clean up the internal sed buffer */ sed_finalize_eval(&ctx->eval, ctx); status = flush_output_buffer(ctx); if (status != APR_SUCCESS) { - clear_ctxpool(ctx); - return status; + break; } + /* Move the eos bucket to ctx->bb brigade */ APR_BUCKET_REMOVE(b); - /* Insert the eos bucket to ctx->bb brigade */ APR_BRIGADE_INSERT_TAIL(ctx->bb, b); - b = b1; } else if (APR_BUCKET_IS_FLUSH(b)) { - apr_bucket *b1 = APR_BUCKET_NEXT(b); - APR_BUCKET_REMOVE(b); status = flush_output_buffer(ctx); if (status != APR_SUCCESS) { - clear_ctxpool(ctx); - return status; + break; } + /* Move the flush bucket to ctx->bb brigade */ + APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->bb, b); - b = b1; } - else if (APR_BUCKET_IS_METADATA(b)) { - b = APR_BUCKET_NEXT(b); - } - else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) - == APR_SUCCESS) { - apr_bucket *b1 = APR_BUCKET_NEXT(b); - status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx); - if (status != APR_SUCCESS) { - clear_ctxpool(ctx); - return status; + else { + if (!APR_BUCKET_IS_METADATA(b)) { + const char *buf = NULL; + apr_size_t bytes = 0; + + status = apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ); + if (status == APR_SUCCESS) { + status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx); + } + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, f->r, APLOGNO(10394) "error evaluating sed on output"); + break; + } } - APR_BUCKET_REMOVE(b); apr_bucket_delete(b); - b = b1; - } - else { - apr_bucket *b1 = APR_BUCKET_NEXT(b); - APR_BUCKET_REMOVE(b); - b = b1; } } - apr_brigade_cleanup(bb); - status = flush_output_buffer(ctx); - if (status != APR_SUCCESS) { - clear_ctxpool(ctx); - return status; + if (status == APR_SUCCESS) { + status = flush_output_buffer(ctx); } if (!APR_BRIGADE_EMPTY(ctx->bb)) { - status = ap_pass_brigade(f->next, ctx->bb); + if (status == APR_SUCCESS) { + status = ap_pass_brigade(f->next, ctx->bb); + } apr_brigade_cleanup(ctx->bb); } clear_ctxpool(ctx); @@ -424,7 +422,7 @@ static apr_status_t sed_request_filter(ap_filter_t *f, * the buckets in bbinp and read the data from buckets and invoke * sed_eval_buffer on the data. libsed will generate its output using * sed_write_output which will add data in ctx->bb. Do it until it have - * atleast one bucket in ctx->bb. At the end of data eos bucket + * at least one bucket in ctx->bb. At the end of data eos bucket * should be there. * * Once eos bucket is seen, then invoke sed_finalize_eval to clear the @@ -466,8 +464,10 @@ static apr_status_t sed_request_filter(ap_filter_t *f, if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) == APR_SUCCESS) { status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx); - if (status != APR_SUCCESS) + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, f->r, APLOGNO(10395) "error evaluating sed on input"); return status; + } flush_output_buffer(ctx); } } diff --git a/modules/filters/mod_substitute.c b/modules/filters/mod_substitute.c index b7d5296..d454bf3 100644 --- a/modules/filters/mod_substitute.c +++ b/modules/filters/mod_substitute.c @@ -306,7 +306,7 @@ static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb, } else { apr_size_t repl_len; - /* acount for string before the match */ + /* account for string before the match */ if (space_left <= regm[0].rm_so) return APR_ENOMEM; space_left -= regm[0].rm_so; @@ -402,6 +402,7 @@ static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb) ctx->passbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); /* Create our temporary pool only once */ apr_pool_create(&(ctx->tpool), f->r->pool); + apr_pool_tag(ctx->tpool, "substitute_tpool"); apr_table_unset(f->r->headers_out, "Content-Length"); } @@ -667,8 +668,10 @@ static const char *set_pattern(cmd_parms *cmd, void *cfg, const char *line) /* first see if we can compile the regex */ if (!is_pattern) { - r = ap_pregcomp(cmd->pool, from, AP_REG_EXTENDED | - (ignore_case ? AP_REG_ICASE : 0)); + int flags = AP_REG_NO_DEFAULT + | (ap_regcomp_get_default_cflags() & AP_REG_DOLLAR_ENDONLY) + | (ignore_case ? AP_REG_ICASE : 0); + r = ap_pregcomp(cmd->pool, from, flags); if (!r) return "Substitute could not compile regex"; } diff --git a/modules/filters/mod_xml2enc.c b/modules/filters/mod_xml2enc.c index 05a4e9a..eb05c18 100644 --- a/modules/filters/mod_xml2enc.c +++ b/modules/filters/mod_xml2enc.c @@ -23,9 +23,28 @@ #include <ctype.h> +/* libxml2 includes unicode/[...].h files which uses C++ comments */ +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wcomment" +#elif defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic push +#pragma GCC diagnostic warning "-Wcomment" +#endif +#endif + /* libxml2 */ #include <libxml/encoding.h> +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic pop +#endif +#endif + #include "http_protocol.h" #include "http_config.h" #include "http_log.h" @@ -51,7 +70,7 @@ module AP_MODULE_DECLARE_DATA xml2enc_module; (((enc)!=XML_CHAR_ENCODING_NONE)&&((enc)!=XML_CHAR_ENCODING_ERROR)) /* - * XXX: Check all those ap_assert()s ans replace those that should not happen + * XXX: Check all those ap_assert()s and replace those that should not happen * XXX: with AP_DEBUG_ASSERT and those that may happen with proper error * XXX: handling. */ @@ -187,11 +206,11 @@ static void sniff_encoding(request_rec* r, xml2ctx* ctx) } } } - + /* to sniff, first we look for BOM */ if (ctx->xml2enc == XML_CHAR_ENCODING_NONE) { - ctx->xml2enc = xmlDetectCharEncoding((const xmlChar*)ctx->buf, - ctx->bytes); + ctx->xml2enc = xmlDetectCharEncoding((const unsigned char*)ctx->buf, + ctx->bytes); if (HAVE_ENCODING(ctx->xml2enc)) { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01432) "Got charset from XML rules.") ; @@ -304,7 +323,7 @@ static apr_status_t xml2enc_ffunc(ap_filter_t* f, apr_bucket_brigade* bb) apr_bucket* bstart; apr_size_t insz = 0; int pending_meta = 0; - char *ctype; + char *mtype; char *p; if (!ctx || !f->r->content_type) { @@ -313,13 +332,17 @@ static apr_status_t xml2enc_ffunc(ap_filter_t* f, apr_bucket_brigade* bb) return ap_pass_brigade(f->next, bb) ; } - ctype = apr_pstrdup(f->r->pool, f->r->content_type); - for (p = ctype; *p; ++p) - if (isupper(*p)) - *p = tolower(*p); - - /* only act if starts-with "text/" or contains "xml" */ - if (strncmp(ctype, "text/", 5) && !strstr(ctype, "xml")) { + /* Extract the media type, ignoring parameters in content-type. */ + mtype = apr_pstrdup(f->r->pool, f->r->content_type); + if ((p = ap_strchr(mtype, ';')) != NULL) *p = '\0'; + ap_str_tolower(mtype); + + /* Accept text/ types, plus any XML media type per RFC 7303. */ + if (!(strncmp(mtype, "text/", 5) == 0 + || strcmp(mtype, "application/xml") == 0 + || (strlen(mtype) > 7 /* minimum 'a/b+xml' length */ + && (p = strstr(mtype, "+xml")) != NULL + && strlen(p) == 4 /* ensures +xml is a suffix */))) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb) ; } diff --git a/modules/filters/regexp.h b/modules/filters/regexp.h index 6af8912..dc4993a 100644 --- a/modules/filters/regexp.h +++ b/modules/filters/regexp.h @@ -93,8 +93,8 @@ extern void command_errf(sed_commands_t *commands, const char *fmt, ...) #define SEDERR_COMES "cannot open %s" #define SEDERR_CCMES "cannot create %s" #define SEDERR_TMLNMES "too many line numbers" -#define SEDERR_TMAMES "too many appends after line %lld" -#define SEDERR_TMRMES "too many reads after line %lld" +#define SEDERR_TMAMES "too many appends after line %" APR_INT64_T_FMT +#define SEDERR_TMRMES "too many reads after line %" APR_INT64_T_FMT #define SEDERR_DOORNG "``\\digit'' out of range: %s" #define SEDERR_EDMOSUB "ending delimiter missing on substitution: %s" #define SEDERR_EDMOSTR "ending delimiter missing on string: %s" diff --git a/modules/filters/sed1.c b/modules/filters/sed1.c index be03506..047f49b 100644 --- a/modules/filters/sed1.c +++ b/modules/filters/sed1.c @@ -71,7 +71,7 @@ static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n, static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2); static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, step_vars_storage *step_vars); -static apr_status_t wline(sed_eval_t *eval, char *buf, int sz); +static apr_status_t wline(sed_eval_t *eval, char *buf, apr_size_t sz); static apr_status_t arout(sed_eval_t *eval); static void eval_errf(sed_eval_t *eval, const char *fmt, ...) @@ -87,18 +87,20 @@ static void eval_errf(sed_eval_t *eval, const char *fmt, ...) } #define INIT_BUF_SIZE 1024 +#define MAX_BUF_SIZE 1024*8192 /* * grow_buffer */ -static void grow_buffer(apr_pool_t *pool, char **buffer, - char **spend, unsigned int *cursize, - unsigned int newsize) +static apr_status_t grow_buffer(apr_pool_t *pool, char **buffer, + char **spend, apr_size_t *cursize, + apr_size_t newsize) { char* newbuffer = NULL; - int spendsize = 0; - if (*cursize >= newsize) - return; + apr_size_t spendsize = 0; + if (*cursize >= newsize) { + return APR_SUCCESS; + } /* Avoid number of times realloc is called. It could cause huge memory * requirement if line size is huge e.g 2 MB */ if (newsize < *cursize * 2) { @@ -107,6 +109,9 @@ static void grow_buffer(apr_pool_t *pool, char **buffer, /* Align it to 4 KB boundary */ newsize = (newsize + ((1 << 12) - 1)) & ~((1 << 12) - 1); + if (newsize > MAX_BUF_SIZE) { + return APR_ENOMEM; + } newbuffer = apr_pcalloc(pool, newsize); if (*spend && *buffer && (*cursize > 0)) { spendsize = *spend - *buffer; @@ -119,123 +124,168 @@ static void grow_buffer(apr_pool_t *pool, char **buffer, if (spend != buffer) { *spend = *buffer + spendsize; } + return APR_SUCCESS; } /* * grow_line_buffer */ -static void grow_line_buffer(sed_eval_t *eval, int newsize) +static apr_status_t grow_line_buffer(sed_eval_t *eval, apr_size_t newsize) { - grow_buffer(eval->pool, &eval->linebuf, &eval->lspend, + return grow_buffer(eval->pool, &eval->linebuf, &eval->lspend, &eval->lsize, newsize); } /* * grow_hold_buffer */ -static void grow_hold_buffer(sed_eval_t *eval, int newsize) +static apr_status_t grow_hold_buffer(sed_eval_t *eval, apr_size_t newsize) { - grow_buffer(eval->pool, &eval->holdbuf, &eval->hspend, + return grow_buffer(eval->pool, &eval->holdbuf, &eval->hspend, &eval->hsize, newsize); } /* * grow_gen_buffer */ -static void grow_gen_buffer(sed_eval_t *eval, int newsize, +static apr_status_t grow_gen_buffer(sed_eval_t *eval, apr_size_t newsize, char **gspend) { + apr_status_t rc = 0; if (gspend == NULL) { gspend = &eval->genbuf; } - grow_buffer(eval->pool, &eval->genbuf, gspend, - &eval->gsize, newsize); - eval->lcomend = &eval->genbuf[71]; + rc = grow_buffer(eval->pool, &eval->genbuf, gspend, + &eval->gsize, newsize); + if (rc == APR_SUCCESS) { + eval->lcomend = &eval->genbuf[71]; + } + return rc; } /* * appendmem_to_linebuf */ -static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, int len) +static apr_status_t appendmem_to_linebuf(sed_eval_t *eval, const char* sz, apr_size_t len) { - unsigned int reqsize = (eval->lspend - eval->linebuf) + len; + apr_status_t rc = 0; + apr_size_t reqsize = (eval->lspend - eval->linebuf) + len; if (eval->lsize < reqsize) { - grow_line_buffer(eval, reqsize); + rc = grow_line_buffer(eval, reqsize); + if (rc != APR_SUCCESS) { + return rc; + } } memcpy(eval->lspend, sz, len); eval->lspend += len; + return APR_SUCCESS; } /* * append_to_linebuf */ -static void append_to_linebuf(sed_eval_t *eval, const char* sz) +static apr_status_t append_to_linebuf(sed_eval_t *eval, const char* sz, + step_vars_storage *step_vars) { - int len = strlen(sz); + apr_size_t len = strlen(sz); + char *old_linebuf = eval->linebuf; + apr_status_t rc = 0; /* Copy string including null character */ - appendmem_to_linebuf(eval, sz, len + 1); + rc = appendmem_to_linebuf(eval, sz, len + 1); + if (rc != APR_SUCCESS) { + return rc; + } --eval->lspend; /* lspend will now point to NULL character */ + /* Sync step_vars after a possible linebuf expansion */ + if (step_vars && old_linebuf != eval->linebuf) { + if (step_vars->loc1) { + step_vars->loc1 = step_vars->loc1 - old_linebuf + eval->linebuf; + } + if (step_vars->loc2) { + step_vars->loc2 = step_vars->loc2 - old_linebuf + eval->linebuf; + } + if (step_vars->locs) { + step_vars->locs = step_vars->locs - old_linebuf + eval->linebuf; + } + } + return APR_SUCCESS; } /* * copy_to_linebuf */ -static void copy_to_linebuf(sed_eval_t *eval, const char* sz) +static apr_status_t copy_to_linebuf(sed_eval_t *eval, const char* sz, + step_vars_storage *step_vars) { eval->lspend = eval->linebuf; - append_to_linebuf(eval, sz); + return append_to_linebuf(eval, sz, step_vars); } /* * append_to_holdbuf */ -static void append_to_holdbuf(sed_eval_t *eval, const char* sz) +static apr_status_t append_to_holdbuf(sed_eval_t *eval, const char* sz) { - int len = strlen(sz); - unsigned int reqsize = (eval->hspend - eval->holdbuf) + len + 1; + apr_size_t len = strlen(sz); + apr_size_t reqsize = (eval->hspend - eval->holdbuf) + len + 1; + apr_status_t rc = 0; if (eval->hsize <= reqsize) { - grow_hold_buffer(eval, reqsize); + rc = grow_hold_buffer(eval, reqsize); + if (rc != APR_SUCCESS) { + return rc; + } } memcpy(eval->hspend, sz, len + 1); /* hspend will now point to NULL character */ eval->hspend += len; + return APR_SUCCESS; } /* * copy_to_holdbuf */ -static void copy_to_holdbuf(sed_eval_t *eval, const char* sz) +static apr_status_t copy_to_holdbuf(sed_eval_t *eval, const char* sz) { eval->hspend = eval->holdbuf; - append_to_holdbuf(eval, sz); + return append_to_holdbuf(eval, sz); } /* * append_to_genbuf */ -static void append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend) +static apr_status_t append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend) { - int len = strlen(sz); - unsigned int reqsize = (*gspend - eval->genbuf) + len + 1; + apr_size_t len = strlen(sz); + apr_size_t reqsize = (*gspend - eval->genbuf) + len + 1; + apr_status_t rc = 0; if (eval->gsize < reqsize) { - grow_gen_buffer(eval, reqsize, gspend); + rc = grow_gen_buffer(eval, reqsize, gspend); + if (rc != APR_SUCCESS) { + return rc; + } } memcpy(*gspend, sz, len + 1); /* *gspend will now point to NULL character */ *gspend += len; + return APR_SUCCESS; } /* * copy_to_genbuf */ -static void copy_to_genbuf(sed_eval_t *eval, const char* sz) +static apr_status_t copy_to_genbuf(sed_eval_t *eval, const char* sz) { - int len = strlen(sz); - unsigned int reqsize = len + 1; + apr_size_t len = strlen(sz); + apr_size_t reqsize = len + 1; + apr_status_t rc = APR_SUCCESS;; if (eval->gsize < reqsize) { - grow_gen_buffer(eval, reqsize, NULL); + rc = grow_gen_buffer(eval, reqsize, NULL); + if (rc != APR_SUCCESS) { + return rc; + } } memcpy(eval->genbuf, sz, len + 1); + return rc; } /* @@ -353,7 +403,7 @@ apr_status_t sed_eval_file(sed_eval_t *eval, apr_file_t *fin, void *fout) /* * sed_eval_buffer */ -apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void *fout) +apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz, void *fout) { apr_status_t rv; @@ -382,8 +432,9 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void } while (bufsz) { + apr_status_t rc = 0; char *n; - int llen; + apr_size_t llen; n = memchr(buf, '\n', bufsz); if (n == NULL) @@ -396,7 +447,10 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void break; } - appendmem_to_linebuf(eval, buf, llen + 1); + rc = appendmem_to_linebuf(eval, buf, llen + 1); + if (rc != APR_SUCCESS) { + return rc; + } --eval->lspend; /* replace new line character with NULL */ *eval->lspend = '\0'; @@ -411,7 +465,10 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void /* Save the leftovers for later */ if (bufsz) { - appendmem_to_linebuf(eval, buf, bufsz); + apr_status_t rc = appendmem_to_linebuf(eval, buf, bufsz); + if (rc != APR_SUCCESS) { + return rc; + } } return APR_SUCCESS; @@ -433,6 +490,7 @@ apr_status_t sed_finalize_eval(sed_eval_t *eval, void *fout) /* Process leftovers */ if (eval->lspend > eval->linebuf) { apr_status_t rv; + apr_status_t rc = 0; if (eval->lreadyflag) { eval->lreadyflag = 0; @@ -442,7 +500,10 @@ apr_status_t sed_finalize_eval(sed_eval_t *eval, void *fout) * buffer is not a newline. */ /* Assure space for NULL */ - append_to_linebuf(eval, ""); + rc = append_to_linebuf(eval, "", NULL); + if (rc != APR_SUCCESS) { + return rc; + } } *eval->lspend = '\0'; @@ -640,11 +701,15 @@ static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n, sp = eval->genbuf; rp = rhsbuf; sp = place(eval, sp, lp, step_vars->loc1); + if (sp == NULL) { + return APR_EGENERAL; + } while ((c = *rp++) != 0) { if (c == '&') { sp = place(eval, sp, step_vars->loc1, step_vars->loc2); - if (sp == NULL) + if (sp == NULL) { return APR_EGENERAL; + } } else if (c == '\\') { c = *rp++; @@ -660,13 +725,19 @@ static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n, *sp++ = c; if (sp >= eval->genbuf + eval->gsize) { /* expand genbuf and set the sp appropriately */ - grow_gen_buffer(eval, eval->gsize + 1024, &sp); + rv = grow_gen_buffer(eval, eval->gsize + 1024, &sp); + if (rv != APR_SUCCESS) { + return rv; + } } } lp = step_vars->loc2; step_vars->loc2 = sp - eval->genbuf + eval->linebuf; - append_to_genbuf(eval, lp, &sp); - copy_to_linebuf(eval, eval->genbuf); + rv = append_to_genbuf(eval, lp, &sp); + if (rv != APR_SUCCESS) { + return rv; + } + rv = copy_to_linebuf(eval, eval->genbuf, step_vars); return rv; } @@ -676,11 +747,14 @@ static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n, static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2) { char *sp = asp; - int n = al2 - al1; - unsigned int reqsize = (sp - eval->genbuf) + n + 1; + apr_size_t n = al2 - al1; + apr_size_t reqsize = (sp - eval->genbuf) + n + 1; if (eval->gsize < reqsize) { - grow_gen_buffer(eval, reqsize, &sp); + apr_status_t rc = grow_gen_buffer(eval, reqsize, &sp); + if (rc != APR_SUCCESS) { + return NULL; + } } memcpy(sp, al1, n); return sp + n; @@ -735,7 +809,8 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, } p1++; - copy_to_linebuf(eval, p1); + rv = copy_to_linebuf(eval, p1, step_vars); + if (rv != APR_SUCCESS) return rv; eval->jflag++; break; @@ -745,21 +820,27 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, break; case GCOM: - copy_to_linebuf(eval, eval->holdbuf); + rv = copy_to_linebuf(eval, eval->holdbuf, step_vars); + if (rv != APR_SUCCESS) return rv; break; case CGCOM: - append_to_linebuf(eval, "\n"); - append_to_linebuf(eval, eval->holdbuf); + rv = append_to_linebuf(eval, "\n", step_vars); + if (rv != APR_SUCCESS) return rv; + rv = append_to_linebuf(eval, eval->holdbuf, step_vars); + if (rv != APR_SUCCESS) return rv; break; case HCOM: - copy_to_holdbuf(eval, eval->linebuf); + rv = copy_to_holdbuf(eval, eval->linebuf); + if (rv != APR_SUCCESS) return rv; break; case CHCOM: - append_to_holdbuf(eval, "\n"); - append_to_holdbuf(eval, eval->linebuf); + rv = append_to_holdbuf(eval, "\n"); + if (rv != APR_SUCCESS) return rv; + rv = append_to_holdbuf(eval, eval->linebuf); + if (rv != APR_SUCCESS) return rv; break; case ICOM: @@ -881,7 +962,8 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, if (rv != APR_SUCCESS) return rv; } - append_to_linebuf(eval, "\n"); + rv = append_to_linebuf(eval, "\n", step_vars); + if (rv != APR_SUCCESS) return rv; eval->pending = ipc->next; break; @@ -955,9 +1037,12 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, break; case XCOM: - copy_to_genbuf(eval, eval->linebuf); - copy_to_linebuf(eval, eval->holdbuf); - copy_to_holdbuf(eval, eval->genbuf); + rv = copy_to_genbuf(eval, eval->linebuf); + if (rv != APR_SUCCESS) return rv; + rv = copy_to_linebuf(eval, eval->holdbuf, step_vars); + if (rv != APR_SUCCESS) return rv; + rv = copy_to_holdbuf(eval, eval->genbuf); + if (rv != APR_SUCCESS) return rv; break; case YCOM: @@ -1013,7 +1098,7 @@ static apr_status_t arout(sed_eval_t *eval) /* * wline */ -static apr_status_t wline(sed_eval_t *eval, char *buf, int sz) +static apr_status_t wline(sed_eval_t *eval, char *buf, apr_size_t sz) { apr_status_t rv = APR_SUCCESS; rv = eval->writefn(eval->fout, buf, sz); |